@ghostlytools/cli 1.2.4 → 1.3.1

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 (100) hide show
  1. package/README.md +25 -9
  2. package/dist/assets/api-Dq2QXQDk.js +4172 -0
  3. package/dist/assets/api-Dq2QXQDk.js.map +1 -0
  4. package/dist/assets/api-TiB5QSG9.js +3 -0
  5. package/dist/assets/apiKit-CA2x5GJF.js +200 -0
  6. package/dist/assets/apiKit-CA2x5GJF.js.map +1 -0
  7. package/dist/assets/apps-BxV9i_Y4.js +786 -0
  8. package/dist/assets/apps-BxV9i_Y4.js.map +1 -0
  9. package/dist/assets/apps-C-T8jeji.js +2 -0
  10. package/dist/assets/{arkade-CdnKGDbg.js → arkade-C_qDmHnm.js} +2 -2
  11. package/dist/assets/{arkade-CdnKGDbg.js.map → arkade-C_qDmHnm.js.map} +1 -1
  12. package/dist/assets/backup-Bugnn3Ue.js +2 -0
  13. package/dist/assets/backup-CnMN2oUK.js +731 -0
  14. package/dist/assets/backup-CnMN2oUK.js.map +1 -0
  15. package/dist/assets/bark_ffi_wasm_bg-EY2uc0Bx.js.map +1 -1
  16. package/dist/assets/bitcoind-CZchoh43.js +349 -0
  17. package/dist/assets/bitcoind-CZchoh43.js.map +1 -0
  18. package/dist/assets/bitcoindProvider-DGw3xBSa.js +97 -0
  19. package/dist/assets/bitcoindProvider-DGw3xBSa.js.map +1 -0
  20. package/dist/assets/bitcoindevkit_bg-B-iLWrmX.js.map +1 -1
  21. package/dist/assets/buttons-Bj7Vf7Pn.js +2 -0
  22. package/dist/assets/buttons-CvNBzh2S.js +496 -0
  23. package/dist/assets/buttons-CvNBzh2S.js.map +1 -0
  24. package/dist/assets/{endpoint-pgFkR2AS.js → endpoint-BOTFMpfS.js} +56 -18
  25. package/dist/assets/endpoint-BOTFMpfS.js.map +1 -0
  26. package/dist/assets/envelope-WjingYSc.js +90 -0
  27. package/dist/assets/envelope-WjingYSc.js.map +1 -0
  28. package/dist/assets/errors-DO5z6h_l.js +105 -0
  29. package/dist/assets/errors-DO5z6h_l.js.map +1 -0
  30. package/dist/assets/fedimint_client_wasm_bg-BtHHlFpp.js.map +1 -1
  31. package/dist/assets/{fileBytes-Dbifu5I5.js → fileBytes-CfSXjTFa.js} +2 -2
  32. package/dist/assets/{fileBytes-Dbifu5I5.js.map → fileBytes-CfSXjTFa.js.map} +1 -1
  33. package/dist/assets/fileBytes-CgbibpbV.js +2 -0
  34. package/dist/assets/{fileBytes-BcYZhNgF.js → fileBytes-Di7VZtmU.js} +6 -19
  35. package/dist/assets/fileBytes-Di7VZtmU.js.map +1 -0
  36. package/dist/assets/fileBytesIdb-CFP-DSkZ.js +2 -0
  37. package/dist/assets/{fileBytesIdb-DzSaqI0A.js → fileBytesIdb-D5u6H8X_.js} +4 -4
  38. package/dist/assets/{fileBytesIdb-DzSaqI0A.js.map → fileBytesIdb-D5u6H8X_.js.map} +1 -1
  39. package/dist/assets/{fileBytesOpfs-Dx8RZiV5.js → fileBytesOpfs-Dd1N4ERH.js} +3 -3
  40. package/dist/assets/{fileBytesOpfs-Dx8RZiV5.js.map → fileBytesOpfs-Dd1N4ERH.js.map} +1 -1
  41. package/dist/assets/groupFiles-IEUW3DQz.js +3202 -0
  42. package/dist/assets/groupFiles-IEUW3DQz.js.map +1 -0
  43. package/dist/assets/host-CWt-jtqz.js +4673 -0
  44. package/dist/assets/host-CWt-jtqz.js.map +1 -0
  45. package/dist/assets/hyperdhtRelay-BT_wRxab.js +21 -0
  46. package/dist/assets/hyperdhtRelay-BT_wRxab.js.map +1 -0
  47. package/dist/assets/{hyperdhtRelay-C1p0nkle.js → hyperdhtRelay-CPYwSLLS.js} +25 -10
  48. package/dist/assets/hyperdhtRelay-CPYwSLLS.js.map +1 -0
  49. package/dist/assets/{idb-CTF6LF5B.js → idb-B5L8wuOm.js} +7 -5
  50. package/dist/assets/idb-B5L8wuOm.js.map +1 -0
  51. package/dist/assets/irohWeb-4oChIOIp.js +544 -0
  52. package/dist/assets/irohWeb-4oChIOIp.js.map +1 -0
  53. package/dist/assets/{joinTrace-CO8mlhNO.js → joinTrace-CjRDFy85.js} +2 -7
  54. package/dist/assets/{joinTrace-CO8mlhNO.js.map → joinTrace-CjRDFy85.js.map} +1 -1
  55. package/dist/assets/joinTrace-D-fnt2p3.js +2 -0
  56. package/dist/assets/main-DbqcdShN.js +4409 -0
  57. package/dist/assets/main-DbqcdShN.js.map +1 -0
  58. package/dist/assets/{mainline-BH6s8HVi.js → mainline-ZkiOnevs.js} +11 -6
  59. package/dist/assets/mainline-ZkiOnevs.js.map +1 -0
  60. package/dist/assets/mints-BrlY773j.js +74 -0
  61. package/dist/assets/mints-BrlY773j.js.map +1 -0
  62. package/dist/assets/mints-CqTdA4Gk.js +2 -0
  63. package/dist/assets/networkReason-CetKkTPo.js +154 -0
  64. package/dist/assets/networkReason-CetKkTPo.js.map +1 -0
  65. package/dist/assets/nwc-CgkqyvsE.js +612 -0
  66. package/dist/assets/nwc-CgkqyvsE.js.map +1 -0
  67. package/dist/assets/nwcProvider-4eVz_8HP.js +77 -0
  68. package/dist/assets/nwcProvider-4eVz_8HP.js.map +1 -0
  69. package/dist/assets/privateFolder-Bk9C9oFv.js +38 -0
  70. package/dist/assets/privateFolder-Bk9C9oFv.js.map +1 -0
  71. package/dist/assets/profiles-BHc9pEYg.js +139 -0
  72. package/dist/assets/profiles-BHc9pEYg.js.map +1 -0
  73. package/dist/assets/registry-DlAKO0Fy.js +3793 -0
  74. package/dist/assets/registry-DlAKO0Fy.js.map +1 -0
  75. package/dist/assets/registry-OBJBnHVQ.js +2 -0
  76. package/dist/assets/resolve-CouHhCpJ.js +591 -0
  77. package/dist/assets/resolve-CouHhCpJ.js.map +1 -0
  78. package/dist/assets/{restoredRows-C_84q0Pd.js → restoredRows-Db3oUTko.js} +2 -2
  79. package/dist/assets/{restoredRows-C_84q0Pd.js.map → restoredRows-Db3oUTko.js.map} +1 -1
  80. package/dist/assets/{server-0zfNpABQ.js → server-Ss0gJ-nw.js} +1275 -1404
  81. package/dist/assets/server-Ss0gJ-nw.js.map +1 -0
  82. package/dist/assets/{src-CbLkHfz8.js → src-DDzSERyc.js} +882 -134
  83. package/dist/assets/src-DDzSERyc.js.map +1 -0
  84. package/dist/assets/turnRefs-B87dtrHM.js +323 -0
  85. package/dist/assets/turnRefs-B87dtrHM.js.map +1 -0
  86. package/dist/assets/types-C4_KafL_.js +122 -0
  87. package/dist/assets/types-C4_KafL_.js.map +1 -0
  88. package/dist/assets/{usdt-Qf4UuygS.js → usdt-Fj8CQO2z.js} +3 -3
  89. package/dist/assets/{usdt-Qf4UuygS.js.map → usdt-Fj8CQO2z.js.map} +1 -1
  90. package/dist/ghostly.mjs +4 -22077
  91. package/dist/ghostly.mjs.map +1 -1
  92. package/package.json +1 -1
  93. package/dist/assets/endpoint-pgFkR2AS.js.map +0 -1
  94. package/dist/assets/fileBytes-BcYZhNgF.js.map +0 -1
  95. package/dist/assets/hyperdhtRelay-C1p0nkle.js.map +0 -1
  96. package/dist/assets/idb-CTF6LF5B.js.map +0 -1
  97. package/dist/assets/mainline-BH6s8HVi.js.map +0 -1
  98. package/dist/assets/rolldown-runtime-8H4AJuhK.js +0 -14
  99. package/dist/assets/server-0zfNpABQ.js.map +0 -1
  100. package/dist/assets/src-CbLkHfz8.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ghostly.mjs","names":["randomBytes","DAMAGED","TYPES","constants","randomBytes","checked","randomBytes","randomBytes","check","HEX64","hex","unhex","nip19Decode","lookup","cache","exactKeys","loading","fromBase64Url","fromBase64Url","text","scopeFor","randomBytes","callbackParams","currentHost","hostOf","resolve","DID_DOCUMENT_MAX_BYTES","exactKeys","withSession","redact","refused","hex","fromBase64Url","open","createServer","ITERATIONS","keyFrom","through","open","sha256","join","FORMAT","writeWhole","openEnvelope","store","store","RANGE","store","hex","request","NETWORKS","METHODS","fromBase64Url","commands","commands","commands","commands","commands","commands","commands","commands","general","chats","groups","files","payments","identities","services","calls","apps","loading","fromBase64Url","hex","compiling","compiled","watch","Worker","dnsLookup","IDBKeyRange","fromBase64Url","packageJson"],"sources":["../package.json","../src/errors.ts","../src/args.ts","../../../apps/ui/src/lib/parse/secrets.ts","../src/engineMethods.ts","../src/apiKit.ts","../../browser/src/shared/storedFiles.ts","../../browser/src/shared/paymentIds.ts","../../browser/src/shared/replies.ts","../../browser/src/engine/groupFiles.ts","../src/typing.ts","../src/voicePeaks.ts","../src/files.ts","../src/waits.ts","../src/statusCards.ts","../src/buttons.ts","../src/holds.ts","../src/views.ts","../src/groupAdmin.ts","../src/turnRefs.ts","../../browser/src/proofs/bitcoinWallets.ts","../../browser/src/proofs/providers/bitcoin.ts","../../browser/src/proofs/nostr.ts","../../browser/src/proofs/providers/nostr.ts","../../browser/src/proofs/domain.ts","../../browser/src/proofs/providers/domain.ts","../../browser/src/proofs/openpgp.ts","../../browser/src/proofs/providers/openpgp.ts","../../browser/src/proofs/sshForges.ts","../../browser/src/proofs/providers/ssh.ts","../../browser/src/host.ts","../../browser/src/proofs/oidc/providers.ts","../../browser/src/proofs/oidc/jwks.ts","../../browser/src/proofs/oidc/verify.ts","../../browser/src/proofs/oidc/flow.ts","../../browser/src/proofs/providers/oidc.ts","../../browser/src/proofs/did.ts","../../browser/src/proofs/verify.ts","../../browser/src/proofs/providers/did.ts","../../browser/src/proofs/atproto/resolve.ts","../../browser/src/proofs/atproto/oauth.ts","../../browser/src/proofs/providers/atproto.ts","../../browser/src/proofs/pubky.ts","../../browser/src/proofs/providers/pubky.ts","../../browser/src/proofs/testing.ts","../../browser/src/engine/paymentAdapters/providers/types.ts","../../browser/src/plugins/bundled.ts","../../browser/src/plugins/registry.ts","../../browser/src/proofs/registry.ts","../src/identities.ts","../src/services.ts","../../browser/src/backup/codec.ts","../../browser/src/backup/envelope.ts","../../browser/src/backup/stream.ts","../../browser/src/backup/light.ts","../src/profiles.ts","../src/backup.ts","../src/calls/api.ts","../../browser/src/shared/mints.ts","../../browser/src/shared/types.ts","../../browser/src/engine/paymentAdapters/providers/cashuMint.ts","../../browser/src/engine/paymentAdapters/modeGate.ts","../../browser/src/engine/paymentAdapters/persistence.ts","../../browser/src/engine/paymentAdapters/away.ts","../../browser/src/engine/paymentAdapters/providers/sources.ts","../../browser/src/engine/paymentAdapters/providers/lightningService.ts","../../browser/src/engine/paymentAdapters/providers/breezDatabases.ts","../../browser/src/engine/paymentAdapters/providers/recoveryPhrase.ts","../../browser/src/engine/paymentAdapters/providers/lightningCards.ts","../../browser/src/engine/paymentAdapters/networkReason.ts","../../browser/src/engine/paymentAdapters/walletInstances.ts","../src/wallets.ts","../src/api.ts","../src/privateFolder.ts","../src/client.ts","../src/appTemplate.ts","../src/apps.ts","../src/commands/apps.ts","../src/commands/shared.ts","../src/commands/calls.ts","../src/commands/chats.ts","../src/commands/files.ts","../src/commands/general.ts","../src/commands/groups.ts","../src/commands/identities.ts","../src/commands/payments.ts","../src/commands/services.ts","../src/commands/index.ts","../src/announce.ts","../src/calls/pcm.ts","../src/calls/audioSocket.ts","../src/calls/rtp.ts","../src/calls/audio.ts","../src/calls/media.ts","../src/calls/manager.ts","../src/events.ts","../../browser/src/platform/irohWeb.ts","../../browser/src/engine/paymentAdapters/fedimintSdk.ts","../src/runtime/fedimint.ts","../src/runtime/fileFetch.ts","../src/runtime/pushSend.ts","../src/runtime/storage.ts","../src/runtime/engine.ts","../src/host.ts","../src/listen.ts","../src/secretInput.ts","../src/main.ts","../src/runtime/webStorage.ts","../src/bin.ts"],"sourcesContent":["{\n \"name\": \"@ghostlytools/cli\",\n \"version\": \"1.2.4\",\n \"type\": \"module\",\n \"description\": \"Ghostly for bots and terminals: the app's own engine, headless on Node, behind a daemon and a `ghostly` command with JSON output.\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/MiguelMedeiros/ghostly.git\",\n \"directory\": \"packages/cli\"\n },\n \"homepage\": \"https://ghostly.tools/cli\",\n \"bugs\": \"https://github.com/MiguelMedeiros/ghostly/issues\",\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"bin\": {\n \"ghostly\": \"./dist/ghostly.mjs\"\n },\n \"files\": [\n \"dist\",\n \"examples\",\n \"README.md\",\n \"SKILL.md\"\n ],\n \"engines\": {\n \"node\": \">=22.12\"\n },\n \"scripts\": {\n \"build\": \"vite build\",\n \"typecheck\": \"tsc --noEmit -p tsconfig.json\",\n \"test\": \"vitest run\"\n },\n \"dependencies\": {\n \"@arkade-os/sdk\": \"0.4.78\",\n \"@atproto/jwk-webcrypto\": \"^0.3.4\",\n \"@atproto/oauth-client\": \"^0.8.9\",\n \"@bitcoindevkit/bdk-wallet-web\": \"0.3.0\",\n \"@bitcoinerlab/descriptors-scure\": \"^3.2.0\",\n \"@cashu/cashu-ts\": \"^4.11.0\",\n \"@fedimint/core\": \"0.0.0-canary-eea6a3c98909fa8e736d6dadb1f1243fd3322b27\",\n \"@fedimint/fedimint-client-wasm-bundler\": \"0.0.0-canary-eea6a3c98909fa8e736d6dadb1f1243fd3322b27\",\n \"@fedimint/types\": \"0.0.0-canary-eea6a3c98909fa8e736d6dadb1f1243fd3322b27\",\n \"@hyperswarm/dht-relay\": \"0.4.3\",\n \"@noble/ciphers\": \"^2.4.0\",\n \"@noble/curves\": \"^2.4.0\",\n \"@noble/hashes\": \"2.4.0\",\n \"@scure/base\": \"2.4.0\",\n \"@scure/bip32\": \"^2.4.0\",\n \"@scure/bip39\": \"2.4.0\",\n \"@scure/btc-signer\": \"2.4.1\",\n \"@secondts/bark\": \"0.25.0\",\n \"@serenity-kit/opaque\": \"1.1.0\",\n \"@synonymdev/pubky\": \"0.14.0\",\n \"@tetherto/wdk-wallet-evm\": \"1.0.0-beta.20\",\n \"b4a\": \"1.9.0\",\n \"bittorrent-dht\": \"11.0.12\",\n \"ethers\": \"6.17.0\",\n \"eventsource\": \"^5.1.2\",\n \"fake-indexeddb\": \"6.2.5\",\n \"hyperdht\": \"6.34.1\",\n \"keet-identity-key\": \"3.2.0\",\n \"mpg123-decoder\": \"1.0.3\",\n \"node-datachannel\": \"^0.33.4\",\n \"nostr-tools\": \"2.25.2\",\n \"opus-decoder\": \"0.7.12\",\n \"opusscript\": \"0.1.1\"\n },\n \"devDependencies\": {\n \"@ghostly/browser\": \"*\",\n \"@ghostly/core\": \"*\",\n \"@ghostly/iroh-web\": \"*\",\n \"@types/node\": \"^26.6.4\",\n \"typescript\": \"^5.9.3\",\n \"vite\": \"^8.3.3\",\n \"vitest\": \"^5.0.3\"\n }\n}\n","/**\n * Errors that reach a bot: a stable `code` to branch on, a message for people, and the exit status the CLI\n * leaves with (WISP 1100 § Exit codes). The daemon sends `{code, message}` over the socket; the CLI prints\n * `{\"error\": {code, message}}` on stdout and exits with `exit`.\n */\nexport type ErrorCode =\n | \"bad_request\" | \"usage\" | \"not_found\" | \"refused\" | \"unavailable\" | \"confirm\" | \"timeout\"\n | \"engine\" | \"busy\" | \"internal\";\n\nexport const EXIT: Record<ErrorCode, number> = {\n engine: 1, refused: 1, unavailable: 1, bad_request: 1, busy: 1, internal: 1,\n usage: 2, not_found: 3, timeout: 4, confirm: 5,\n};\n\nexport class CliError extends Error {\n constructor(readonly code: ErrorCode, message: string, readonly details?: Record<string, unknown>) {\n super(message);\n this.name = \"CliError\";\n }\n toJSON(): { code: ErrorCode; message: string; details?: Record<string, unknown> } {\n return { code: this.code, message: this.message, ...(this.details ? { details: this.details } : {}) };\n }\n}\n\n/** Anything thrown, as the error a bot sees: the engine's own refusals keep their words. */\nexport function asCliError(error: unknown): CliError {\n if (error instanceof CliError) return error;\n if (error && typeof error === \"object\" && \"code\" in error && \"message\" in error && typeof (error as { code: unknown }).code === \"string\"\n && Object.hasOwn(EXIT, (error as { code: string }).code)) return new CliError((error as { code: ErrorCode }).code, String((error as { message: unknown }).message));\n const local = localFileError(error);\n if (local) return local;\n const unopened = profileOpenError(error);\n if (unopened) return unopened;\n const message = error instanceof Error ? error.message : String(error);\n if (/real money|confirmedReal|Mainnet/i.test(message) && /confirm/i.test(message)) return new CliError(\"confirm\", message);\n if (/other network|cross-network|network does not match/i.test(message)) return new CliError(\"refused\", message);\n return new CliError(\"engine\", message);\n}\n\n/**\n * The profile's database did not open (the engine's `ProfileOpenError`, packages/browser/src/shared/idb.ts): `engine`\n * (exit 1) with the engine's own words, which name the versions when a newer ghostly stored it, and the reason and\n * the versions in `details` for a bot. Read by its shape: this module is also the thin client's, which loads no engine.\n */\nfunction profileOpenError(error: unknown): CliError | undefined {\n if (!(error instanceof Error) || error.name !== \"ProfileOpenError\") return undefined;\n const failure = (error as { failure?: { reason?: unknown; storedVersion?: unknown; supportedVersion?: unknown } }).failure;\n if (!failure || typeof failure.reason !== \"string\") return undefined;\n return new CliError(\"engine\", error.message, {\n reason: failure.reason === \"newer\" ? \"newer_profile\" : `profile_${failure.reason}`,\n ...(typeof failure.storedVersion === \"number\" ? { storedVersion: failure.storedVersion } : {}),\n ...(typeof failure.supportedVersion === \"number\" ? { supportedVersion: failure.supportedVersion } : {}),\n });\n}\n\n/**\n * A file or folder on this machine the command could not open (`file save --dir`, `profile backup --out`): the\n * person's path, not the engine refusing, so it says so with the code a bot branches on. Before, a folder that did\n * not exist was `engine` (exit 1) with Node's `ENOENT: no such file or directory, open '…'`.\n */\nfunction localFileError(error: unknown): CliError | undefined {\n if (!error || typeof error !== \"object\" || typeof (error as { syscall?: unknown }).syscall !== \"string\") return undefined;\n const { code, path } = error as { code?: unknown; path?: unknown };\n const where = typeof path === \"string\" ? path : \"the path\";\n if (code === \"ENOENT\" || code === \"ENOTDIR\") return new CliError(\"not_found\", `No such file or folder: ${where}`);\n if (code === \"EACCES\" || code === \"EPERM\" || code === \"EROFS\") return new CliError(\"refused\", `Not allowed to write or read ${where}`);\n if (code === \"EISDIR\") return new CliError(\"bad_request\", `${where} is a folder: name a file`);\n if (code === \"ENAMETOOLONG\") return new CliError(\"bad_request\", `A name in ${where} is too long for this file system (at most 255 bytes)`);\n return undefined;\n}\n","import { CliError } from \"./errors\";\n\nexport type OptionType = \"string\" | \"number\" | \"boolean\" | \"list\";\nexport interface OptionSpec {\n type: OptionType; short?: string; description: string;\n /** A number option that may stand alone (then `true`): it takes the next word only when that is a number. */\n optionalValue?: boolean;\n /** A boolean the command takes only as `--no-<name>`: help shows it so. */\n negatedOnly?: boolean;\n}\nexport interface Parsed { positionals: string[]; options: Record<string, string | number | boolean | string[] | undefined> }\n\n/**\n * Options every command takes. They may come anywhere on the line.\n */\nexport const GLOBAL_OPTIONS: Record<string, OptionSpec> = {\n profile: { type: \"string\", short: \"p\", description: \"Profile to use (default: $GHOSTLY_PROFILE, then the current one)\" },\n home: { type: \"string\", description: \"Ghostly's folder (default: $GHOSTLY_HOME, then ~/.ghostly)\" },\n pretty: { type: \"boolean\", description: \"Indent the JSON output\" },\n help: { type: \"boolean\", short: \"h\", description: \"Show help\" },\n};\n\n/**\n * A small, strict parser: `--name value`, `--name=value`, `-x value`, boolean flags, repeatable lists, and `--`\n * before positionals that start with a dash.\n *\n * The value of an option is taken as is even when it starts with `-` (base64url keys and seeds do one time in 64:\n * the ghostly-cli lesson of #208). So is a positional that `idSlot` says takes an id, a key or a draft: there a word\n * that starts with `-`, is longer than two characters and is no option of the command is the value (`--` starts one\n * id in 4096; `-x` stays an option). Any other positional that starts with `-` is refused instead of sent: a\n * mistyped flag must not reach a contact as text. Put `--` before a message that starts with a dash.\n */\nexport function parseArgs(argv: readonly string[], spec: Record<string, OptionSpec>, idSlot: (index: number) => boolean = () => false): Parsed {\n const all = { ...GLOBAL_OPTIONS, ...spec };\n const byShort = new Map(Object.entries(all).filter(([, s]) => s.short).map(([name, s]) => [s.short!, name]));\n const isOption = (arg: string) => {\n if (!arg.startsWith(\"--\")) return arg.length === 2 && byShort.has(arg.slice(1));\n const name = arg.slice(2).split(\"=\")[0];\n return Object.hasOwn(all, name) || (name.startsWith(\"no-\") && all[name.slice(3)]?.type === \"boolean\");\n };\n const positionals: string[] = [];\n const options: Parsed[\"options\"] = {};\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg === \"--\") { positionals.push(...argv.slice(i + 1)); break; }\n if (arg.startsWith(\"-\") && arg.length > 2 && idSlot(positionals.length) && !isOption(arg)) { positionals.push(arg); continue; }\n if (arg.startsWith(\"--\") || (arg.startsWith(\"-\") && arg.length === 2 && arg !== \"-\")) {\n let name: string, inline: string | undefined;\n if (arg.startsWith(\"--\")) {\n const eq = arg.indexOf(\"=\");\n name = eq === -1 ? arg.slice(2) : arg.slice(2, eq);\n inline = eq === -1 ? undefined : arg.slice(eq + 1);\n } else {\n const long = byShort.get(arg.slice(1));\n if (!long) throw new CliError(\"usage\", `Unknown option ${arg}`);\n name = long;\n }\n if (name.startsWith(\"no-\") && all[name.slice(3)]?.type === \"boolean\" && inline === undefined) { options[name.slice(3)] = false; continue; }\n const option = all[name];\n if (!option) throw new CliError(\"usage\", `Unknown option --${name}`);\n if (option.type === \"boolean\") {\n if (inline !== undefined && ![\"true\", \"false\"].includes(inline)) throw new CliError(\"usage\", `--${name} takes no value`);\n options[name] = inline === undefined ? true : inline === \"true\";\n continue;\n }\n let value = inline;\n if (value === undefined && option.optionalValue && !(i + 1 < argv.length && argv[i + 1] !== \"\" && Number.isFinite(Number(argv[i + 1])))) {\n options[name] = true;\n continue;\n }\n if (value === undefined) {\n if (i + 1 >= argv.length) throw new CliError(\"usage\", `--${name} needs a value`);\n value = argv[++i];\n }\n if (option.type === \"number\") {\n const number = Number(value);\n if (!Number.isFinite(number)) throw new CliError(\"usage\", `--${name} takes a number, not ${JSON.stringify(value)}`);\n options[name] = number;\n } else if (option.type === \"list\") {\n options[name] = [...((options[name] as string[] | undefined) ?? []), value];\n } else options[name] = value;\n continue;\n }\n if (arg.startsWith(\"-\") && arg !== \"-\") throw new CliError(\"usage\", `Unknown option ${arg} (put -- before text that starts with a dash)`);\n positionals.push(arg);\n }\n return { positionals, options };\n}\n\n/** The global option a word names (`--profile`, `--profile=bot`, `-p`), else undefined. */\nexport function globalOption(arg: string): OptionSpec | undefined {\n const name = globalName(arg);\n return name ? GLOBAL_OPTIONS[name] : undefined;\n}\n\n/** The name of the global option a word names (`profile` for `-p`), else undefined. */\nfunction globalName(arg: string): string | undefined {\n const eq = arg.indexOf(\"=\");\n const name = arg.startsWith(\"--\") ? arg.slice(2, eq === -1 ? undefined : eq) : arg.length === 2 && arg.startsWith(\"-\") ? Object.entries(GLOBAL_OPTIONS).find(([, o]) => o.short === arg.slice(1))?.[0] : undefined;\n return name && Object.hasOwn(GLOBAL_OPTIONS, name) ? name : undefined;\n}\n\n/**\n * Global options given before the command (`ghostly --profile bot send …`) move after it, where every command's\n * parser reads them. Stops at `--` and at the first word that is not a global option. They go before the first word\n * after the command's that starts with a dash (its first option, or the `--` that ends them), else at the end: never\n * between an option and its value, which may itself be `--` (`--pretty profile set --name --`). One that takes a value\n * and is the last word has none: a usage error.\n */\nexport function liftGlobals(argv: readonly string[]): string[] {\n const lifted: string[] = [];\n let i = 0;\n while (i < argv.length) {\n const arg = argv[i];\n const eq = arg.indexOf(\"=\");\n const option = globalOption(arg);\n if (!option || arg === \"--\") break;\n const takesValue = option.type !== \"boolean\" && eq === -1;\n // `ghostly -p` alone: a usage error, as `ghostly send -p` is, not the help a line of global options alone shows.\n if (takesValue && i + 1 >= argv.length) throw new CliError(\"usage\", `--${globalName(arg)} needs a value`);\n lifted.push(...argv.slice(i, i + (takesValue ? 2 : 1)));\n i += takesValue ? 2 : 1;\n }\n const rest = argv.slice(i);\n // The first word is the command, also when it is `--version` or `--help`; a `--` there ends the options at once.\n const dash = rest[0] === \"--\" ? 0 : rest.findIndex((word, i) => i > 0 && word.startsWith(\"-\"));\n return dash === -1 ? [...rest, ...lifted] : [...rest.slice(0, dash), ...lifted, ...rest.slice(dash)];\n}\n","import { sha256 } from \"@noble/hashes/sha2.js\";\nimport { createBase58check } from \"@scure/base\";\nimport { wordlist } from \"@scure/bip39/wordlists/english.js\";\n\n/**\n * What the composer asks about before a message goes: text that looks like a secret someone could take funds or an\n * identity with, or a Cashu token (bearer money: the text box would hand it to whoever reads the chat).\n *\n * Detection is local and synchronous, and a finding never carries the text it found: only what kind it is and, for\n * ecash, the amount decoded here. Ark, Spark and Fedimint wallets are BIP39 seeds or raw keys, caught by the rules\n * below; Fedimint ecash notes are plain base64 with no prefix, so they are not recognised.\n */\nexport type SecretFinding =\n | { kind: \"mnemonic\" | \"nsec\" | \"bitcoin-key\" | \"hex-key\" }\n | { kind: \"cashu\"; amount: number | null; unit: string };\n\nexport type SecretKind = SecretFinding[\"kind\"];\n\nconst WORD_INDEX = new Map(wordlist.map((word, index) => [word, index]));\nconst MNEMONIC_LENGTHS = [12, 15, 18, 21, 24];\n/** Checksums tried per message at most: a 16 KiB run of seed words stays a few milliseconds. */\nconst CHECKSUM_BUDGET = 4096;\n\nconst NSEC = /\\bnsec1[02-9ac-hj-np-z]{58}(?![02-9ac-hj-np-z])/i;\nconst EXTENDED_PRIVATE = /\\b[xtyzuvYZUV]prv[1-9A-HJ-NP-Za-km-z]{100,}/;\nconst WIF = /\\b[59KLc][1-9A-HJ-NP-Za-km-z]{50,51}\\b/g;\nconst HEX_SECRET = /\\b(?:0x)?(?:[0-9a-fA-F]{128}|[0-9a-fA-F]{64})\\b/g;\nconst SECRET_CONTEXT = /priv(?:ate)?[\\s_-]*key|secret|seed|mnemonic|nsec|xprv|\\bwif\\b|recovery|passphrase/i;\n/** How far from a 64-hex string a word like \"seed\" still counts as saying what it is. */\nconst CONTEXT_REACH = 48;\nconst CASHU_TOKEN = /\\bcashu([AB])([A-Za-z0-9_\\-+/]{20,}={0,2})/g;\n\n/**\n * Characters that take no room on screen: format characters (zero-width space, soft hyphen, word joiner, the joiners,\n * direction marks), control characters that are not white space, variation selectors, the grapheme joiner and the\n * Hangul fillers. A page, a PDF or a terminal puts some of them into long strings as places to break a line.\n */\nconst INVISIBLE = /(?![\\t\\n\\v\\f\\r\\x85])[\\p{Cc}\\p{Cf}]|\\u034f|[\\u115f\\u1160\\u3164\\uffa0]|[\\ufe00-\\ufe0f]|[\\u{e0100}-\\u{e01ef}]/gu;\n\nconst base58check = createBase58check(sha256);\n\n/**\n * The first secret in `text`, keys and seeds before ecash; null for anything else. The text is read as it is and as\n * it reads without its invisible characters: a key with one inside is whole again once the reader takes it out.\n */\nexport function findSecret(text: string): SecretFinding | null {\n const visible = text.replace(INVISIBLE, \"\");\n const readings = visible === text ? [text] : [text, visible];\n if (readings.some(hasMnemonic)) return { kind: \"mnemonic\" };\n if (readings.some((reading) => NSEC.test(reading))) return { kind: \"nsec\" };\n if (readings.some((reading) => EXTENDED_PRIVATE.test(reading) || hasWif(reading))) return { kind: \"bitcoin-key\" };\n if (readings.some(hasHexSecret)) return { kind: \"hex-key\" };\n const tokens = readings.map(findCashu);\n return tokens.find((found) => found?.kind === \"cashu\" && found.amount !== null) ?? tokens.find((found) => found) ?? null;\n}\n\n/**\n * 12 to 24 words in a row from the English BIP39 list whose checksum holds, anywhere in the text; or a message that\n * is nothing but 12, 15, 18, 21 or 24 of those words (a seed with one word mistyped is still most of a seed). Prose\n * breaks a run at once: \"the\", \"a\", \"is\", \"to\", \"and\", \"of\" and \"I\" are not on the list.\n *\n * One word said over and over is not a seed, though one in 16 such runs passes a 12-word checksum (\"word\" does): a\n * window of a single word never counts, and a window mostly of one word (\"abandon … about\") counts only when that\n * word does not go on past it on either side.\n */\nfunction hasMnemonic(text: string): boolean {\n const tokens = text.toLowerCase().match(/[a-z]+/g);\n if (!tokens || tokens.length < 12) return false;\n let budget = CHECKSUM_BUDGET;\n let run: number[] = [];\n const seedLike = (start: number, length: number) => {\n const window = run.slice(start, start + length);\n const counts = new Map<number, number>();\n for (const word of window) counts.set(word, (counts.get(word) ?? 0) + 1);\n if (counts.size === 1) return false;\n const [most, times] = [...counts].reduce((a, b) => (b[1] > a[1] ? b : a));\n if (times > length / 2 && (run[start - 1] === most || run[start + length] === most)) return false;\n return checksumHolds(window);\n };\n const check = () => {\n if (run.length >= 12) {\n for (const length of MNEMONIC_LENGTHS) {\n for (let start = 0; start + length <= run.length && budget > 0; start++, budget--) {\n if (seedLike(start, length)) return true;\n }\n }\n if (run.length === tokens.length && MNEMONIC_LENGTHS.includes(run.length) && new Set(run).size > 1) return true;\n }\n run = [];\n return false;\n };\n for (const token of tokens) {\n const index = WORD_INDEX.get(token);\n if (index !== undefined) run.push(index);\n else if (check()) return true;\n }\n return check();\n}\n\n/** BIP39: the words' 11-bit indices are the entropy followed by the first bits of its SHA-256. */\nfunction checksumHolds(indices: number[]): boolean {\n const bits = indices.length * 11, checksumBits = bits / 33, entropyBytes = (bits - checksumBits) / 8;\n const bytes = new Uint8Array(Math.ceil(bits / 8));\n indices.forEach((index, i) => {\n for (let b = 0; b < 11; b++) if (index & (1 << (10 - b))) { const at = i * 11 + b; bytes[at >> 3] |= 0x80 >> (at & 7); }\n });\n const expected = sha256(bytes.subarray(0, entropyBytes))[0] >> (8 - checksumBits);\n const got = bytes[entropyBytes] >> (8 - checksumBits);\n return expected === got;\n}\n\n/** A WIF private key: base58check with version 0x80 (mainnet) or 0xef (testnet), 32 bytes and an optional 0x01. */\nfunction hasWif(text: string): boolean {\n for (const [candidate] of text.matchAll(WIF)) {\n try {\n const payload = base58check.decode(candidate);\n if ((payload[0] === 0x80 || payload[0] === 0xef) && (payload.length === 33 || (payload.length === 34 && payload[33] === 1))) return true;\n } catch { /* not base58check: an ordinary word */ }\n }\n return false;\n}\n\n/** 64 or 128 hex digits with \"private key\", \"secret\", \"seed\" or the like close by. A bare hash is not asked about. */\nfunction hasHexSecret(text: string): boolean {\n for (const match of text.matchAll(HEX_SECRET)) {\n const before = text.slice(Math.max(0, match.index - CONTEXT_REACH), match.index);\n const after = text.slice(match.index + match[0].length, match.index + match[0].length + CONTEXT_REACH);\n if (SECRET_CONTEXT.test(before) || SECRET_CONTEXT.test(after)) return true;\n }\n return false;\n}\n\n/** Every Cashu token in the text, with their amounts added up when all of them decode and share one unit. */\nfunction findCashu(text: string): SecretFinding | null {\n const tokens = [...text.matchAll(CASHU_TOKEN)].map(([, version, body]) => decodeCashu(version as \"A\" | \"B\", body));\n if (!tokens.length) return null;\n const unit = tokens.find((token) => token)?.unit ?? \"sat\";\n const readable = tokens.every((token) => token && token.unit === unit);\n return { kind: \"cashu\", amount: readable ? tokens.reduce((sum, token) => sum + token!.amount, 0) : null, unit };\n}\n\n/** What a Cashu token is worth, read from the token itself (NUT-00): nothing is asked of the mint. */\nexport function decodeCashu(version: \"A\" | \"B\", body: string): { amount: number; unit: string } | null {\n try {\n const bytes = fromBase64(body);\n if (version === \"A\") {\n const token = JSON.parse(new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes)) as { token?: { proofs?: { amount?: unknown }[] }[]; unit?: unknown };\n return total(token.token?.flatMap((entry) => entry.proofs ?? []).map((proof) => proof.amount), token.unit);\n }\n const token = decodeCbor(bytes) as { t?: { p?: { a?: unknown }[] }[]; u?: unknown } | null;\n return total(token?.t?.flatMap((entry) => entry.p ?? []).map((proof) => proof.a), token?.u);\n } catch {\n return null;\n }\n}\n\nfunction total(amounts: unknown[] | undefined, unit: unknown): { amount: number; unit: string } | null {\n if (!amounts?.length || !amounts.every((a): a is number => Number.isSafeInteger(a) && (a as number) >= 0)) return null;\n const amount = amounts.reduce((sum, a) => sum + a, 0);\n return Number.isSafeInteger(amount) ? { amount, unit: typeof unit === \"string\" && unit ? unit : \"sat\" } : null;\n}\n\nfunction fromBase64(text: string): Uint8Array {\n const standard = text.replace(/-/g, \"+\").replace(/_/g, \"/\").replace(/=+$/, \"\");\n const binary = atob(standard + \"=\".repeat((4 - (standard.length % 4)) % 4));\n return Uint8Array.from(binary, (c) => c.charCodeAt(0));\n}\n\n/** Enough CBOR (RFC 8949) for a cashuB token: definite lengths, integers, bytes, text, arrays, maps, tags, simple values. */\nfunction decodeCbor(bytes: Uint8Array): unknown {\n let at = 0, depth = 0;\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const length = (info: number): number => {\n if (info < 24) return info;\n const size = info === 24 ? 1 : info === 25 ? 2 : info === 26 ? 4 : info === 27 ? 8 : 0;\n if (!size || at + size > bytes.length) throw new Error(\"cbor\");\n let value = 0;\n for (let i = 0; i < size; i++) value = value * 256 + bytes[at + i];\n at += size;\n return value;\n };\n const item = (): unknown => {\n if (at >= bytes.length || ++depth > 32) throw new Error(\"cbor\");\n const head = bytes[at++], major = head >> 5, info = head & 31;\n try {\n if (major === 7) {\n if (info === 20) return false;\n if (info === 21) return true;\n if (info === 22 || info === 23) return null;\n if (info === 25) { at += 2; return NaN; } // a half float: never in a token\n if (info === 26) { at += 4; return view.getFloat32(at - 4); }\n if (info === 27) { at += 8; return view.getFloat64(at - 8); }\n throw new Error(\"cbor\");\n }\n const n = length(info);\n switch (major) {\n case 0: return n;\n case 1: return -1 - n;\n case 2: case 3: {\n if (at + n > bytes.length) throw new Error(\"cbor\");\n const slice = bytes.subarray(at, (at += n));\n return major === 2 ? slice : new TextDecoder(\"utf-8\", { fatal: true }).decode(slice);\n }\n case 4: {\n if (n > bytes.length - at) throw new Error(\"cbor\");\n return Array.from({ length: n }, item);\n }\n case 5: {\n if (n > bytes.length - at) throw new Error(\"cbor\");\n const map: Record<string, unknown> = Object.create(null);\n for (let i = 0; i < n; i++) { const key = item(); map[String(key)] = item(); }\n return map;\n }\n default: return item(); // a tag: the value it tags\n }\n } finally { depth--; }\n };\n return item();\n}\n","/**\n * The engine's calls the daemon passes through as `engine.<method>` (WISP 1100 § Local control API): exactly the\n * app's `EngineApi` (packages/browser/src/shared/rpc.ts; `engineMethods.test.ts` keeps the two equal), plus two\n * reads every host has. Nothing else on the engine object is reachable.\n */\nexport const ENGINE_METHODS: readonly string[] = [\n \"usdtCreate\", \"usdtUnlock\", \"usdtReveal\", \"usdtLock\", \"usdtRefresh\", \"usdtExportBackup\", \"usdtRestoreBackup\",\n \"arkCreate\", \"arkUnlock\", \"arkLock\", \"arkBackup\", \"arkExportBackup\", \"arkRestoreBackup\", \"arkRefresh\", \"arkRecover\",\n \"barkCreate\", \"barkBackup\", \"barkExportBackup\", \"barkRestoreBackup\", \"barkRefresh\", \"barkBoard\", \"fedimintPreview\",\n \"fedimintJoin\", \"fedimintLeave\", \"fedimintRefresh\", \"fedimintSpendNotes\", \"fedimintReceiveNotes\", \"fedimintInvoice\",\n \"fedimintTakeBack\", \"fedimintBackup\", \"fedimintExportBackup\", \"fedimintRestoreBackup\", \"fedimintRestorePhrase\",\n \"sparkCreate\", \"sparkBackup\", \"sparkExportBackup\", \"sparkRestoreBackup\", \"sparkRefresh\", \"sparkUseForLightning\",\n \"preparePayment\", \"approvePayment\", \"reconcilePayment\", \"cancelPayment\", \"refreshPublicProfiles\",\n \"choosePublicProfile\", \"preparePeerProof\", \"submitPeerProof\", \"withdrawPeerProof\", \"beginIdentityProof\",\n \"completeIdentityProof\", \"cancelIdentityProof\", \"removeIdentityProof\", \"shareIdentityProof\",\n \"withdrawIdentityProof\", \"recheckIdentityProof\", \"lookupIdentityDisplay\", \"loadPublicProfile\", \"loadPublicPosts\",\n \"loadPublicGraph\", \"loadPublicPostImage\", \"clearProfileData\", \"setDidListed\", \"nostrLoadContact\", \"nostrForgetContact\", \"nostrLoadOwn\",\n \"nostrLookup\", \"nostrDraft\", \"nostrPublish\", \"createLink\", \"takeInvite\", \"joinLink\", \"ensureLink\", \"confirmPair\",\n \"pollNow\", \"removeLink\", \"renameLink\", \"setActiveLink\", \"sendMessage\", \"editMessage\", \"retryMessage\", \"react\", \"pinMessage\", \"appId\", \"appOpen\", \"appClose\", \"appSend\", \"messageDetails\",\n \"deleteMessage\", \"exportLinks\", \"sendFile\", \"forwardMessages\", \"fileAction\", \"setDeliveryMode\", \"setTransportPreference\",\n \"setChatTransport\", \"setChatPaymentMethods\", \"setChatHold\", \"connect\", \"walletAddMint\", \"walletCreate\",\n \"walletRemove\", \"walletTestCoins\", \"walletSetupRetry\", \"walletSetupDismiss\", \"wake\", \"peekProfile\", \"walletSetPrimaryMint\", \"walletRemoveMint\", \"walletReceiveLightning\",\n \"walletQuoteInvoice\", \"walletPayQuote\", \"lnurlResolve\", \"lnurlInvoice\", \"checkPayment\", \"lightningSetSource\",\n \"lightningClearSource\", \"lightningRetrySource\", \"lightningReconfigureSource\", \"lightningRefresh\",\n \"lightningSetReceive\", \"lightningRename\", \"bitcoinSetSource\", \"bitcoinClearSource\", \"bitcoinRetrySource\",\n \"bitcoinReconfigureSource\", \"bitcoinReceiveAddress\", \"bitcoinRefresh\", \"walletReceiveToken\", \"walletInspectCashu\",\n \"walletExport\", \"walletBackupReminder\", \"sendPayment\", \"requestPayment\", \"requestGroupPayment\", \"groupPaymentHello\", \"askToPay\",\n \"payRequest\", \"reclaimPayment\", \"disconnect\", \"addService\", \"removeService\", \"setServiceEnabled\",\n \"setServiceShared\", \"updateSettings\", \"setCallSignal\", \"setTyping\", \"setWakeSubscription\", \"setWakeMuted\", \"wakeForCall\", \"setCallOn\", \"wakeConfirm\", \"setFastPoll\", \"createGroup\", \"inviteToGroup\",\n \"acceptGroupInvitation\", \"declineGroupInvitation\", \"enableGroupLink\", \"disableGroupLink\", \"joinGroupByLink\",\n \"sendGroupMessage\", \"sendGroupFile\", \"groupFileCheck\", \"pressButton\", \"groupMessages\", \"messagePage\", \"statusCardIndex\", \"groupTaken\", \"leaveGroup\", \"removeGroupMember\", \"makeGroupAdmin\", \"setGroupHub\", \"rotateGroup\",\n \"setGroupPicture\", \"renameGroup\", \"setGroupTyping\", \"forgetGroup\", \"setGroupManage\",\n // One profile on several devices (WISP 06): a CLI profile is always on one device, so these answer `single` or refuse.\n \"deviceEnrollInvite\", \"deviceEnrollConfirm\", \"deviceEnrollCancel\", \"deviceEnrollView\", \"deviceEnrollJoin\", \"deviceEnrollReady\", \"deviceEnrollFinish\",\n \"deviceEnrollRemove\", \"deviceSet\", \"devicePing\", \"deviceHandoffVerifier\", \"deviceHandoffPush\", \"deviceHandoffPull\", \"deviceHandoffAccept\",\n \"deviceHandoffSettle\", \"deviceHandoffCancel\", \"deviceHandoffView\", \"deviceHandoffAllow\", \"deviceTakeoverInfo\", \"deviceTakeover\", \"deviceTurnPeek\", \"deviceForkDiscard\", \"deviceRestoreStartOwn\",\n \"deviceRemove\", \"deviceNewSecret\", \"deviceSecretOfferDismiss\", \"deviceSetNoticeSeen\", \"deviceTurnCheck\", \"devicePushState\", \"devicePushSet\",\n \"appList\", \"appStoreList\", \"appStorePreview\", \"appStoreAdd\", \"appStoreRemove\", \"appStoreRefresh\", \"appPreview\", \"appInstall\",\n \"appUpdateAccept\", \"appCheckUpdates\", \"appUninstall\", \"appRunCheck\", \"appFetchFiles\", \"appEntry\", \"appFile\", \"appStorageGet\", \"appStorageSet\",\n \"appStorageDelete\", \"appStorageKeys\", \"appDataExport\",\n];\n\n/** Reads the hosts use outside `EngineApi`: the whole state, and one chat's stored messages. */\nexport const ENGINE_READS: readonly string[] = [\"getState\", \"getMessages\"];\n\n/**\n * Calls whose answer holds a secret (chat seeds, wallet phrases and keys, ecash, backups): the CLI prints their\n * result only with `--show-secret`.\n */\nexport const SECRET_RESULTS: ReadonlySet<string> = new Set([\n \"exportLinks\", \"takeInvite\", \"walletExport\", \"usdtReveal\",\n \"arkBackup\", \"barkBackup\", \"sparkBackup\", \"fedimintBackup\",\n \"usdtExportBackup\", \"arkExportBackup\", \"barkExportBackup\", \"sparkExportBackup\", \"fedimintExportBackup\",\n \"fedimintSpendNotes\",\n]);\n","import type { GhostlyNode } from \"@ghostly/browser/engine/node\";\nimport type { EngineState, GroupView, LinkView, StoredMessage } from \"@ghostly/browser/shared/types\";\nimport { CliError } from \"./errors\";\nimport type { CallManager } from \"./calls/manager\";\nimport type { EventHub } from \"./events\";\nimport type { Runtime } from \"./runtime/engine\";\n\n/**\n * The methods of the local control API (WISP 1100): the daemon answers them over its socket, and a one-shot\n * command calls them in its own process. Parameters come from JSON: every one is checked here.\n */\nexport interface ApiContext {\n runtime: Runtime;\n hub: EventHub;\n /** Voice calls (WISP 1100 § Calls). */\n calls: CallManager;\n /** How the host runs: a daemon stays; a one-shot leaves when its command is done. */\n mode: \"daemon\" | \"one-shot\";\n version: string;\n /** The daemon asks to stop (answered first). */\n stop?: () => void;\n}\n\nexport type Params = Record<string, unknown>;\nexport type Method = (ctx: ApiContext, params: Params) => Promise<unknown>;\n\nexport const node = (ctx: ApiContext): GhostlyNode => ctx.runtime.server.node;\nexport const state = (ctx: ApiContext): EngineState => node(ctx).getState();\n\n/**\n * The time of a send the engine names its row after (`me_<timestamp>`: a file, a payment, a request), its own even\n * within one millisecond (a bot paying or sending several times at once over the socket, `file send` run in\n * parallel), or the chat keeps only one. One clock for all of them: they share the row names. A file's resend after a\n * drop finds its row by it, as a forward does.\n */\nlet lastSentAt = 0;\nexport const sentAt = () => (lastSentAt = Math.max(Date.now(), lastSentAt + 1));\n\n// ---------- parameters ----------\n\nexport function str(params: Params, name: string, required: true): string;\nexport function str(params: Params, name: string, required?: false): string | undefined;\nexport function str(params: Params, name: string, required = false): string | undefined {\n const value = params[name];\n if (value === undefined || value === null || value === \"\") {\n if (required) throw new CliError(\"bad_request\", `${name} is required`);\n return undefined;\n }\n if (typeof value !== \"string\") throw new CliError(\"bad_request\", `${name} must be a string`);\n return value;\n}\n/** A number in `[min, max]`; `whole` takes only whole ones (a count of messages), as the engine needs them. */\nexport function num(params: Params, name: string, fallback: number, { min = 0, max = Number.MAX_SAFE_INTEGER, whole = false } = {}): number {\n const value = params[name];\n if (value === undefined || value === null) return fallback;\n if (typeof value !== \"number\" || !Number.isFinite(value) || value < min || value > max) throw new CliError(\"bad_request\", `${name} must be a ${whole ? \"whole \" : \"\"}number from ${min} to ${max}`);\n if (whole && !Number.isSafeInteger(value)) throw new CliError(\"bad_request\", `${name} must be a whole number from ${min} to ${max}`);\n return value;\n}\nexport function bool(params: Params, name: string): boolean {\n const value = params[name];\n if (value === undefined || value === null) return false;\n if (typeof value !== \"boolean\") throw new CliError(\"bad_request\", `${name} must be true or false`);\n return value;\n}\nexport function list(params: Params, name: string): string[] {\n const value = params[name];\n if (value === undefined || value === null) return [];\n if (!Array.isArray(value) || value.some((item) => typeof item !== \"string\")) throw new CliError(\"bad_request\", `${name} must be a list of strings`);\n return value as string[];\n}\nexport function oneOf<T extends string>(params: Params, name: string, values: readonly T[], fallback: T): T {\n const value = str(params, name) ?? fallback;\n if (!values.includes(value as T)) throw new CliError(\"bad_request\", `${name} must be one of ${values.join(\", \")}`);\n return value as T;\n}\n\n// ---------- finding things ----------\n\n/** A contact key as chats show it (`peer`): 52 characters of z-base-32. */\nexport const CONTACT_KEY = /^[ybndrfg8ejkmcpqxot1uwisza345h769]{52}$/;\n\n/**\n * A chat by its id; by its contact's key (only by the key: a name is never taken for one); by a unique prefix of its\n * id; or by its name: the label this side gave it first, and only when no label matches, the name the contact gave\n * itself (which the contact controls, so it never wins over a label).\n */\nexport function findChat(links: readonly LinkView[], ref: string): LinkView {\n return chatBy(links, ref).link;\n}\n\n/** `findChat`, and whether only the name the contact gave itself (`theirName`) found the chat. */\nfunction chatBy(links: readonly LinkView[], ref: string): { link: LinkView; theirName: boolean } {\n const exact = links.find((link) => link.id === ref);\n if (exact) return { link: exact, theirName: false };\n if (CONTACT_KEY.test(ref)) {\n const byKey = links.filter((link) => link.peerPubKeyZ32 === ref);\n if (byKey.length === 1) return { link: byKey[0], theirName: false };\n if (byKey.length > 1) throw new CliError(\"bad_request\", `${JSON.stringify(ref)} names more than one chat: use its id`, { matches: byKey.map((link) => link.id) });\n throw new CliError(\"not_found\", `No chat with ${JSON.stringify(ref)}`);\n }\n const byPrefix = links.filter((link) => link.id.startsWith(ref));\n if (byPrefix.length === 1) return { link: byPrefix[0], theirName: false };\n const lower = ref.toLowerCase();\n const named = (name: string | null | undefined) => name?.trim().toLowerCase() === lower;\n const byLabel = links.filter((link) => named(link.label));\n const byName = byLabel.length ? byLabel : links.filter((link) => named(link.peerNick));\n if (byName.length === 1) return { link: byName[0], theirName: !byLabel.length };\n if (byPrefix.length > 1 || byName.length > 1) throw new CliError(\"bad_request\", `${JSON.stringify(ref)} names more than one chat: use its id`, { matches: [...byPrefix, ...byName].map((link) => link.id) });\n throw new CliError(\"not_found\", `No chat ${JSON.stringify(ref)}`);\n}\n\n/** Only an invitation so far: this profile is not in the group (yet), and whoever invited chose its name. */\nconst invitedOnly = (group: GroupView) => group.status === undefined && !!group.invitation;\n\n/**\n * A group by its id, by a unique prefix of its id, or by its name. A group this profile is in wins over an invitation\n * with the same prefix or name (the one who invites chooses those); an invitation is still found by its id, and by\n * prefix or name where no group of this profile has it.\n */\nexport function findGroup(groups: readonly GroupView[], ref: string): GroupView {\n const exact = groups.find((group) => group.id === ref);\n if (exact) return exact;\n const mineFirst = (found: GroupView[]) => { const mine = found.filter((group) => !invitedOnly(group)); return mine.length ? mine : found; };\n const byPrefix = mineFirst(groups.filter((group) => group.id.startsWith(ref)));\n if (byPrefix.length === 1) return byPrefix[0];\n const byName = mineFirst(groups.filter((group) => group.name.trim().toLowerCase() === ref.toLowerCase()));\n if (byName.length === 1) return byName[0];\n if (byPrefix.length > 1 || byName.length > 1) throw new CliError(\"bad_request\", `${JSON.stringify(ref)} names more than one group: use its id`, { matches: [...byPrefix, ...byName].map((group) => group.id) });\n throw new CliError(\"not_found\", `No group ${JSON.stringify(ref)}`);\n}\n\n/**\n * A chat or a group, where a command takes either: `group:<id>` is a group; anything else a chat first (`findChat`),\n * then a group. A chat found only by the name its contact gave itself never takes what also names a group: a group's\n * own id is that group, and a prefix or the name of a group this profile is in is refused, with both ids. With neither,\n * one `not_found` names both and where to look (a contact's key: no chat with it); a name that matches more than one\n * chat or group stays that error.\n */\nexport function findChatOrGroup(links: readonly LinkView[], groups: readonly GroupView[], ref: string): { link: LinkView } | { group: GroupView } {\n if (ref.startsWith(\"group:\")) return { group: findGroup(groups, ref.slice(\"group:\".length)) };\n let chat: ReturnType<typeof chatBy>;\n try {\n chat = chatBy(links, ref);\n } catch (error) {\n if (!(error instanceof CliError) || error.code !== \"not_found\") throw error;\n try { return { group: findGroup(groups, ref) }; } catch (inner) {\n if (!(inner instanceof CliError) || inner.code !== \"not_found\") throw inner;\n if (CONTACT_KEY.test(ref)) throw error;\n throw new CliError(\"not_found\", `No chat or group ${JSON.stringify(ref)} (ghostly chat list, ghostly group list)`);\n }\n }\n if (!chat.theirName) return { link: chat.link };\n const exact = groups.find((group) => group.id === ref);\n if (exact) return { group: exact };\n const lower = ref.toLowerCase();\n const also = groups.filter((group) => !invitedOnly(group) && (group.id.startsWith(ref) || group.name.trim().toLowerCase() === lower));\n if (also.length) throw new CliError(\"bad_request\", `${JSON.stringify(ref)} names a chat (the name its contact chose) and a group: use the chat's id or group:<id>`, { chat: chat.link.id, groups: also.map((group) => group.id) });\n return { link: chat.link };\n}\n\nexport const chatOf = (ctx: ApiContext, params: Params) => findChat(state(ctx).links, str(params, \"chat\", true));\n\n/** A chat or a group as `findChatOrGroup` reads it. `linkId` is what the engine calls it. */\nexport function chatOrGroup(ctx: ApiContext, ref: string): { id: string; linkId: string; group: boolean } {\n const found = findChatOrGroup(state(ctx).links, state(ctx).groups, ref);\n return \"group\" in found ? { id: found.group.id, linkId: `group:${found.group.id}`, group: true } : { id: found.link.id, linkId: found.link.id, group: false };\n}\n\nexport const groupOf = (ctx: ApiContext, params: Params) => findGroup(state(ctx).groups, str(params, \"group\", true));\n\n/**\n * A message of a chat or a group (`group:<id>`) named by its id here, read as one row; only a name that is no such\n * row's key (the id both sides know, `wireId`) reads the whole history. `keep`: only a message it accepts (mine, or\n * someone else's). The history is synchronous work on the daemon's one thread, about 120 ms at 10,000 messages.\n */\nexport async function messageOf(ctx: ApiContext, linkId: string, ref: string, keep: (message: StoredMessage) => boolean = () => true): Promise<StoredMessage | undefined> {\n const found = await node(ctx).getMessage(linkId, ref);\n if (found && keep(found)) return found;\n const all = linkId.startsWith(\"group:\") ? await node(ctx).groupMessages({ groupId: linkId.slice(\"group:\".length) }) : await node(ctx).getMessages(linkId);\n return all.find((m) => keep(m) && m.wireId === ref);\n}\n\n// ---------- waiting ----------\n\n/** Resolves with `check`'s first defined answer: now, or on a later state; `timeout` error after `ms`. */\nexport function waitForState<T>(ctx: ApiContext, check: (state: EngineState) => T | undefined, ms: number, what: string): Promise<T> {\n const now = check(state(ctx));\n if (now !== undefined) return Promise.resolve(now);\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => { off(); reject(new CliError(\"timeout\", `Timed out after ${Math.round(ms / 1000)} s waiting for ${what}`)); }, ms);\n const off = ctx.hub.onState((next) => {\n const value = check(next);\n if (value === undefined) return;\n clearTimeout(timer);\n off();\n resolve(value);\n });\n });\n}\n\n","import { FILE_BYTES_STEP, fileBytesOf, type FileBytes } from \"./fileBytes\";\nimport { fileStore, type StoredFile } from \"./idb\";\n\n/** The bytes a file holds: its metadata says, and a file stored before that says it with its Blob. */\nexport function storedSize(file: StoredFile): number {\n return file.metadata?.size ?? file.blob?.size ?? 0;\n}\n\nasync function backend(file: StoredFile): Promise<FileBytes | null> {\n return file.bytes ? fileBytesOf(file.bytes) : null;\n}\n\n/** The file as a Blob backed by storage, or null where it cannot be had as one (see `FileBytes.blob`). */\nexport async function storedBlob(file: StoredFile, type: string): Promise<Blob | null> {\n if (file.blob) return file.blob.slice(0, file.blob.size, type);\n return (await backend(file))?.blob(file.id, type) ?? null;\n}\n\n/** At most `length` bytes from `offset`. */\nexport async function readStored(file: StoredFile, offset: number, length: number): Promise<Uint8Array> {\n if (file.blob) return new Uint8Array(await file.blob.slice(offset, offset + length).arrayBuffer());\n const bytes = await backend(file);\n if (!bytes) throw new Error(\"The file is gone\");\n return bytes.read(file.id, offset, length);\n}\n\n/** The file, a step at a time, from `offset`. */\nexport async function* streamStored(file: StoredFile, offset = 0): AsyncGenerator<Uint8Array> {\n const size = storedSize(file);\n while (offset < size) {\n const part = await readStored(file, offset, Math.min(FILE_BYTES_STEP, size - offset));\n if (!part.length) throw new Error(\"The file is shorter than it says\");\n offset += part.length;\n yield part;\n }\n}\n\n/** A file's record and its bytes, wherever they are. */\nexport async function removeStored(id: string): Promise<void> {\n const file = await fileStore.get(id);\n await fileStore.delete(id);\n if (file?.bytes) await (await backend(file))?.remove(id);\n}\n\n/** Writes are gathered to this size before they go to storage: a chunk from the wire is 16 KiB. */\nconst GATHER = 256 * 1024;\n\n/**\n * Writes one file in order, gathering small writes into larger ones. `written` counts what was handed over,\n * gathered or not; `flush` makes all of it durable.\n */\nexport class FileAppender {\n private buffer = new Uint8Array(GATHER);\n private buffered = 0;\n private stored: number;\n\n constructor(readonly bytes: FileBytes, readonly id: string, offset = 0) { this.stored = offset; }\n\n get written(): number { return this.stored + this.buffered; }\n\n async append(chunk: Uint8Array): Promise<void> {\n if (this.buffered + chunk.length > GATHER) await this.drain();\n if (chunk.length >= GATHER) {\n await this.bytes.append(this.id, this.stored, chunk);\n this.stored += chunk.length;\n return;\n }\n this.buffer.set(chunk, this.buffered);\n this.buffered += chunk.length;\n }\n\n private async drain(): Promise<void> {\n if (!this.buffered) return;\n const part = this.buffer.slice(0, this.buffered);\n await this.bytes.append(this.id, this.stored, part);\n this.stored += part.length;\n this.buffered = 0;\n }\n\n async flush(): Promise<void> {\n await this.drain();\n await this.bytes.flush(this.id);\n }\n\n async close(): Promise<void> {\n await this.drain();\n await this.bytes.close(this.id);\n }\n}\n","/**\n * Payment ids are chosen by whoever sends them, and every member of a group sees the requests the others make to the\n * group. So a contact's request is kept under the id it came with, unless a record of another chat already holds that\n * id: then under `<id>.<chat>`. A `.` is never in a payment id on the wire (8 to 64 of `A-Z a-z 0-9 _ -`), so no\n * contact can name another's record, and two contacts' requests never share one.\n */\nexport const paymentAlias = (wireId: string, linkId: string): string => `${wireId}.${linkId}`;\n\n/** The id a payment record goes by on the wire, which is also the one its chat and its group's notes know it by. */\nexport const paymentWireId = (key: string): string => key.split(\".\", 1)[0];\n","import { replySnippet, type WireReply } from \"@ghostly/core\";\nimport { paymentWireId } from \"./paymentIds\";\nimport type { MessageReply, StoredMessage } from \"./types\";\n\n/*\n * Replies (WISP 400 § Replies) as the engine and the UI both need them: which id a message goes by on both sides of\n * its chat, and a reply as it is kept and as it goes on the wire.\n */\n\n/** A paired chat's text (`me_`/`peer_` + its 22-character wire id), or a held file (`peer_` + its 16-character one). */\nconst PAIRED_ROW = /^(?:me|peer)_([A-Za-z0-9_-]{22}|[A-Za-z0-9_-]{16})$/;\nconst WIRE_ID = /^[A-Za-z0-9_-]{8,64}$/;\nconst GROUP_LINK = \"group:\";\n\n/** The parts of a message, as the engine or the UI keeps it, that say which id both sides know it by. */\nexport interface ReplyTarget {\n id: string;\n linkId?: string;\n wireId?: string;\n paymentId?: string;\n file?: { id: string };\n event?: unknown;\n groupPay?: unknown;\n /** Set by the UI from the engine's row (`replyRef`), for what the UI's own copy no longer says. */\n ref?: string;\n}\n\n/**\n * The id a message of a chat goes by on both sides, which a reply names; none for what only this side has (a notice,\n * a group payment line, a refused payment). A paired chat: the text's wire id, a payment's id, a file's wire id. A\n * group: the message id, which is the same for every member.\n */\nexport function replyRef(message: ReplyTarget, group = message.linkId?.startsWith(GROUP_LINK) ?? false): string | undefined {\n if (message.ref) return message.ref;\n if (group) return message.paymentId || message.event || message.groupPay || !message.id.includes(\":\") ? undefined : message.id;\n if (message.paymentId) return paymentWireId(message.paymentId);\n if (message.wireId && WIRE_ID.test(message.wireId)) return message.wireId;\n const out = message.file?.id.match(/-out-([A-Za-z0-9_-]{8,64})$/);\n if (out) return out[1];\n const row = message.id.match(PAIRED_ROW);\n return row && !/^\\d+$/.test(row[1]) ? row[1] : undefined;\n}\n\n/** A reply to `original` as this side keeps it: always found, since it is here. The engine's row, or the UI's copy. */\nexport function replyTo(original: Pick<StoredMessage, \"id\" | \"text\" | \"member\"> & { sender: string }, ref: string): MessageReply {\n return { id: ref, snippet: replySnippet(original.text), from: original.sender === \"me\" ? \"me\" : \"peer\",\n ...(original.member && { member: original.member }), messageId: original.id };\n}\n\n/**\n * A reply as it goes on a paired chat's wire, from the side that sends it (`f`: who wrote the original, from the\n * sender's side). For a group, `f` is the author's member key, which the reply keeps.\n */\nexport function pairedWireReply(reply: MessageReply, sender: \"me\" | \"peer\" = \"me\"): WireReply {\n return { i: reply.id, s: reply.snippet, f: reply.from === sender ? \"sender\" : \"recipient\", ...(reply.button && { b: reply.button }) };\n}\n\n/** A paired chat's reply as it arrived (`f` from the sender's side), as this side keeps it. */\nexport function receivedPairedReply(reply: WireReply | { i: string }): MessageReply {\n if (!(\"f\" in reply)) return { id: reply.i, snippet: \"\" };\n return { id: reply.i, snippet: reply.s, from: reply.f === \"sender\" ? \"peer\" : \"me\", ...(reply.b && { button: reply.b }) };\n}\n\n/** A group's reply as it arrived or was sent (`f` the author's member key), as this member keeps it. */\nexport function groupReply(reply: WireReply, myKey: string): MessageReply {\n return { id: reply.i, snippet: reply.s, from: reply.f === myKey ? \"me\" : \"peer\", member: reply.f, ...(reply.b && { button: reply.b }) };\n}\n","import {\n ChatFiles,\n GROUP_FILE_LIMITS,\n GROUP_HAVE_FRAME,\n GROUP_WANT_FRAME,\n GROUP_WANT_NO_FRAME,\n RateWindow,\n fetchesByItself,\n fileMessageText,\n groupFileFallback,\n isGroupMessageId,\n randomBytes,\n readGroupHave,\n readGroupWant,\n readGroupWantNo,\n toBase64Url,\n transferEnded,\n type FileInfo,\n type FileTransferRecord,\n type GroupFileMeta,\n type GroupWantRefusal,\n type IncomingTarget,\n type OfferDecision,\n type OutgoingSource,\n} from \"@ghostly/core\";\nimport { fileBytes, fileBytesOf } from \"../shared/fileBytes\";\nimport { fileStore, type StoredFile } from \"../shared/idb\";\nimport { FileAppender, readStored, removeStored } from \"../shared/storedFiles\";\nimport type { FileTransferView, MessageFile } from \"../shared/types\";\n\n/*\n * Files and voice messages in groups (WISP 503 · Group Files), the engine's side. A file is announced as a group message\n * carrying its description (`fl`); each member's app then fetches the bytes from a member that holds them, over\n * `files/3` on the edge between the two (`ChatFiles`, WISP 501): it sends `group-want`, the holder offers the file with\n * `gm` naming the message, and what arrives is checked against the author's digest. Whoever stored it holds it, and says\n * so (`group-have`), so a member away during the announcement gets it later from whoever is there.\n */\n\n/** A group file's local id: its group's and a random part, never anything a member sends. */\nexport const groupFileId = (groupId: string, direction: \"in\" | \"out\", random = toBase64Url(randomBytes(12))) => `group-${groupId}-${direction}-${random}`;\n/** The group a local file id names, among `groupIds`. */\nexport const groupOfFile = (fileId: string, groupIds: Iterable<string>): string | undefined =>\n [...groupIds].find(g => fileId.startsWith(`group-${g}-in-`) || fileId.startsWith(`group-${g}-out-`));\n\n/** What a file's message says: its caption, or (when the author wrote none) the file itself, not the line older apps show. */\nexport const groupFileText = (text: string, meta: GroupFileMeta): string => text === groupFileFallback(meta) ? fileMessageText(meta) : text;\n\n/** Where a group's files are kept: the profile's file store and file storage (`storedGroupFiles`), or a test's memory. */\nexport interface GroupFileStore {\n /** The records of a group's files (`linkId` `group:<id>`). */\n list(groupId: string): Promise<StoredFile[]>;\n /** Every group a stored file is of, whether or not this device still has the group. */\n groups(): Promise<string[]>;\n get(id: string): Promise<StoredFile | undefined>;\n put(file: StoredFile): Promise<void>;\n patch(id: string, fields: Pick<StoredFile, \"transfer\" | \"group\">): Promise<void>;\n /** The record and its bytes. */\n remove(id: string): Promise<void>;\n /**\n * Writes a file in order: from its first byte (whatever was there goes), or with `resume` after what an earlier\n * transfer stored (the writer's `offset`).\n */\n write(id: string, resume?: boolean): Promise<GroupFileWriter>;\n /** At most `length` bytes of a kept file from `offset`. */\n read(file: StoredFile, offset: number, length: number): Promise<Uint8Array>;\n /** Bytes this device can still take for files, or null when the platform does not say. */\n room(): Promise<number | null>;\n}\n\nexport interface GroupFileWriter {\n /** Where it writes from: the bytes already stored. */\n offset: number;\n append(bytes: Uint8Array): Promise<void>;\n flush(): Promise<void>;\n /** SHA-256 of what was stored, read back from storage, base64url. */\n digest(): Promise<string>;\n /** Done writing for now: what is stored stays. */\n close(): Promise<void>;\n discard(): Promise<void>;\n}\n\n/** The profile's files store and file storage, as a 1:1 chat's files use them. */\nexport const storedGroupFiles: GroupFileStore = {\n list: groupId => fileStore.listForLink(`group:${groupId}`),\n groups: () => fileStore.groupIds(),\n get: id => fileStore.get(id),\n put: file => fileStore.put(file),\n patch: (id, fields) => fileStore.patch(id, fields),\n remove: id => removeStored(id),\n async write(id, resume = false) {\n const stored = await fileStore.get(id);\n const bytes = (stored?.bytes && await fileBytesOf(stored.bytes)) || await fileBytes();\n const size = await bytes.size(id);\n const offset = resume && size !== null ? size : 0;\n if (size !== null && size !== offset) await bytes.truncate(id, offset);\n if (stored && stored.bytes !== bytes.kind) await fileStore.patch(id, { bytes: bytes.kind });\n const appender = new FileAppender(bytes, id, offset);\n return {\n offset,\n append: chunk => appender.append(chunk),\n flush: () => appender.flush(),\n digest: async () => { await appender.close(); return bytes.digest(id); },\n close: () => appender.close(),\n discard: async () => { await appender.close().catch(() => {}); await bytes.remove(id); },\n };\n },\n read: (file, offset, length) => readStored(file, offset, length),\n room: async () => (await fileBytes()).room(),\n};\n\n/** What the engine gives the group files: where they are kept, their views, the profile's settings, an edge's files/3. */\nexport interface GroupFilesHost {\n store: GroupFileStore;\n transfers: Map<string, FileTransferView>;\n settings(): { autoDownloads: boolean; serveFiles: boolean };\n /** A files/3 frame on an edge's open session; false when it could not go. */\n sendFiles(linkId: string, frame: Record<string, unknown>): boolean;\n /** Resolves once the edge's session can take another data frame. */\n writable?(linkId: string): Promise<void> | undefined;\n}\n\n/** Me in a group I am active in, and who could read one of its messages. */\nexport interface GroupFileMembership {\n me: string;\n community: boolean;\n inRoster(key: string): boolean;\n /** In the roster of the message's epoch and in the roster now (WISP 503 § Asking). */\n couldRead(key: string, messageId: string): boolean;\n}\n\nexport interface GroupFileDeps extends GroupFilesHost {\n /** A group frame on an edge; false when it could not go. */\n send(linkId: string, frame: Record<string, unknown>): boolean;\n /** Member key → edge link id, for the group's edges. */\n edges(groupId: string): Map<string, string>;\n /** The edge is up, and the app at its other end announced group files (`GROUP_VERSION_FILES`). */\n ready(linkId: string): boolean;\n membership(groupId: string): GroupFileMembership | undefined;\n /** An application frame to everyone in a community. */\n sendApp(groupId: string, frame: Record<string, unknown>): Promise<void>;\n changed(): void;\n now(): number;\n}\n\n/** A group file this device knows of: one it announced, or one a member announced. */\ninterface Kept {\n id: string;\n groupId: string;\n message: string;\n author: string;\n meta: GroupFileMeta;\n /** I announced it: my copy is its first. */\n mine: boolean;\n /** Fetched without asking: it counts toward the group's automatic limit. */\n auto: boolean;\n /** Its person asked for it (Download). */\n asked: boolean;\n /** Every byte is here, checked against the author's digest: this device holds it. */\n held: boolean;\n /** Members said to hold it (`group-have`), newest first. The author is asked whether or not it is here. */\n holders: string[];\n /** A discard of what an earlier transfer stored, which the next one waits for. */\n idle?: Promise<void>;\n /** Bytes a transfer stored here that the next one goes on from, shown while it waits for a holder. */\n stored?: number;\n /** The transfer (its id on the edge) its person cancelled: what it stored is deleted, not kept for the next one. */\n dropped?: string;\n /**\n * The bytes kept from an earlier holder did not check out with the next one's tail: who served the bad part is not\n * known, so the next transfer starts from 0 (`fresh`) and the holder whose tail failed is not counted as damaged\n * (`unclear`, read once by `transferChanged`).\n */\n fresh?: boolean;\n unclear?: boolean;\n /**\n * Holders whose bytes failed the author's digest: not asked again for it, whatever the round, until its person asks\n * for it again (Download). Once every holder known is here, it is fetched no more (`DAMAGED`).\n */\n damaged: Set<string>;\n}\n\n/** A file being asked for: of one holder at a time. */\ninterface Want {\n /** The holder asked now and the edge it was asked on; none while nobody reachable has it. */\n holder?: string;\n linkId?: string;\n at: number;\n /** Holders asked in this round that had nothing to give: the next ask goes to another. */\n tried: Set<string>;\n /** The holder's transfer, once it offered: its id on the edge, and when its bytes last moved. */\n wire?: string;\n moved: number;\n movedAt: number;\n /** What the last holder asked in this round answered, when it was not the file: shown while the want waits. */\n why?: \"busy\" | \"damaged\";\n}\n\n/** One edge's files/3, both ways. */\ninterface Edge {\n linkId: string;\n groupId: string;\n peer: string;\n files: ChatFiles;\n /** Transfers this side serves on the edge (their wire id) → the local file. */\n serving: Map<string, string>;\n /** Of each transfer served: the bytes the asker confirmed, and when that last changed. */\n servedMoved: Map<string, { confirmed: number; at: number }>;\n /** Transfers this side takes on the edge (the holder's wire id) → the local file. */\n taking: Map<string, string>;\n /** Transfers that ended on the edge, oldest first: the last `ENDED_KEPT` keep their record, the rest are forgotten. */\n ended: [\"in\" | \"out\", string][];\n /** Message ids to say in the next `group-have`, and when one last went. */\n have: Set<string>;\n haveAt: number;\n /** Message ids its member asked for while this device served all it serves at once: said in a `group-have` once a slot frees. */\n busy: Set<string>;\n}\n\n/**\n * Ended transfers an edge keeps the record of. A late frame for one of them is still answered as files/3 says (an abort\n * to an asker going on with a transfer cancelled here); older ones are forgotten, so an edge's records stay few\n * however many transfers it has seen.\n */\nconst ENDED_KEPT = 8;\n\n/** Why a want waits, as the person reads it (the app shows its own words for each `wait`). */\nconst WAITING_NOTES = {\n nobody: \"Nobody you are connected to has this file yet\",\n busy: \"Its holders are busy; asking again soon\",\n damaged: \"It arrived damaged; ask again\",\n} as const;\n/** Why a want stopped: every holder served bytes that are not the author's file. */\nconst DAMAGED = \"Every copy of this file arrived damaged and was deleted. Ask for it again later.\";\n\nexport class GroupFileDesk {\n private readonly kept = new Map<string, Kept>();\n /** `<group>\\n<message id>` → local id. */\n private readonly byMessage = new Map<string, string>();\n private readonly wants = new Map<string, Want>();\n private readonly edgesById = new Map<string, Edge>();\n /** My file about to be announced, by `<group>\\n<digest>`: the message that announces it takes this local id. */\n private readonly own = new Map<string, string>();\n /** Announcements sent per group, and taken per member of a group (WISP 503 § Rules for both, Pace). */\n private readonly sentPace = new Map<string, RateWindow>();\n private readonly sentTaken = new Map<string, RateWindow>();\n private readonly takenPace = new Map<string, RateWindow>();\n /** `<group>\\n<message id>` a `group-have` named before its message came → its holders, newest first. */\n private readonly ahead = new Map<string, string[]>();\n /** A community's ids for its next `group-have`, and when one last went. */\n private readonly communityHave = new Map<string, { ids: Set<string>; at: number }>();\n /** Bytes served today, and the day (UTC days since the epoch). */\n private served = { day: 0, bytes: 0 };\n\n constructor(private readonly deps: GroupFileDeps) {}\n\n // -- what the groups hand over -------------------------------------------------------------------------------\n\n /**\n * At start: each group's files as they were kept. One fetched by itself, or asked for, and not here yet is asked for\n * again; with automatic downloads off, one chosen by itself before waits for a Download instead.\n */\n async load(groupIds: string[]): Promise<void> {\n for (const groupId of groupIds) {\n for (const file of await this.deps.store.list(groupId).catch(() => [])) {\n if (!file.group || !file.metadata || !file.digest || this.kept.has(file.id)) continue;\n const { name, size, mime, voice, video, image } = file.metadata;\n const kept: Kept = { id: file.id, groupId, message: file.group.message, author: file.group.author,\n meta: { name, size, mime, d: file.digest, ...(voice && { voice }), ...(video && { video }), ...(image && { image }) },\n mine: file.direction === \"out\", auto: !!file.group.auto, asked: !!file.group.asked, held: file.transfer?.state === \"done\", holders: [], damaged: new Set() };\n this.add(kept);\n if (kept.held) this.show(kept, { state: \"done\", transferred: size, size, direction: kept.mine ? \"out\" : \"in\" });\n // Every holder served it damaged: it waits for its person to ask again, not fetched again by itself.\n else if (file.transfer?.state === \"failed\") this.show(kept, { state: \"failed\", transferred: 0, size, direction: \"in\", retry: true, error: file.transfer.error ?? DAMAGED });\n else if (kept.asked || (kept.auto && this.deps.settings().autoDownloads)) this.wants.set(kept.id, this.newWant());\n else if (kept.auto) await this.stop(kept, false);\n else this.show(kept, { state: \"transferring\", stage: \"asking\", transferred: 0, size, direction: \"in\" });\n }\n }\n for (const [id] of this.wants) this.ask(this.kept.get(id)!);\n this.deps.changed();\n }\n\n /**\n * At start, once every group was read: the files of a group this device no longer has (`groupIds` are the groups it\n * has, a tombstone or a join under way among them) go, with their bytes. A group left before Leave took its files\n * left them behind, where nothing lists them.\n */\n async sweep(groupIds: string[]): Promise<void> {\n const here = new Set(groupIds);\n for (const groupId of await this.deps.store.groups().catch(() => [])) {\n if (here.has(groupId)) continue;\n for (const file of await this.deps.store.list(groupId).catch(() => [])) await this.deps.store.remove(file.id).catch(() => {});\n }\n }\n\n /**\n * The person stops a file that is coming, or that this device would fetch (Cancel, Decline): nobody is asked for it\n * any more, the transfer under way ends, what came of it is deleted, and it waits for a Download. Paused (`keep`),\n * what came stays, and Resume (`download`) goes on from there.\n */\n async halt(fileId: string, keep = false): Promise<void> {\n const kept = this.kept.get(fileId);\n if (!kept) throw new Error(\"No such file\");\n // Only a file that is coming pauses: one waiting for a Download has nothing to go on with.\n if (keep && !this.wants.has(fileId)) return;\n await this.stop(kept, keep);\n }\n\n /** Automatic downloads were turned off: files chosen by themselves and not here yet stop coming, and wait for a Download. */\n async autoDownloadsOff(): Promise<void> {\n for (const kept of [...this.kept.values()]) if (kept.auto && !kept.asked && this.wants.has(kept.id)) await this.stop(kept, false);\n }\n\n private async stop(kept: Kept, keep: boolean): Promise<void> {\n if (kept.held || kept.mine) return;\n const want = this.wants.get(kept.id);\n // Gone first: the transfer's end below asks nobody else (`transferChanged`), and a late offer is refused.\n this.wants.delete(kept.id);\n const edge = want?.wire && want.linkId ? this.edgesById.get(want.linkId) : undefined;\n const record = edge?.files.get(\"in\", want!.wire!);\n const { size } = kept.meta;\n if (!keep) { kept.stored = 0; kept.fresh = true; }\n if (edge && record && !transferEnded(record)) {\n // The transfer's own writer deletes what it stored (`openTarget`), or keeps it when paused.\n if (!keep) kept.dropped = record.id;\n edge.files.cancel(\"in\", record.id);\n edge.taking.delete(record.id);\n } else if (!keep) {\n // No transfer under way: what an earlier one stored goes, before the next one writes.\n kept.idle = (kept.idle ?? Promise.resolve()).then(() => this.deps.store.write(kept.id)).then(writer => writer.discard()).catch(() => {});\n }\n this.show(kept, keep\n ? { state: \"transferring\", stage: \"paused\", pausedBy: \"me\", transferred: kept.stored ?? 0, size, direction: \"in\" }\n : { state: \"transferring\", stage: \"asking\", transferred: 0, size, direction: \"in\" });\n // Not fetched by itself any more, nor asked for: it stays stopped after a restart, and counts toward no limit.\n if (kept.auto || kept.asked) {\n kept.auto = kept.asked = false;\n await this.deps.store.patch(kept.id, { group: this.groupField(kept) }).catch(() => {});\n }\n }\n\n /**\n * An error when this group took as many announcements of mine as it takes in a minute, or as its members take from\n * one member in `announceWindowMs` (past that they would not fetch the file by themselves); null when one may go now.\n */\n mayAnnounce(groupId: string): string | null {\n const wait = Math.max(...this.sentPaces(groupId).map(pace => pace.wait()));\n if (!wait) return null;\n const minutes = Math.ceil(wait / 60_000);\n return `You sent many files to this group just now. Wait ${minutes > 1 ? `${minutes} minutes` : \"a minute\"}.`;\n }\n\n /** My file, stored under `fileId` with the author's digest, is about to be announced in the group: its message takes it. */\n outgoing(groupId: string, fileId: string, meta: GroupFileMeta): void {\n this.own.set(`${groupId}\\n${meta.d}`, fileId);\n }\n /** The announcement did not go: the file is not this group's after all. */\n notSent(groupId: string, meta: GroupFileMeta): void { this.own.delete(`${groupId}\\n${meta.d}`); }\n\n /**\n * A message of the group announces a file (mine, or a member's): the file its row shows. A member's is kept here\n * unfetched, and fetched now when it is one this device takes by itself and the member announced no more than the\n * group fetches that way. The same message again (a catch-up, a whole copy) is the same file.\n */\n async announced(groupId: string, m: { id: string; sender: string; file: GroupFileMeta }, me: string): Promise<MessageFile | undefined> {\n const known = this.byMessage.get(`${groupId}\\n${m.id}`);\n if (known) return messageFile(this.kept.get(known)!);\n const ownId = m.sender === me ? this.own.get(`${groupId}\\n${m.file.d}`) : undefined;\n if (ownId) {\n this.own.delete(`${groupId}\\n${m.file.d}`);\n for (const pace of this.sentPaces(groupId)) pace.take();\n const kept: Kept = { id: ownId, groupId, message: m.id, author: me, meta: m.file, mine: true, auto: false, asked: false, held: true, holders: [], damaged: new Set() };\n this.add(kept);\n const transfer = { state: \"done\" as const, transferred: m.file.size, size: m.file.size };\n await this.deps.store.patch(ownId, { group: { message: m.id, author: me }, transfer }).catch(() => {});\n this.show(kept, { ...transfer, direction: \"out\" });\n this.queueHave(kept);\n return messageFile(kept);\n }\n // More from one member than a group fetches by itself: past it, the file is kept and waits for a Download (WISP 503,\n // Pace). Counted as they come, so a catch-up of files sent over hours is one burst: none of them is lost to it.\n const paced = this.pace(this.takenPace, `${groupId}\\n${m.sender}`, GROUP_FILE_LIMITS.announceTaken, GROUP_FILE_LIMITS.announceWindowMs).take();\n const id = groupFileId(groupId, \"in\");\n const auto = paced && this.deps.settings().autoDownloads && fetchesByItself(m.file, this.keptAutomatically(groupId));\n const holders = this.ahead.get(`${groupId}\\n${m.id}`) ?? [];\n this.ahead.delete(`${groupId}\\n${m.id}`);\n const kept: Kept = { id, groupId, message: m.id, author: m.sender, meta: m.file, mine: false, auto, asked: false, held: false, holders, damaged: new Set() };\n const { name, size, mime, voice, video, image } = m.file;\n // Known at once: the same message coming again meanwhile (another copy of it) is this file.\n this.add(kept);\n // No bytes yet: where they go is decided when they come (`GroupFileStore.write`).\n await this.deps.store.put({ id, linkId: `group:${groupId}`, direction: \"in\", digest: m.file.d, createdAt: this.deps.now(),\n metadata: { name, size, mime, timestamp: this.deps.now(), voice, video, image },\n transfer: { state: \"transferring\", transferred: 0, size }, group: { message: m.id, author: m.sender, ...(auto && { auto: true }) } });\n if (auto) { this.wants.set(id, this.newWant()); this.ask(kept); }\n else this.show(kept, { state: \"transferring\", stage: \"asking\", transferred: 0, size, direction: \"in\" });\n return messageFile(kept);\n }\n\n /** The person asks for a file this device did not fetch by itself (Download), or for one that stopped coming. */\n async download(fileId: string): Promise<void> {\n const kept = this.kept.get(fileId);\n if (!kept) throw new Error(\"No such file\");\n if (kept.held) return;\n const room = await this.deps.store.room().catch(() => null);\n if (room !== null && kept.meta.size > room) throw new Error(\"Not enough space on this device for this file\");\n if (!kept.asked) {\n kept.asked = true;\n await this.deps.store.patch(kept.id, { group: this.groupField(kept) }).catch(() => {});\n }\n // Asked again: every holder is worth asking again, one that served it damaged too.\n kept.damaged.clear();\n if (this.deps.transfers.get(kept.id)?.state === \"failed\") {\n await this.deps.store.patch(kept.id, { transfer: { state: \"transferring\", transferred: 0, size: kept.meta.size } }).catch(() => {});\n }\n const want = this.wants.get(kept.id);\n if (!want) this.wants.set(kept.id, this.newWant());\n else if (!want.wire) { want.tried.clear(); want.why = undefined; }\n this.ask(kept);\n }\n\n /** The group is gone from this device: its files go too. */\n async drop(groupId: string): Promise<void> {\n for (const kept of [...this.kept.values()].filter(k => k.groupId === groupId)) {\n this.kept.delete(kept.id);\n this.byMessage.delete(`${groupId}\\n${kept.message}`);\n this.wants.delete(kept.id);\n this.deps.transfers.delete(kept.id);\n await this.deps.store.remove(kept.id).catch(() => {});\n }\n for (const [linkId, edge] of this.edgesById) if (edge.groupId === groupId) { edge.files.detach(); this.edgesById.delete(linkId); }\n for (const key of [...this.ahead.keys()]) if (key.startsWith(`${groupId}\\n`)) this.ahead.delete(key);\n this.communityHave.delete(groupId);\n this.deps.changed();\n }\n\n // -- edges ---------------------------------------------------------------------------------------------------\n\n /** An edge's files/3 session opened (both sides agreed it) or closed. */\n session(groupId: string, peer: string, linkId: string, open: boolean): void {\n const edge = this.edge(groupId, peer, linkId);\n if (open) edge.files.attach(); else edge.files.detach();\n if (open) this.retry(groupId);\n }\n\n /** An edge came up and both sides said where they are: what this device holds goes to it, and wants waiting for a holder go. */\n edgeReady(groupId: string, peer: string, linkId: string): void {\n if (!this.deps.ready(linkId)) return;\n const edge = this.edge(groupId, peer, linkId);\n if (this.deps.settings().serveFiles) {\n const held = [...this.kept.values()].filter(k => k.groupId === groupId && k.held).map(k => k.message).slice(-GROUP_FILE_LIMITS.haveIds);\n if (held.length) this.sendHave(edge, held);\n }\n this.retry(groupId);\n }\n\n /** A `group-want`, `group-want-no` or `group-have` on an edge, from the member it is pinned to. */\n async frame(groupId: string, peer: string, linkId: string, frame: Record<string, unknown>): Promise<void> {\n if (frame.t === GROUP_WANT_FRAME) {\n const want = readGroupWant(frame, groupId);\n if (want) await this.serve(this.edge(groupId, peer, linkId), want.id);\n } else if (frame.t === GROUP_WANT_NO_FRAME) {\n const no = readGroupWantNo(frame, groupId);\n if (no) this.refused(groupId, peer, linkId, no.id, no.why);\n } else if (frame.t === GROUP_HAVE_FRAME) {\n const ids = readGroupHave(frame, groupId);\n if (ids) this.heard(groupId, peer, ids);\n }\n }\n\n /** A community's `group-have`, an application frame its signer sent the group. */\n communityApp(groupId: string, sender: string, frame: Record<string, unknown>): void {\n const ids = readGroupHave(frame);\n if (ids) this.heard(groupId, sender, ids);\n }\n\n /**\n * A files/3 frame on an edge. An offer is taken only for a file this device asked that member for (`gm` naming the\n * message): nobody can push a file into another's storage. What it brings belongs to the group's message, not to the\n * edge as a chat.\n */\n filesFrame(groupId: string, peer: string, linkId: string, frame: Record<string, unknown>): Promise<void> {\n const edge = this.edge(groupId, peer, linkId);\n if (frame.t === \"pf-offer\" && typeof frame.id === \"string\" && /^[A-Za-z0-9_-]{8,64}$/.test(frame.id) && !edge.taking.has(frame.id)) {\n const kept = isGroupMessageId(frame.gm) ? this.kept.get(this.byMessage.get(`${groupId}\\n${frame.gm}`) ?? \"\") : undefined;\n const want = kept && this.wants.get(kept.id);\n if (!kept || kept.held || !want || want.linkId !== linkId || want.wire) {\n this.deps.sendFiles(linkId, { t: \"pf-refuse\", id: frame.id, why: \"invalid\" });\n return Promise.resolve();\n }\n edge.taking.set(frame.id, kept.id);\n want.wire = frame.id;\n want.why = undefined;\n want.movedAt = this.deps.now();\n }\n return edge.files.handle(frame);\n }\n\n /** Wants that waited too long for an offer, or for bytes, go to the next holder; `group-have`s gathered go. */\n tick(): void {\n const now = this.deps.now();\n for (const [id, want] of this.wants) {\n const kept = this.kept.get(id);\n if (!kept) { this.wants.delete(id); continue; }\n if (now - (want.wire ? want.movedAt : want.at) < GROUP_FILE_LIMITS.wantWaitMs) continue;\n if (want.wire) {\n // A holder that stalls is left: the transfer ends here, and the next holder is asked (`transferChanged`).\n const edge = want.linkId ? this.edgesById.get(want.linkId) : undefined;\n if (edge?.files.get(\"in\", want.wire) && !transferEnded(edge.files.get(\"in\", want.wire)!)) { edge.files.cancel(\"in\", want.wire); continue; }\n want.wire = undefined;\n }\n if (want.holder) want.tried.add(want.holder);\n // Everyone asked in turn: a new round, as holders come and go.\n else { want.tried.clear(); want.why = undefined; }\n this.ask(kept);\n }\n this.letGo();\n for (const edge of this.edgesById.values()) {\n if (!edge.have.size || now - edge.haveAt < GROUP_FILE_LIMITS.haveEveryMs || !this.deps.ready(edge.linkId)) continue;\n this.sendHave(edge, [...edge.have]);\n }\n for (const [groupId, have] of this.communityHave) {\n if (!have.ids.size || now - have.at < GROUP_FILE_LIMITS.communityHaveEveryMs) continue;\n const ids = [...have.ids].slice(-GROUP_FILE_LIMITS.haveIds);\n have.ids.clear();\n have.at = now;\n void this.deps.sendApp(groupId, { t: GROUP_HAVE_FRAME, ids }).catch(() => {});\n }\n }\n\n // -- asking --------------------------------------------------------------------------------------------------\n\n private newWant(): Want { return { at: this.deps.now(), tried: new Set(), moved: 0, movedAt: 0 }; }\n\n /** Wants of this group that wait for a holder: asked now (an edge came up, or a member said it holds something). */\n private retry(groupId: string): void {\n for (const [id, want] of this.wants) {\n const kept = this.kept.get(id);\n if (kept?.groupId === groupId && !want.holder) this.ask(kept);\n }\n }\n\n /**\n * Asks the next holder for a file: the author first while its edge is up, then the holders whose edge is up. Only over\n * an edge this device has. (A private group past 16 members, or a community, reaches a holder it has no edge with\n * over a file session of the two members, WISP 503 § The file session; that session is not built yet, so such a\n * holder is not asked, and the file waits for one that is reachable.)\n */\n private ask(kept: Kept): void {\n const want = this.wants.get(kept.id);\n if (!want || kept.held || want.wire) return;\n const membership = this.deps.membership(kept.groupId);\n const edges = this.deps.edges(kept.groupId);\n const known = [kept.author, ...kept.holders.filter(key => key !== kept.author)]\n .filter(key => membership && key !== membership.me && membership.inRoster(key));\n // Every holder known served it damaged: asking again would fetch the same bytes, all of them, round after round.\n if (known.length && known.every(key => kept.damaged.has(key))) { this.stopDamaged(kept); return; }\n const order = known.filter(key => !want.tried.has(key) && !kept.damaged.has(key));\n let behind = false;\n for (const key of order) {\n const linkId = edges.get(key);\n if (!linkId || !this.deps.ready(linkId) || !this.edgesById.get(linkId)?.files.live) continue;\n // A holder serves this device one file at a time (WISP 503 § Serving limits): asked now, it would answer `busy`.\n // This one waits its turn, and is asked when the file before it ends (`transferChanged`).\n if (this.askedOf(linkId, want)) { behind = true; continue; }\n if (!this.deps.send(linkId, { t: GROUP_WANT_FRAME, g: kept.groupId, id: kept.message })) { want.tried.add(key); continue; }\n Object.assign(want, { holder: key, linkId, at: this.deps.now() });\n this.show(kept, { state: \"transferring\", stage: \"queued\", transferred: kept.stored ?? 0, size: kept.meta.size, direction: \"in\" });\n return;\n }\n Object.assign(want, { holder: undefined, linkId: undefined, at: this.deps.now() });\n if (behind) { this.show(kept, { state: \"transferring\", stage: \"queued\", transferred: kept.stored ?? 0, size: kept.meta.size, direction: \"in\" }); return; }\n // Why it waits: a holder busy, bytes that came damaged (its person can ask again), or nobody in reach has it.\n const wait = want.why ?? \"nobody\";\n this.show(kept, { state: \"transferring\", stage: \"waiting\", transferred: kept.stored ?? 0, size: kept.meta.size, direction: \"in\",\n wait, note: WAITING_NOTES[wait], ...(wait === \"damaged\" && { stalled: true }) });\n }\n\n /** Another file is asked of, or coming from, the member at the other end of this edge. */\n private askedOf(linkId: string, except: Want): boolean {\n for (const want of this.wants.values()) if (want !== except && want.linkId === linkId) return true;\n return false;\n }\n\n /** No holder serves the author's bytes: the want stops, and stays stopped after a restart, until Download. */\n private stopDamaged(kept: Kept): void {\n this.wants.delete(kept.id);\n const { size } = kept.meta;\n void this.deps.store.patch(kept.id, { transfer: { state: \"failed\", transferred: 0, size, error: DAMAGED } }).catch(() => {});\n this.show(kept, { state: \"failed\", transferred: 0, size, direction: \"in\", retry: true, error: DAMAGED });\n }\n\n /** A holder's no: the next one is asked. One that deleted the file is no holder any more. */\n private refused(groupId: string, peer: string, linkId: string, messageId: string, why: GroupWantRefusal): void {\n const kept = this.kept.get(this.byMessage.get(`${groupId}\\n${messageId}`) ?? \"\");\n const want = kept && this.wants.get(kept.id);\n if (!kept || !want || want.linkId !== linkId || want.wire) return;\n if (why === \"gone\") kept.holders = kept.holders.filter(key => key !== peer);\n if (why === \"busy\") want.why = \"busy\";\n want.tried.add(peer);\n want.holder = want.linkId = undefined;\n this.ask(kept);\n // The edge is free for a file waiting its turn there.\n this.retry(groupId);\n }\n\n /** A member says it holds these files: remembered as a holder of each (newest first), and asked when a want waits. */\n private heard(groupId: string, from: string, ids: string[]): void {\n const membership = this.deps.membership(groupId);\n if (!membership || from === membership.me || !membership.inRoster(from)) return;\n const newest = (holders: string[]) => [from, ...holders.filter(key => key !== from)].slice(0, GROUP_FILE_LIMITS.holders);\n for (const message of ids) {\n const key = `${groupId}\\n${message}`, kept = this.kept.get(this.byMessage.get(key) ?? \"\");\n if (!kept) {\n // Its message has not come yet (it is on its way in a catch-up): who holds it is kept for when it does.\n this.ahead.set(key, newest(this.ahead.get(key) ?? []));\n if (this.ahead.size > GROUP_FILE_LIMITS.heardAhead) this.ahead.delete(this.ahead.keys().next().value!);\n continue;\n }\n if (kept.mine) continue;\n kept.holders = newest(kept.holders);\n const want = this.wants.get(kept.id);\n if (!want) continue;\n want.tried.delete(from);\n if (!want.holder) this.ask(kept);\n }\n }\n\n // -- serving -------------------------------------------------------------------------------------------------\n\n /** A member asks for a file: offered over files/3 on its edge, or a `group-want-no` saying why not. */\n private async serve(edge: Edge, messageId: string): Promise<void> {\n const kept = this.kept.get(this.byMessage.get(`${edge.groupId}\\n${messageId}`) ?? \"\");\n const here = !!kept?.held && !!(await this.deps.store.get(kept.id).catch(() => undefined));\n // Decided and offered in one step: two wants at once never both find the last free place.\n const why = this.mayServe(edge, messageId, here ? kept : undefined);\n if (why || !kept) { this.deps.send(edge.linkId, { t: GROUP_WANT_NO_FRAME, g: edge.groupId, id: messageId, why: why ?? \"gone\" }); return; }\n const wire = toBase64Url(randomBytes(12));\n edge.serving.set(wire, kept.id);\n edge.servedMoved.set(wire, { confirmed: 0, at: this.deps.now() });\n const { name, mime, size, voice, video, image } = kept.meta;\n const file: FileInfo = { id: wire, name, mime, size, timestamp: this.deps.now(), ...(voice && { voice }), ...(video && { video }), ...(image && { image }) };\n edge.files.offer(file, kept.meta.d);\n }\n\n /**\n * Why this device does not serve a file now, or null when it does (WISP 503 § Asking, Serving limits): `refused` to\n * someone who could not read the announcement, `gone` when it is not here (`kept` undefined), `busy` when serving is\n * off or full.\n */\n private mayServe(edge: Edge, messageId: string, kept: Kept | undefined): GroupWantRefusal | null {\n const membership = this.deps.membership(edge.groupId);\n if (!membership || !membership.couldRead(edge.peer, messageId)) return \"refused\";\n if (!kept) return \"gone\";\n if (!this.deps.settings().serveFiles || !edge.files.live) return \"busy\";\n this.letGo();\n const active = this.servingNow();\n if (active.length >= GROUP_FILE_LIMITS.serveAtOnce) { edge.busy.add(messageId); return \"busy\"; }\n // One that has all its bytes waits only for the member's word that its check passed, which may come after this want.\n if (active.some(a => a.peer === edge.peer && a.sending)) return \"busy\";\n const today = Math.floor(this.deps.now() / 86_400_000);\n if (this.served.day !== today) this.served = { day: today, bytes: 0 };\n const reserved = active.reduce((sum, a) => sum + a.size, 0);\n return this.served.bytes + reserved + kept.meta.size > GROUP_FILE_LIMITS.serveBytesPerDay ? \"busy\" : null;\n }\n\n /**\n * Transfers served now, all groups together: to whom, what each takes of the day (its size, or what it sent when that\n * is more), and whether bytes are still to go.\n */\n private servingNow(): { peer: string; size: number; sending: boolean }[] {\n const active: { peer: string; size: number; sending: boolean }[] = [];\n for (const edge of this.edgesById.values()) for (const wire of edge.serving.keys()) {\n const record = edge.files.get(\"out\", wire);\n if (record && !transferEnded(record)) {\n active.push({ peer: edge.peer, size: Math.max(record.file.size, edge.files.sent(wire)), sending: record.confirmed < record.file.size });\n }\n }\n return active;\n }\n\n /**\n * An asker has a served transfer send parts again (WISP 503 § Serving limits): every byte sent counts toward the day,\n * so it goes on only while the day has room for it.\n */\n private mayResend(): boolean {\n const today = Math.floor(this.deps.now() / 86_400_000);\n if (this.served.day !== today) this.served = { day: today, bytes: 0 };\n return this.served.bytes + this.servingNow().reduce((sum, a) => sum + a.size, 0) < GROUP_FILE_LIMITS.serveBytesPerDay;\n }\n\n /**\n * Served transfers that no longer count end, their places free for others (WISP 503 § Serving limits): the asker's\n * session closed, it is not in the roster any more, or its bytes have not moved for `wantWaitMs` (the asker has moved\n * on by then).\n */\n private letGo(): void {\n const now = this.deps.now();\n for (const edge of this.edgesById.values()) for (const wire of [...edge.serving.keys()]) {\n const record = edge.files.get(\"out\", wire);\n if (!record || transferEnded(record)) continue;\n let moved = edge.servedMoved.get(wire);\n if (!moved || moved.confirmed !== record.confirmed) edge.servedMoved.set(wire, (moved = { confirmed: record.confirmed, at: now }));\n const gone = !edge.files.live || !this.deps.membership(edge.groupId)?.inRoster(edge.peer);\n if (gone || now - moved.at >= GROUP_FILE_LIMITS.wantWaitMs) edge.files.cancel(\"out\", wire);\n }\n }\n\n /** Says on an edge which files this device holds. */\n private sendHave(edge: Edge, ids: string[]): void {\n for (let i = 0; i < ids.length; i += GROUP_FILE_LIMITS.haveIds) {\n if (!this.deps.send(edge.linkId, { t: GROUP_HAVE_FRAME, g: edge.groupId, ids: ids.slice(i, i + GROUP_FILE_LIMITS.haveIds) })) return;\n }\n edge.have.clear();\n edge.haveAt = this.deps.now();\n }\n\n /** This device holds a new file: the group hears it with the next `group-have` (batched, `haveEveryMs` at most on an edge). */\n private queueHave(kept: Kept): void {\n if (!this.deps.settings().serveFiles) return;\n if (this.deps.membership(kept.groupId)?.community) {\n let have = this.communityHave.get(kept.groupId);\n if (!have) this.communityHave.set(kept.groupId, (have = { ids: new Set(), at: 0 }));\n have.ids.add(kept.message);\n return;\n }\n for (const [key, linkId] of this.deps.edges(kept.groupId)) this.edge(kept.groupId, key, linkId).have.add(kept.message);\n }\n\n // -- files/3 on an edge --------------------------------------------------------------------------------------\n\n private edge(groupId: string, peer: string, linkId: string): Edge {\n const known = this.edgesById.get(linkId);\n if (known) return known;\n const edge: Edge = { linkId, groupId, peer, files: undefined as unknown as ChatFiles, serving: new Map(), servedMoved: new Map(), taking: new Map(), ended: [], have: new Set(), haveAt: 0, busy: new Set() };\n edge.files = new ChatFiles({\n // An offer names the group message it serves (`gm`), which a 1:1 chat never carries.\n send: frame => {\n const kept = frame.t === \"pf-offer\" ? this.kept.get(edge.serving.get(frame.id as string) ?? \"\") : undefined;\n return this.deps.sendFiles(linkId, kept ? { ...frame, gm: kept.message } : frame);\n },\n decide: file => this.decide(edge, file),\n openTarget: record => this.openTarget(edge, record),\n openSource: record => this.openSource(edge, record),\n changed: (record, transferred) => this.transferChanged(edge, record, transferred),\n room: () => this.deps.store.room(),\n writable: () => this.deps.writable?.(linkId),\n resend: record => !edge.serving.has(record.id) || this.mayResend(),\n });\n this.edgesById.set(linkId, edge);\n return edge;\n }\n\n /** An offer that answers my want (`filesFrame` checked that): taken, unless it is not the file announced or there is no room. */\n private async decide(edge: Edge, file: FileInfo): Promise<OfferDecision> {\n const kept = this.kept.get(edge.taking.get(file.id) ?? \"\"), want = kept && this.wants.get(kept.id);\n if (!kept || kept.held || want?.linkId !== edge.linkId || want.wire !== file.id || file.size !== kept.meta.size) return { refuse: \"invalid\" };\n const room = await this.deps.store.room().catch(() => null);\n return room !== null && file.size > room ? { refuse: \"no-room\", room } : \"accept\";\n }\n\n private async openTarget(edge: Edge, record: FileTransferRecord): Promise<IncomingTarget> {\n const kept = this.kept.get(edge.taking.get(record.id) ?? \"\");\n if (!kept) throw new Error(\"No such file\");\n // What an earlier holder's transfer stored stays, and this one goes on from there (WISP 503: the asker accepts from\n // the offset it stored): every holder serves the author's bytes, and the whole file is checked at the end.\n await kept.idle;\n const writer = await this.deps.store.write(kept.id, !kept.fresh);\n kept.fresh = false;\n const resumed = writer.offset > 0;\n let damaged = false;\n return {\n offset: writer.offset,\n append: bytes => writer.append(bytes),\n flush: () => writer.flush(),\n // Against the author's digest, read back from storage, not the holder's word: a holder can withhold a file, never\n // serve another one.\n verify: async () => {\n damaged = (await writer.digest()) !== kept.meta.d;\n const want = this.wants.get(kept.id);\n if (damaged && want) want.why = \"damaged\";\n return !damaged;\n },\n // Only damaged bytes go, and those of a file its person cancelled. Left with a holder that stalled or stopped, what\n // came is kept for the next holder.\n discard: () => {\n // Whole from this holder and damaged: its bytes are not the author's, and it is not asked for them again.\n if (damaged) { kept.stored = 0; if (resumed) kept.fresh = kept.unclear = true; else kept.damaged.add(edge.peer); }\n return (kept.idle = (damaged || kept.dropped === record.id ? writer.discard() : writer.close()).catch(() => {}));\n },\n };\n }\n\n private async openSource(edge: Edge, record: FileTransferRecord): Promise<OutgoingSource> {\n const kept = this.kept.get(edge.serving.get(record.id) ?? \"\");\n const stored = kept && await this.deps.store.get(kept.id);\n if (!kept || !stored) throw new Error(\"The file is gone\");\n return { read: (offset, length) => this.deps.store.read(stored, offset, length), digest: async () => kept.meta.d };\n }\n\n /** A transfer on the edge ended: its record stays among the last `ENDED_KEPT`, and the oldest past that is forgotten. */\n private transferOver(edge: Edge, record: FileTransferRecord): void {\n if (edge.ended.some(([direction, id]) => direction === record.direction && id === record.id)) return;\n edge.ended.push([record.direction, record.id]);\n while (edge.ended.length > ENDED_KEPT) edge.files.forget(...edge.ended.shift()!);\n }\n\n private transferChanged(edge: Edge, record: FileTransferRecord, transferred: number): void {\n if (transferEnded(record)) this.transferOver(edge, record);\n if (record.direction === \"out\") {\n if (transferEnded(record) && edge.serving.has(record.id)) {\n // What was sent to a member counts toward the day's serving, done or not, and whatever it confirmed.\n const today = Math.floor(this.deps.now() / 86_400_000);\n if (this.served.day !== today) this.served = { day: today, bytes: 0 };\n this.served.bytes += edge.files.sent(record.id);\n edge.serving.delete(record.id);\n edge.servedMoved.delete(record.id);\n // A slot is free: those turned away as busy hear this device holds their file (with the next `group-have`), and\n // ask it again rather than wait out their round.\n const serving = this.deps.settings().serveFiles;\n for (const other of this.edgesById.values()) {\n if (serving) for (const id of other.busy) other.have.add(id);\n other.busy.clear();\n }\n }\n return;\n }\n const kept = this.kept.get(edge.taking.get(record.id) ?? \"\"), want = kept && this.wants.get(kept.id);\n // A transfer this device moved on from (the holder stalled, then went on) changes nothing.\n if (!kept || !want || want.wire !== record.id || want.linkId !== edge.linkId) return;\n if (transferred !== want.moved) { want.moved = transferred; want.movedAt = this.deps.now(); }\n if (record.state !== \"failed\" && record.state !== \"declined\" && record.state !== \"cancelled\") kept.stored = transferred;\n const { size } = kept.meta;\n switch (record.state) {\n case \"done\": {\n kept.held = true;\n this.wants.delete(kept.id);\n edge.taking.delete(record.id);\n void this.deps.store.patch(kept.id, { transfer: { state: \"done\", transferred: size, size } }).catch(() => {});\n this.show(kept, { state: \"done\", transferred: size, size, direction: \"in\" });\n this.queueHave(kept);\n // The holder is free for the next file this device waits for.\n this.retry(kept.groupId);\n return;\n }\n case \"failed\": case \"declined\": case \"cancelled\":\n // Damaged (refused, its bytes deleted), stopped by the holder or left here: the next holder is asked.\n edge.taking.delete(record.id);\n // A resumed file that failed its check may owe its bad bytes to the earlier holder: this one is asked again, from 0.\n if (want.holder && !kept.unclear) want.tried.add(want.holder);\n kept.unclear = false;\n Object.assign(want, { wire: undefined, holder: undefined, linkId: undefined, moved: 0 });\n this.ask(kept);\n this.retry(kept.groupId);\n return;\n case \"verifying\":\n this.show(kept, { state: \"transferring\", stage: \"verifying\", transferred, size, direction: \"in\" });\n return;\n default:\n this.show(kept, { state: \"transferring\", transferred, size, direction: \"in\" });\n }\n }\n\n // -- bookkeeping ---------------------------------------------------------------------------------------------\n\n private add(kept: Kept): void {\n this.kept.set(kept.id, kept);\n this.byMessage.set(`${kept.groupId}\\n${kept.message}`, kept.id);\n }\n\n /** Bytes this device fetched by itself from the group and keeps (or is fetching): what `autoBytesPerGroup` bounds. */\n private keptAutomatically(groupId: string): number {\n let sum = 0;\n for (const kept of this.kept.values()) if (kept.groupId === groupId && kept.auto && !kept.mine) sum += kept.meta.size;\n return sum;\n }\n\n private groupField(kept: Kept): NonNullable<StoredFile[\"group\"]> {\n return { message: kept.message, author: kept.author, ...(kept.auto && { auto: true }), ...(kept.asked && { asked: true }) };\n }\n\n private pace(windows: Map<string, RateWindow>, key: string, limit: number, ms: number): RateWindow {\n let window = windows.get(key);\n if (!window) windows.set(key, (window = new RateWindow(limit, ms, () => this.deps.now())));\n return window;\n }\n\n /** My announcements in a group, in a minute and in the window the members count them over. */\n private sentPaces(groupId: string): RateWindow[] {\n return [this.pace(this.sentPace, groupId, GROUP_FILE_LIMITS.announcePerMinute, 60_000),\n this.pace(this.sentTaken, groupId, GROUP_FILE_LIMITS.announceTaken, GROUP_FILE_LIMITS.announceWindowMs)];\n }\n\n private show(kept: Kept, view: FileTransferView): void {\n this.deps.transfers.set(kept.id, view);\n this.deps.changed();\n }\n}\n\nfunction messageFile(kept: Kept): MessageFile {\n const { name, size, mime, voice, video, image } = kept.meta;\n return { id: kept.id, name, size, mime, ...(voice && { voice }), ...(video && { video }), ...(image && { image }) };\n}\n","import { TYPING_REFRESH_MS, type TypingKind } from \"@ghostly/core\";\nimport { node, type ApiContext } from \"./apiKit\";\n\n/**\n * `typing --for` and `group typing --for`: a `start` holds 6 s on the other side (WISP 401 § Typing, WISP 902 · Group\n * Mesh § Typing), so the daemon says it again until the time is up, a message goes to that chat or group, or a\n * `--stop`. The engine sends a `start` at most once per refresh, counted from when the last one went: asking every half\n * refresh missed the tick at 3 s by a millisecond about half the time, so a start went every 4.5 s, 1.5 s inside the\n * contact's 6 s, and a busy machine flickered the contact's indicator off and on. Asking every half second sends one\n * at most 3.5 s after the last, and soon again when the send limit held one back.\n */\nconst KEEP_EVERY_MS = TYPING_REFRESH_MS / 6;\nconst keepers = new WeakMap<ApiContext, Map<string, () => void>>();\n\n/** Where typing is said: a 1:1 chat, or a private group. */\nexport type TypingTarget = { linkId: string } | { groupId: string };\ntype Word = { kind?: TypingKind; status?: string };\n\nfunction keepersOf(ctx: ApiContext): Map<string, () => void> {\n let map = keepers.get(ctx);\n if (!map) keepers.set(ctx, (map = new Map()));\n return map;\n}\n\nconst keyOf = (target: TypingTarget) => \"groupId\" in target ? `group:${target.groupId}` : target.linkId;\n\n/** Says it once: this side is typing (with its word), or stopped. */\nexport function sayTyping(ctx: ApiContext, target: TypingTarget, typing: boolean, word: Word = {}): void {\n const said = typing ? word : {};\n if (\"groupId\" in target) node(ctx).setGroupTyping({ groupId: target.groupId, typing, ...said });\n else node(ctx).setTyping({ linkId: target.linkId, typing, ...said });\n}\n\n/**\n * Says this side is typing in the chat or group for `ms`, with the kind and status it gave (`--kind`, `--status`) each\n * time; resolves when that ends, whichever way.\n */\nexport function keepTyping(ctx: ApiContext, target: TypingTarget, ms: number, word: Word = {}): Promise<void> {\n endTyping(ctx, target, false);\n const map = keepersOf(ctx), key = keyOf(target);\n return new Promise<void>((resolve) => {\n const end = () => { clearInterval(again); clearTimeout(until); off(); if (map.get(key) === end) map.delete(key); resolve(); };\n const again = setInterval(() => sayTyping(ctx, target, true, word), KEEP_EVERY_MS);\n const until = setTimeout(() => { end(); sayTyping(ctx, target, false); }, ms);\n // A message ends it, sent from anywhere (the engine said `stop` with it).\n const off = ctx.hub.onEvent((event) => {\n if (\"groupId\" in target ? event.type === \"group.sent\" && event.group === target.groupId : event.type === \"message.sent\" && event.chat === target.linkId) end();\n });\n map.set(key, end);\n });\n}\n\n/** Ends a kept `--for` in the chat or group; `tell`: and says this side stopped. Whether one was kept. */\nexport function endTyping(ctx: ApiContext, target: TypingTarget, tell: boolean): boolean {\n const end = keepersOf(ctx).get(keyOf(target));\n if (!end) return false;\n end();\n if (tell) sayTyping(ctx, target, false);\n return true;\n}\n","import { spawn } from \"node:child_process\";\nimport { readFile } from \"node:fs/promises\";\nimport { voicePeaksOf } from \"@ghostly/core\";\n\n/**\n * The waveform and length of a voice note sent from a file (`file send --voice`), measured as the apps measure their\n * own recordings (packages/core voice.ts `voicePeaksOf`, the recorder's meter) so the bars look the same. The sound is\n * decoded here, without native code: WAV by hand, Opus in WebM or Ogg and MP3 with the wasm decoders, anything\n * else (AAC in MP4, FLAC…) with `ffmpeg` when it is on the PATH.\n */\n\nexport interface Sound {\n /** One channel, -1 to 1: the channels averaged, as the recorder's analyser hears them. */\n mono: Float32Array;\n sampleRate: number;\n}\n\nexport interface VoiceMeasure { duration: number; peaks: number[] }\n\n/** Longer than any voice note (15 minutes) with room: a file past it is not decoded into memory. */\nconst MAX_BYTES = 256 * 1024 * 1024;\nconst FFMPEG_TIMEOUT_MS = 120_000;\n\n/** The waveform and length of the sound in `path`, or why they could not be read. */\nexport async function measureVoice(path: string): Promise<{ voice: VoiceMeasure } | { problem: string }> {\n let sound: Sound;\n try {\n sound = await decodeSound(path);\n } catch (error) {\n return { problem: error instanceof Error ? error.message : String(error) };\n }\n if (sound.mono.length === 0) return { problem: \"no sound in it\" };\n return { voice: { duration: Math.round((sound.mono.length / sound.sampleRate) * 1000), peaks: voicePeaksOf(sound.mono, sound.sampleRate) } };\n}\n\nexport async function decodeSound(path: string): Promise<Sound> {\n const bytes = await readFile(path);\n if (bytes.length > MAX_BYTES) throw new Error(\"too large to decode\");\n let own: Error | undefined;\n try {\n const sound = await decodeBytes(bytes);\n if (sound) return sound;\n } catch (error) {\n own = error instanceof Error ? error : new Error(String(error));\n }\n try {\n return await decodeWithFfmpeg(path);\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n throw new Error(own ? `${own.message}; ${reason}` : reason, { cause: error });\n }\n}\n\n/** The sound in a file this module decodes itself, or null for a kind it leaves to ffmpeg. */\nexport async function decodeBytes(bytes: Uint8Array): Promise<Sound | null> {\n if (ascii(bytes, 0, 4) === \"RIFF\" && ascii(bytes, 8, 4) === \"WAVE\") return decodeWav(bytes);\n if (bytes[0] === 0x1a && bytes[1] === 0x45 && bytes[2] === 0xdf && bytes[3] === 0xa3) {\n const opus = demuxWebm(bytes);\n return opus ? decodeOpus(opus) : null;\n }\n if (ascii(bytes, 0, 4) === \"OggS\") {\n const opus = demuxOgg(bytes);\n return opus ? decodeOpus(opus) : null;\n }\n if (ascii(bytes, 0, 3) === \"ID3\" || (bytes[0] === 0xff && (bytes[1]! & 0xe0) === 0xe0)) return decodeMp3(bytes);\n return null;\n}\n\nfunction ascii(bytes: Uint8Array, at: number, length: number): string {\n return String.fromCharCode(...bytes.subarray(at, at + length));\n}\n\nfunction mix(channels: Float32Array[], length: number): Float32Array {\n if (channels.length === 1) return channels[0]!.subarray(0, length);\n const mono = new Float32Array(length);\n for (const channel of channels) for (let i = 0; i < length; i++) mono[i]! += channel[i]! / channels.length;\n return mono;\n}\n\n// ---------- WAV ----------\n\n/** PCM (8, 16, 24 or 32 bit) or float (32 or 64 bit) WAV. A data chunk whose size is unknown (a pipe) runs to the end. */\nexport function decodeWav(bytes: Uint8Array): Sound {\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n let format = 0, channels = 0, sampleRate = 0, bits = 0;\n let at = 12;\n while (at + 8 <= bytes.length) {\n const id = ascii(bytes, at, 4);\n const size = view.getUint32(at + 4, true);\n const body = at + 8;\n if (id === \"fmt \") {\n format = view.getUint16(body, true);\n channels = view.getUint16(body + 2, true);\n sampleRate = view.getUint32(body + 4, true);\n bits = view.getUint16(body + 14, true);\n // WAVE_FORMAT_EXTENSIBLE: the real format is the sub-format GUID's first two bytes.\n if (format === 0xfffe && size >= 26) format = view.getUint16(body + 24, true);\n } else if (id === \"data\") {\n if (!channels || !sampleRate || !bits) throw new Error(\"a WAV without its format\");\n const end = size === 0 || size === 0xffffffff || body + size > bytes.length ? bytes.length : body + size;\n return { mono: wavSamples(view, body, end, format, channels, bits), sampleRate };\n }\n at = body + size + (size & 1);\n }\n throw new Error(\"a WAV without sound\");\n}\n\nfunction wavSamples(view: DataView, from: number, to: number, format: number, channels: number, bits: number): Float32Array {\n const width = bits / 8;\n const read: ((at: number) => number) | undefined =\n format === 1 && bits === 8 ? (at) => (view.getUint8(at) - 128) / 128\n : format === 1 && bits === 16 ? (at) => view.getInt16(at, true) / 32768\n : format === 1 && bits === 24 ? (at) => (((view.getInt8(at + 2) << 16) | (view.getUint8(at + 1) << 8) | view.getUint8(at)) / 8388608)\n : format === 1 && bits === 32 ? (at) => view.getInt32(at, true) / 2147483648\n : format === 3 && bits === 32 ? (at) => view.getFloat32(at, true)\n : format === 3 && bits === 64 ? (at) => view.getFloat64(at, true)\n : undefined;\n if (!read) throw new Error(`a WAV in a format not read here (${format}, ${bits} bit)`);\n const frames = Math.floor((to - from) / (width * channels));\n const mono = new Float32Array(frames);\n for (let frame = 0; frame < frames; frame++) {\n let sum = 0;\n for (let c = 0; c < channels; c++) sum += read(from + (frame * channels + c) * width);\n mono[frame] = sum / channels;\n }\n return mono;\n}\n\n// ---------- Opus (WebM, Ogg) ----------\n\nexport interface OpusStream {\n /** The OpusHead (RFC 7845): channels, pre-skip, channel mapping. */\n head: Uint8Array | null;\n channels: number;\n frames: Uint8Array[];\n /** Ogg's last granule position: where the sound really ends (the last frame is padded). */\n endGranule?: number;\n}\n\n/** A variable-length number in EBML: its length in bytes and value (the marker bit kept for ids, dropped for sizes). */\nfunction vint(bytes: Uint8Array, at: number, keepMarker: boolean): { length: number; value: number; unknown: boolean } | null {\n const first = bytes[at];\n if (first === undefined || first === 0) return null;\n const length = Math.clz32(first) - 23;\n if (at + length > bytes.length) return null;\n let value = keepMarker ? first : first & (0xff >> length);\n let allOnes = value === (0xff >> length);\n for (let i = 1; i < length; i++) {\n value = value * 256 + bytes[at + i]!;\n if (bytes[at + i] !== 0xff) allOnes = false;\n }\n return { length, value, unknown: !keepMarker && allOnes };\n}\n\n/** Elements whose children are read: EBML Segment, Tracks, TrackEntry, Audio, Cluster, BlockGroup. */\nconst WEBM_PARENTS = new Set([0x18538067, 0x1654ae6b, 0xae, 0xe1, 0x1f43b675, 0xa0]);\n\n/**\n * The Opus track of a WebM (Matroska) file. It reads the file in order, stepping into the elements that hold what it\n * needs rather than jumping by their sizes: MediaRecorder writes the Segment and every Cluster with an unknown size.\n */\nexport function demuxWebm(bytes: Uint8Array): OpusStream | null {\n interface Track { number: number; codec: string; head: Uint8Array | null; channels: number }\n const tracks: Track[] = [];\n const blocks: { track: number; data: Uint8Array }[] = [];\n let at = 0;\n while (at < bytes.length) {\n const id = vint(bytes, at, true);\n if (!id) break;\n const size = vint(bytes, at + id.length, false);\n if (!size) break;\n const body = at + id.length + size.length;\n if (WEBM_PARENTS.has(id.value)) {\n if (id.value === 0xae) tracks.push({ number: 0, codec: \"\", head: null, channels: 0 });\n at = body;\n continue;\n }\n if (size.unknown) break;\n const end = Math.min(bytes.length, body + size.value);\n const data = bytes.subarray(body, end);\n const track = tracks.at(-1);\n if (id.value === 0xd7 && track) track.number = uint(data);\n else if (id.value === 0x86 && track) track.codec = ascii(data, 0, data.length);\n else if (id.value === 0x63a2 && track) track.head = data;\n else if (id.value === 0x9f && track) track.channels = uint(data);\n else if (id.value === 0xa3 || id.value === 0xa1) {\n const number = vint(data, 0, false);\n if (number) for (const frame of laced(data, number.length)) blocks.push({ track: number.value, data: frame });\n }\n at = end;\n }\n const opus = tracks.find((t) => t.codec === \"A_OPUS\");\n if (!opus) return null;\n return { head: opus.head, channels: opus.channels || 1, frames: blocks.filter((b) => b.track === opus.number).map((b) => b.data) };\n}\n\nfunction uint(data: Uint8Array): number {\n let value = 0;\n for (const byte of data) value = value * 256 + byte;\n return value;\n}\n\n/** The frames in a (Simple)Block after its track number: one, or several laced (Xiph, fixed-size or EBML). */\nfunction laced(data: Uint8Array, at: number): Uint8Array[] {\n const flags = data[at + 2]!;\n let pos = at + 3;\n const lacing = (flags >> 1) & 3;\n if (lacing === 0) return [data.subarray(pos)];\n const count = data[pos++]! + 1;\n const sizes: number[] = [];\n if (lacing === 1) {\n for (let i = 0; i < count - 1; i++) {\n let size = 0;\n for (let byte = 255; byte === 255; size += byte) byte = data[pos++]!;\n sizes.push(size);\n }\n } else if (lacing === 3) {\n const first = vint(data, pos, false)!;\n pos += first.length;\n sizes.push(first.value);\n for (let i = 1; i < count - 1; i++) {\n const delta = vint(data, pos, false)!;\n pos += delta.length;\n // A signed difference from the size before: the value less half its range.\n sizes.push(sizes[i - 1]! + delta.value - (2 ** (7 * delta.length - 1) - 1));\n }\n } else {\n const each = Math.floor((data.length - pos) / count);\n for (let i = 0; i < count - 1; i++) sizes.push(each);\n }\n sizes.push(data.length - pos - sizes.reduce((a, b) => a + b, 0));\n const frames: Uint8Array[] = [];\n for (const size of sizes) { frames.push(data.subarray(pos, pos + size)); pos += size; }\n return frames;\n}\n\n/** The first Opus stream of an Ogg file: its packets put back together from the pages' segments. */\nexport function demuxOgg(bytes: Uint8Array): OpusStream | null {\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n let serial: number | undefined;\n let head: Uint8Array | null = null;\n const frames: Uint8Array[] = [];\n let packet: Uint8Array[] = [];\n let endGranule: number | undefined;\n let at = 0;\n while (at + 27 <= bytes.length && ascii(bytes, at, 4) === \"OggS\") {\n const segments = bytes[at + 26]!;\n const table = bytes.subarray(at + 27, at + 27 + segments);\n let pos = at + 27 + segments;\n const pageSerial = view.getUint32(at + 14, true);\n const granule = Number(view.getBigInt64(at + 6, true));\n const mine = serial === undefined ? ascii(bytes, pos, 8) === \"OpusHead\" : pageSerial === serial;\n if (mine) {\n serial = pageSerial;\n if (granule >= 0) endGranule = granule;\n for (const size of table) {\n packet.push(bytes.subarray(pos, pos + size));\n pos += size;\n if (size < 255) {\n const whole = packet.length === 1 ? packet[0]! : Buffer.concat(packet);\n packet = [];\n if (!head) head = whole;\n else if (ascii(whole, 0, 8) !== \"OpusTags\") frames.push(whole);\n }\n }\n } else pos += table.reduce((a, b) => a + b, 0);\n at = pos;\n }\n if (!head) return null;\n return { head, channels: head[9] ?? 1, frames, endGranule };\n}\n\nexport async function decodeOpus(stream: OpusStream): Promise<Sound> {\n const { OpusDecoder } = await import(\"opus-decoder\");\n const head = stream.head && ascii(stream.head, 0, 8) === \"OpusHead\" ? stream.head : null;\n const channels = head ? head[9]! : Math.max(1, stream.channels);\n const preSkip = head ? head[10]! | (head[11]! << 8) : 0;\n const family = head ? head[18]! : 0;\n const mapping = family !== 0 && head\n ? { streamCount: head[19]!, coupledStreamCount: head[20]!, channelMappingTable: [...head.subarray(21, 21 + channels)] }\n : { streamCount: 1, coupledStreamCount: channels - 1, channelMappingTable: channels === 1 ? [0] : [0, 1] };\n const decoder = new OpusDecoder({ channels, preSkip, ...mapping });\n await decoder.ready;\n try {\n const decoded = decoder.decodeFrames(stream.frames);\n let length = decoded.samplesDecoded;\n // Ogg says where the sound ends; the rest of the last frame is padding.\n if (stream.endGranule !== undefined && stream.endGranule > preSkip) length = Math.min(length, stream.endGranule - preSkip);\n return { mono: mix(decoded.channelData, length), sampleRate: decoded.sampleRate };\n } finally {\n decoder.free();\n }\n}\n\n// ---------- MP3 ----------\n\nexport async function decodeMp3(bytes: Uint8Array): Promise<Sound> {\n const { MPEGDecoder } = await import(\"mpg123-decoder\");\n const decoder = new MPEGDecoder();\n await decoder.ready;\n try {\n const decoded = decoder.decode(bytes);\n if (!decoded.samplesDecoded) throw new Error(\"no MP3 frames in it\");\n return { mono: mix(decoded.channelData, decoded.samplesDecoded), sampleRate: decoded.sampleRate };\n } finally {\n decoder.free();\n }\n}\n\n// ---------- ffmpeg ----------\n\n/** Anything ffmpeg reads, as a float WAV on its output (the channels kept, so they are averaged as above). */\nexport function decodeWithFfmpeg(path: string, command = process.env.GHOSTLY_FFMPEG || \"ffmpeg\"): Promise<Sound> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, [\"-v\", \"error\", \"-nostdin\", \"-i\", path, \"-vn\", \"-f\", \"wav\", \"-c:a\", \"pcm_f32le\", \"-\"], { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n const chunks: Buffer[] = [];\n let size = 0, errors = \"\";\n const timer = setTimeout(() => child.kill(\"SIGKILL\"), FFMPEG_TIMEOUT_MS);\n child.stdout.on(\"data\", (chunk: Buffer) => {\n size += chunk.length;\n if (size > MAX_BYTES * 2) child.kill(\"SIGKILL\");\n else chunks.push(chunk);\n });\n child.stderr.on(\"data\", (chunk: Buffer) => { if (errors.length < 2000) errors += chunk.toString(); });\n child.on(\"error\", (error: NodeJS.ErrnoException) => {\n clearTimeout(timer);\n reject(new Error(error.code === \"ENOENT\" ? \"no decoder here for this kind of audio (install ffmpeg to read it)\" : `ffmpeg: ${error.message}`));\n });\n child.on(\"close\", (code) => {\n clearTimeout(timer);\n if (code !== 0) { reject(new Error(`ffmpeg could not read it${errors.trim() ? `: ${errors.trim().split(\"\\n\")[0]}` : \"\"}`)); return; }\n try { resolve(decodeWav(Buffer.concat(chunks))); } catch (error) { reject(error); }\n });\n });\n}\n","import { openAsBlob } from \"node:fs\";\nimport { access, constants, open, rm, stat } from \"node:fs/promises\";\nimport { basename, extname, join, resolve } from \"node:path\";\nimport { GROUP_FILE_LIMITS, LIMITS, PLAYABLE_AUDIO, VOICE_LIMITS, baseMime, parseVoiceMeta, randomBytes, readImageMeta, sanitizeFileName, sanitizeMime, toBase64Url, type VoiceMeta } from \"@ghostly/core\";\nimport { FILE_BYTES_STEP, fileBytes, fileBytesOf } from \"@ghostly/browser/shared/fileBytes\";\nimport { fileStore } from \"@ghostly/browser/shared/idb\";\nimport { removeStored } from \"@ghostly/browser/shared/storedFiles\";\nimport { replyRef } from \"@ghostly/browser/shared/replies\";\nimport type { GroupView, MessageFile } from \"@ghostly/browser/shared/types\";\nimport { groupFileId, groupOfFile } from \"@ghostly/browser/engine/groupFiles\";\nimport { bool, chatOf, findChatOrGroup, list, node, num, oneOf, sentAt, state, str, waitForState, type ApiContext, type Method, type Params } from \"./apiKit\";\nimport { groupSendRefused } from \"./api\";\nimport { CliError } from \"./errors\";\nimport { endTyping } from \"./typing\";\nimport { measureVoice } from \"./voicePeaks\";\n\n/**\n * Files and voice notes in a chat (WISP 1100, phase 3), as the app's composer sends them (packages/browser\n * platform/services.ts `sendFile`): the bytes go into the profile's file store first, then the engine offers them.\n * Large files follow files/3: the receiving side's person (here, a command) accepts or declines each offer.\n *\n * In a group (WISP 503 · Group Files) the file is announced as a group message, and each member's app fetches the\n * bytes from whoever holds them: by itself up to the automatic limits, on `file accept` past them.\n */\n\nconst TYPES: Record<string, string> = {\n \".png\": \"image/png\", \".jpg\": \"image/jpeg\", \".jpeg\": \"image/jpeg\", \".gif\": \"image/gif\", \".webp\": \"image/webp\",\n \".pdf\": \"application/pdf\", \".txt\": \"text/plain\", \".md\": \"text/markdown\", \".json\": \"application/json\", \".csv\": \"text/csv\",\n \".zip\": \"application/zip\", \".mp3\": \"audio/mpeg\", \".m4a\": \"audio/mp4\", \".ogg\": \"audio/ogg\", \".opus\": \"audio/ogg\", \".wav\": \"audio/wav\",\n \".webm\": \"audio/webm\", \".mp4\": \"video/mp4\", \".mov\": \"video/quicktime\",\n};\n\nexport function mimeOf(path: string, given?: string): string {\n return sanitizeMime(given ?? TYPES[extname(path).toLowerCase()] ?? \"application/octet-stream\");\n}\n\n/** A chat's files as the timeline shows them, with where each transfer stands. */\nexport function filesOf(messages: { id: string; file?: MessageFile; sender: string; timestamp: number }[], transfers: Record<string, unknown>) {\n return messages.filter((m) => m.file).map((m) => ({\n messageId: m.id, from: m.sender, timestamp: m.timestamp,\n file: { id: m.file!.id, name: m.file!.name, size: m.file!.size, mime: m.file!.mime, ...(m.file!.voice ? { voice: m.file!.voice } : {}), ...(m.file!.image ? { image: m.file!.image } : {}) },\n transfer: transfers[m.file!.id] ?? null,\n }));\n}\n\n/** The chat or the group a file command names (`findChatOrGroup`). */\nconst chatOrGroupOf = (ctx: ApiContext, params: Params) => findChatOrGroup(state(ctx).links, state(ctx).groups, str(params, \"chat\", true));\n\n/**\n * The chat or group a file belongs to: a file id starts with its chat's id (a group's file with `group-<id>-`), so it\n * may be left out; given, it must be the file's own. `where` is what the answer names it by.\n */\nfunction fileChat(ctx: ApiContext, params: Params, fileId: string): { linkId: string; where: { chat: string } | { group: string } } {\n const groupId = groupOfFile(fileId, state(ctx).groups.map((g) => g.id));\n if (groupId) {\n const named = params.chat === undefined ? undefined : chatOrGroupOf(ctx, params);\n if (named && !(\"group\" in named && named.group.id === groupId)) throw new CliError(\"not_found\", `No file ${fileId} in this ${\"group\" in named ? \"group\" : \"chat\"}`);\n return { linkId: `group:${groupId}`, where: { group: groupId } };\n }\n const link = params.chat === undefined\n ? state(ctx).links.find((l) => fileId.startsWith(`${l.id}-in-`) || fileId.startsWith(`${l.id}-out-`))\n : chatOf(ctx, params);\n if (!link) throw new CliError(\"not_found\", `No file ${fileId}`);\n if (!fileId.startsWith(`${link.id}-`)) throw new CliError(\"not_found\", `No file ${fileId} in this chat`);\n return { linkId: link.id, where: { chat: link.id } };\n}\n\nconst VOICE_ONLY = \"A voice note is audio (webm, ogg, mp4, mpeg, aac, m4a or wav) of at most 15 minutes, with peaks from 0 to 255\";\n\n/** `--voice [ms] [--peaks …]`: the voice description, its length and waveform measured from the sound when not given. */\nasync function voiceOf(params: Params, path: string, mime: string): Promise<{ voice?: VoiceMeta; warning?: string }> {\n if (params.voice === undefined) return {};\n if (!PLAYABLE_AUDIO.test(baseMime(mime))) throw new CliError(\"bad_request\", VOICE_ONLY);\n // Without a length or a waveform, both are measured from the sound, as the apps measure their recordings.\n let duration = params.voice === true ? undefined : num(params, \"voice\", 0, { min: 1 });\n let peaks = list(params, \"peaks\").map(Number);\n let warning: string | undefined;\n if (duration === undefined || !peaks.length) {\n const measured = await measureVoice(path);\n if (\"voice\" in measured) {\n duration ??= measured.voice.duration;\n if (!peaks.length) peaks = measured.voice.peaks;\n } else if (duration === undefined) {\n throw new CliError(\"bad_request\", `Could not measure the voice note (${measured.problem}): give its length as --voice <ms>`);\n } else warning = `Sent without a waveform: could not read the sound (${measured.problem})`;\n }\n const voice = parseVoiceMeta({ duration, peaks: peaks.length ? peaks : new Array(VOICE_LIMITS.bars).fill(0) }, mime);\n if (!voice) throw new CliError(\"bad_request\", VOICE_ONLY);\n return { voice, ...(warning ? { warning } : {}) };\n}\n\n/**\n * A file announced in a group (WISP 503): its bytes staged under a group file id with their digest, as a chat's are, then\n * the announcement. This device is its first holder and serves the members who ask.\n */\nasync function sendToGroup(ctx: ApiContext, params: Params, group: GroupView, path: string, size: number) {\n if (size > GROUP_FILE_LIMITS.maxBytes) throw new CliError(\"refused\", `Too large for a group (at most ${GROUP_FILE_LIMITS.maxBytes} bytes)`);\n if (!size) throw new CliError(\"refused\", \"An empty file cannot go to a group\");\n const mime = mimeOf(path, str(params, \"mime\"));\n const replyTo = str(params, \"reply\");\n if (replyTo) {\n const messages = await node(ctx).groupMessages({ groupId: group.id });\n if (!messages.some((m) => m.id === replyTo || replyRef(m) === replyTo)) throw new CliError(\"not_found\", \"That message is not in this group, or cannot be replied to\");\n }\n // A send the group would refuse anyway (its files of the minute are taken, or this device is not in it) is refused\n // before a byte is copied in: a retry loop writes nothing. The announcement checks again.\n const check = await node(ctx).groupFileCheck({ groupId: group.id });\n if (check.error) throw check.refused ? new CliError(\"refused\", check.error) : groupSendRefused(check.error);\n const { voice, warning } = await voiceOf(params, path, mime);\n const source = await openAsBlob(path, { type: mime });\n const image = voice ? undefined : await readImageMeta(source, mime);\n const fileId = groupFileId(group.id, \"out\");\n const file: MessageFile = { id: fileId, name: sanitizeFileName(str(params, \"name\") ?? basename(path)), size, mime, ...(voice ? { voice } : {}), ...(image ? { image } : {}) };\n const timestamp = Date.now();\n const bytes = await fileBytes();\n const digest = await bytes.stage(fileId, source);\n await fileStore.put({\n id: fileId, linkId: `group:${group.id}`, bytes: bytes.kind, digest, createdAt: timestamp, direction: \"out\",\n metadata: { name: file.name, size, mime, timestamp, ...(voice ? { voice } : {}), ...(image ? { image } : {}) },\n transfer: { state: \"transferring\", transferred: 0, size },\n });\n endTyping(ctx, { groupId: group.id }, false);\n const result = await node(ctx).sendGroupFile({ groupId: group.id, file, ...(replyTo ? { replyTo } : {}) });\n if (result.error) {\n // Not announced: nothing is in the group, so the staged copy goes too.\n await removeStored(fileId).catch(() => {});\n throw result.refused ? new CliError(\"refused\", result.error) : groupSendRefused(result.error);\n }\n return { group: group.id, messageId: result.messageId ?? null, file: { id: fileId, name: file.name, size, mime, ...(voice ? { voice: true } : {}), ...(image ? { image } : {}) }, ...(warning ? { warning } : {}) };\n}\n\n/** Bytes a file system takes in one name: 255 on ext4, btrfs, APFS (NTFS counts 255 UTF-16 units, within this too). */\nconst NAME_BYTES = 255;\n\n/**\n * `name` as an entry of a folder: an app's name may have 200 characters, which in UTF-8 can be 800 bytes. The end of\n * its stem goes until it fits, its extension stays (as `sanitizeFileName` shortens it), so \"文…文.txt\" is saved as a text.\n */\nexport function fitName(name: string): string {\n const fits = (candidate: string) => Buffer.byteLength(candidate) <= NAME_BYTES;\n if (fits(name)) return name;\n const extension = /\\.[^.\\s]{1,15}$/u.exec(name)?.[0] ?? \"\";\n const stem = [...name.slice(0, name.length - extension.length)];\n while (stem.length && !fits(stem.join(\"\").trimEnd() + extension)) stem.pop();\n return (stem.join(\"\").trimEnd() || \"file\") + extension;\n}\n\n/**\n * The name `file save --dir` writes a file under. Its sender chose it, and the folder is one scripts and people run\n * tools in: a name that starts with a dash is an option to the tool a `*` hands it to (\"-rf\", \"--checkpoint-action=…\"),\n * so it gets a `_` first, as a Windows device name does in `sanitizeFileName`.\n */\nexport function saveName(name: string): string {\n const clean = sanitizeFileName(name);\n return fitName(clean.startsWith(\"-\") ? `_${clean}` : clean);\n}\n\nconst ACTIONS = [\"accept\", \"decline\", \"pause\", \"resume\", \"cancel\", \"resend\", \"request\"] as const;\n\nexport const FILE_METHODS: Record<string, Method> = {\n async \"file.send\"(ctx, params) {\n const target = chatOrGroupOf(ctx, params);\n const path = resolve(str(params, \"path\", true));\n let info;\n try { info = await stat(path); } catch { throw new CliError(\"not_found\", `No file ${path}`); }\n if (!info.isFile()) throw new CliError(\"bad_request\", `${path} is not a file`);\n // A file this user may not read: refused with its path (errors.ts), not the engine's \"The blob could not be read\" later.\n await access(path, constants.R_OK);\n if (\"group\" in target) return sendToGroup(ctx, params, target.group, path, info.size);\n const { link } = target;\n if (link.profile && link.dataLink === \"open\" && !link.capabilities?.files) throw new CliError(\"unavailable\", \"The contact's app takes no files\");\n if (!link.capabilities?.largeFiles && info.size > LIMITS.maxFileBytes) throw new CliError(\"refused\", `Too large for the contact's app (at most ${LIMITS.maxFileBytes} bytes)`);\n if (typeof link.peerFileRoom === \"number\" && info.size > link.peerFileRoom) throw new CliError(\"refused\", `Not enough space on the contact's device (${link.peerFileRoom} bytes free)`);\n const mime = mimeOf(path, str(params, \"mime\"));\n // A reply names a message of this chat, as `send --reply` does: checked before anything is stored.\n const replyTo = str(params, \"reply\");\n if (replyTo) {\n const messages = await node(ctx).getMessages(link.id);\n if (!messages.some((m) => m.id === replyTo || replyRef(m) === replyTo)) throw new CliError(\"not_found\", \"That message is not in this chat, or cannot be replied to\");\n }\n const { voice, warning } = await voiceOf(params, path, mime);\n const source = await openAsBlob(path, { type: mime });\n // A picture goes with its size, read from its first bytes as the apps read it (PNG, JPEG, GIF, WebP).\n const image = voice ? undefined : await readImageMeta(source, mime);\n const wireId = toBase64Url(randomBytes(12));\n const file: MessageFile = { id: `${link.id}-out-${wireId}`, name: sanitizeFileName(str(params, \"name\") ?? basename(path)), size: info.size, mime, ...(voice ? { voice } : {}), ...(image ? { image } : {}) };\n const timestamp = sentAt();\n const bytes = await fileBytes();\n const digest = await bytes.stage(file.id, source);\n await fileStore.put({\n id: file.id, linkId: link.id, bytes: bytes.kind, digest, createdAt: timestamp, direction: \"out\", wireId,\n metadata: { name: file.name, size: file.size, mime: file.mime, timestamp, ...(voice ? { voice } : {}), ...(image ? { image } : {}) },\n transfer: { state: \"transferring\", transferred: 0, size: file.size },\n });\n endTyping(ctx, { linkId: link.id }, true);\n try {\n await node(ctx).sendFile({ linkId: link.id, file, timestamp, ...(replyTo ? { replyTo } : {}) });\n } catch (error) {\n // Refused before it started (offline, a stopped chat, a contact whose app takes no files): nothing is in the chat,\n // so the staged copy goes too.\n await removeStored(file.id).catch(() => {});\n throw new CliError(\"refused\", error instanceof Error ? error.message : String(error));\n }\n return { chat: link.id, file: { id: file.id, name: file.name, size: file.size, mime: file.mime, ...(voice ? { voice: true } : {}), ...(image ? { image } : {}) }, ...(warning ? { warning } : {}) };\n },\n\n async \"file.list\"(ctx, params) {\n const target = chatOrGroupOf(ctx, params);\n if (\"group\" in target) return { group: target.group.id, files: filesOf(await node(ctx).groupMessages({ groupId: target.group.id }), state(ctx).transfers) };\n return { chat: target.link.id, files: filesOf(await node(ctx).getMessages(target.link.id), state(ctx).transfers) };\n },\n\n /**\n * files/3: answer an offer, or pause, resume or cancel a transfer, either way; `resend` a file sent from here that\n * stopped moving (or failed), `request` again one that stopped arriving. Without a chat, the file's id names it.\n * A group's file (WISP 503) takes `accept` (download one this device did not fetch by itself), `request`, and `cancel`,\n * `decline`, `pause` and `resume` for one that is coming.\n */\n async \"file.action\"(ctx, params) {\n const fileId = str(params, \"file\", true);\n const { linkId, where } = fileChat(ctx, params, fileId);\n const action = oneOf(params, \"action\", ACTIONS, \"accept\");\n if (\"group\" in where) {\n if (action === \"resend\") throw new CliError(\"bad_request\", \"A group's file is downloaded (file accept) or asked for again (file request)\");\n if (state(ctx).transfers[fileId]?.state === \"done\") throw new CliError(\"refused\", action === \"accept\" || action === \"request\" || action === \"resume\" ? \"Nothing to ask for: the file is all here\" : \"Nothing to stop: the file is all here\");\n try { await node(ctx).fileAction({ linkId, fileId, action }); } catch (error) { throw new CliError(\"refused\", error instanceof Error ? error.message : String(error)); }\n return { ...where, file: fileId, action, transfer: state(ctx).transfers[fileId] ?? null };\n }\n if (action === \"resend\" || action === \"request\") {\n const outgoing = fileId.startsWith(`${linkId}-out-`);\n if (outgoing !== (action === \"resend\")) throw new CliError(\"bad_request\", outgoing ? \"A file sent from here is sent again: file resend\" : \"A received file is asked for again: file request\");\n const transfer = state(ctx).transfers[fileId];\n if (!transfer || transfer.state === \"done\") throw new CliError(\"refused\", outgoing ? \"Nothing to send again: it arrived whole\" : \"Nothing to ask for: the file is all here\");\n if (transfer.state === \"failed\" && (!outgoing || !transfer.retry)) throw new CliError(\"refused\", `It ended: ${transfer.error ?? \"failed\"}`);\n }\n await node(ctx).fileAction({ linkId, fileId, action });\n return { ...where, file: fileId, action, transfer: state(ctx).transfers[fileId] ?? null };\n },\n\n /**\n * Until a transfer ends: done answers, failed is an `engine` error with the transfer's words (and whether `file\n * resend` can go on). A file whose transfer this run has not seen answers from the file store.\n */\n async \"file.wait\"(ctx, params) {\n const fileId = str(params, \"file\", true);\n const { where } = fileChat(ctx, params, fileId);\n const stored = await fileStore.get(fileId);\n if (!stored && !state(ctx).transfers[fileId]) throw new CliError(\"not_found\", `No file ${fileId}`);\n const ms = num(params, \"timeout\", 300, { min: 1, max: 86_400 }) * 1000;\n const transfer = await waitForState(ctx, (s) => {\n const now = s.transfers[fileId] ?? stored?.transfer;\n return now && (now.state === \"done\" || now.state === \"failed\") ? now : undefined;\n }, ms, `file ${fileId} to arrive`).catch((error: unknown) => {\n const now = state(ctx).transfers[fileId];\n if (error instanceof CliError && error.code === \"timeout\" && now) throw new CliError(\"timeout\", `${error.message} (${now.transferred} of ${now.size} bytes${now.stage ? `, ${now.stage}` : \"\"})`, { file: fileId, transferred: now.transferred, size: now.size, stage: now.stage ?? null });\n throw error;\n });\n if (transfer.state === \"failed\") throw new CliError(\"engine\", transfer.error ?? \"The transfer failed\", { file: fileId, ...where, retry: !!transfer.retry });\n return { ...where, file: fileId, state: \"done\", size: transfer.size };\n },\n\n /** Writes a received (or sent) file to disk: into `dir` under its own name (`saveName`), or to `path`. Never over a file unless `force`. */\n async \"file.save\"(ctx, params) {\n const fileId = str(params, \"file\", true);\n if (params.chat !== undefined) fileChat(ctx, params, fileId);\n if (bool(params, \"wait\")) await FILE_METHODS[\"file.wait\"](ctx, { file: fileId, timeout: params.timeout });\n const stored = await fileStore.get(fileId);\n if (!stored?.metadata) throw new CliError(\"not_found\", `No file ${fileId}`);\n if (stored.leftOut) throw new CliError(\"not_found\", \"Not in this backup: the light backup this profile was restored from left the file out\");\n const transfer = state(ctx).transfers[fileId] ?? stored.transfer;\n if (transfer && transfer.state !== \"done\") throw new CliError(\"unavailable\", `The file is not all here yet (${transfer.transferred} of ${transfer.size} bytes)`);\n const target = str(params, \"path\") ? resolve(str(params, \"path\", true)) : join(resolve(str(params, \"dir\") ?? \".\"), saveName(stored.metadata.name));\n const flags = bool(params, \"force\") ? \"w\" : \"wx\";\n let out;\n try { out = await open(target, flags, 0o600); } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"EEXIST\") throw new CliError(\"confirm\", `${target} exists: pass force (--force) to replace it`);\n throw error;\n }\n let written = 0, complete = false;\n try {\n const backend = stored.bytes ? await fileBytesOf(stored.bytes) : null;\n if (backend) {\n const size = await backend.size(fileId);\n if (size === null) throw new CliError(\"not_found\", \"The file's bytes are not on this device\");\n for (let offset = 0; offset < size; offset += FILE_BYTES_STEP) {\n const chunk = await backend.read(fileId, offset, FILE_BYTES_STEP);\n await out.write(chunk);\n written += chunk.length;\n }\n } else if (stored.blob) {\n const chunk = new Uint8Array(await stored.blob.arrayBuffer());\n await out.write(chunk);\n written = chunk.length;\n } else throw new CliError(\"not_found\", \"The file's bytes are not on this device\");\n complete = true;\n } finally {\n await out.close();\n if (!complete) await rm(target, { force: true });\n }\n return { file: fileId, path: target, size: written, name: stored.metadata.name, mime: stored.metadata.mime };\n },\n};\n","import type { StoredMessage } from \"@ghostly/browser/shared/types\";\nimport { node, type ApiContext } from \"./apiKit\";\nimport { CliError } from \"./errors\";\nimport type { GhostlyEvent } from \"./events\";\n\n/*\n * Waiting on what a command sent (WISP 1100): a message's delivery, an edit's confirmation, a group frame taken by an\n * edge. Shared by the chat, group and status card methods.\n */\n\nconst DELIVERY_RANK: Record<string, number> = { sending: 0, waiting: 1, queued: 1, held: 2, sent: 2, delivered: 3 };\n\n/** Waits until the contact confirmed the latest edit of one of my messages (it is no longer pending). */\nexport async function waitForEdit(ctx: ApiContext, chat: string, messageId: string, ms: number): Promise<StoredMessage> {\n const until = Date.now() + ms;\n for (;;) {\n const message = await node(ctx).getMessage(chat, messageId);\n if (!message) throw new CliError(\"not_found\", `No message ${messageId} in this chat`);\n if (!message.edit?.pending) return message;\n if (Date.now() >= until) throw new CliError(\"timeout\", `Timed out after ${Math.round(ms / 1000)} s: the contact has not confirmed the edit yet. It goes by itself once the chat is live and the contact's app shows edits, while this profile is online`, { messageId, edits: message.edit.seq });\n await new Promise((resolve) => setTimeout(resolve, 250));\n }\n}\n\n/**\n * Waits until an edge took my group message, or its edit number `edit` (WISP 902: a group has no receipts, so this is\n * as far as the author sees): a member's edge in a private group, an edge to one of my hubs in a community.\n */\nexport async function waitForGroupFrame(ctx: ApiContext, groupId: string, messageId: string, edit: number | undefined, ms: number): Promise<number> {\n const until = Date.now() + ms;\n for (;;) {\n const taken = node(ctx).groupTaken({ groupId, messageId, ...(edit ? { edit } : {}) });\n if (taken > 0) return taken;\n if (Date.now() >= until) throw new CliError(\"timeout\", `Timed out after ${Math.round(ms / 1000)} s: no member's edge took it yet. It stays in the group and goes when one opens, while this profile is online`, { messageId, ...(edit ? { edits: edit } : {}) });\n await new Promise((resolve) => setTimeout(resolve, 250));\n }\n}\n\n/** Waits until a message of mine reached `target` (`sent`: on its way to the contact; `delivered`: acknowledged). */\nexport async function waitForMessage(ctx: ApiContext, chat: string, messageId: string, target: \"sent\" | \"delivered\", ms: number): Promise<StoredMessage> {\n const want = DELIVERY_RANK[target];\n const look = (message: StoredMessage | undefined) => {\n if (!message) return undefined;\n const delivery = message.delivery ?? \"sent\";\n if (delivery === \"failed\") throw new CliError(\"engine\", message.deliveryError ?? \"The message could not be sent\", { messageId, delivery });\n return (DELIVERY_RANK[delivery] ?? 0) >= want ? message : undefined;\n };\n return new Promise<StoredMessage>((resolve, reject) => {\n let settled = false;\n const finish = (error: unknown, message?: StoredMessage) => {\n if (settled) return;\n settled = true; clearTimeout(timer); off();\n if (error) reject(error); else resolve(message!);\n };\n const check = (last = false) => void node(ctx).getMessage(chat, messageId).then((message) => {\n try {\n const done = look(message);\n if (done) finish(null, done);\n else if (last) finish(new CliError(\"timeout\", `Timed out after ${Math.round(ms / 1000)} s: the message is ${message?.delivery ?? \"not sent yet\"} and still goes by itself while this profile is online`, { messageId, delivery: message?.delivery ?? null }));\n } catch (error) { finish(error); }\n }, finish);\n const timer = setTimeout(() => check(true), ms);\n // Any event naming the message: a message first seen already delivered comes as `message.sent`, never as\n // `message.delivery`. Listening before the first read leaves no gap for a change to slip through.\n const off = ctx.hub.onEvent((event: GhostlyEvent) => {\n if (event.chat === chat && (event.messageId === messageId || (event.message as { id?: string } | undefined)?.id === messageId)) check();\n });\n check();\n });\n}\n","import { STATUS_CARD_LIMITS, cardLine, checkStatusCard, randomBytes, toBase64Url, type StatusCard } from \"@ghostly/core\";\nimport type { StoredMessage } from \"@ghostly/browser/shared/types\";\nimport { findSecret } from \"../../../apps/ui/src/lib/parse/secrets\";\nimport { bool, chatOrGroup, node, num, oneOf, state, str, type ApiContext, type Method, type Params } from \"./apiKit\";\nimport { CliError } from \"./errors\";\nimport { waitForEdit, waitForGroupFrame, waitForMessage } from \"./waits\";\n\n/*\n * Status cards (WISP 405 · Status Cards): a bot's task or routine, sent as a message with a card and kept current by\n * editing it. A routine's run (`run`) becomes its last run and the newest of its recent runs.\n * The engine checks every card by the sender's rule and writes its fallback text; this is the bot's side of it: a card\n * built from flags or JSON, found again by its id, merged with an update, and paced, one update per card every\n * `CARD_UPDATE_GAP_MS`. An update that comes sooner is merged into the next one, which goes when the time is up.\n */\n\n/** A card's updates go at most this often: the rest merge into the next, so a chatty bot spends few edits. */\nexport const CARD_UPDATE_GAP_MS = 2_500;\n\ntype Kind = StatusCard[\"kind\"];\ntype Target = { id: string; linkId: string; group: boolean };\n\n/** Per card (its message): when its last update went, what waits to go next, and how to send it now. */\ninterface Pace { last: number; next?: Record<string, unknown>; timer?: ReturnType<typeof setTimeout>; flushed?: Promise<void>; flush?: () => void }\nconst paces = new WeakMap<ApiContext, Map<string, Pace>>();\n\nfunction paceOf(ctx: ApiContext, key: string): Pace {\n let map = paces.get(ctx);\n if (!map) paces.set(ctx, (map = new Map()));\n let pace = map.get(key);\n if (!pace) map.set(key, (pace = { last: 0 }));\n return pace;\n}\n\nconst isObject = (value: unknown): value is Record<string, unknown> => typeof value === \"object\" && value !== null && !Array.isArray(value);\n\n/** The fields a command gave: an object, or a usage error that says so. */\nfunction fieldsOf(params: Params): Record<string, unknown> {\n const fields = params.card ?? {};\n if (!isObject(fields)) throw new CliError(\"bad_request\", \"card must be an object of the card's fields\");\n return fields;\n}\n\n/**\n * An update merged over a card: fields given replace those there, `pr` field by field, and a field given as null goes\n * (`--json '{\"step\":null}'`). The kind and the id never change.\n */\nexport function mergeCard(base: Record<string, unknown>, patch: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = { ...base };\n for (const [key, value] of Object.entries(patch)) {\n if (key === \"kind\" || key === \"id\") continue;\n if (value === null) { delete out[key]; continue; }\n if (key === \"pr\" && isObject(value) && isObject(base.pr)) {\n const pr: Record<string, unknown> = { ...base.pr };\n for (const [k, v] of Object.entries(value)) { if (v === null) delete pr[k]; else pr[k] = v; }\n out.pr = pr;\n continue;\n }\n out[key] = value;\n }\n return out;\n}\n\n/**\n * A routine's run from the command (`run: {result, summary?}`), as its last run and the first of its recent runs,\n * `STATUS_CARD_LIMITS.runs` kept. Undefined when the command names none.\n */\nexport function withRun(fields: Record<string, unknown>, params: Params, now = Date.now()): Record<string, unknown> {\n const run = params.run;\n if (run === undefined) return fields;\n if (!isObject(run) || typeof run.result !== \"string\") throw new CliError(\"bad_request\", \"run is {result: ok|failed|skipped, summary?}\");\n const lastRun = { at: now, result: run.result, ...(typeof run.summary === \"string\" && run.summary ? { summary: run.summary } : {}) };\n const before = Array.isArray(fields.runs) ? fields.runs : [];\n return { ...fields, lastRun, runs: [lastRun, ...before].slice(0, STATUS_CARD_LIMITS.runs) };\n}\n\n/** A card checked by the sender's rule, or a `bad_request` that says what is wrong. */\nfunction checked(card: Record<string, unknown>): StatusCard {\n const result = checkStatusCard(card);\n if (\"error\" in result) throw new CliError(\"bad_request\", `Status card: ${result.error}`, { card });\n return result.card;\n}\n\n/** Every text in a card's fields (title, step, items, runs, links…), as the contact's app may show it. */\nfunction cardTexts(value: unknown, out: string[] = []): string[] {\n if (typeof value === \"string\") out.push(value);\n else if (Array.isArray(value)) for (const item of value) cardTexts(item, out);\n else if (isObject(value)) for (const item of Object.values(value)) cardTexts(item, out);\n return out;\n}\n\n/**\n * The secret guard `send` has (docs/CLI.md § Safety): a card whose text looks like a seed, a private key or a Cashu\n * token goes only with `--force`. `fields` are what this command gives (an update's own, not the card it merges over).\n * A card's text is read as given and as the card carries it (`cardLine`): a line loses its invisible characters on the\n * way, so a key one of them broke in two is whole again on the card. The fallback text goes as it is, as `send`'s.\n */\nfunction guardSecrets(fields: unknown, params: Params, verb: string): void {\n if (bool(params, \"force\")) return;\n const check = (text: string) => {\n const secret = findSecret(text);\n if (secret) throw new CliError(\"confirm\", `The card's text looks like ${secret.kind === \"cashu\" ? \"a Cashu token (money anyone who reads it can take)\" : \"a secret (a seed or a private key)\"}; ${verb} it with --force if you mean to`, { kind: secret.kind });\n };\n for (const text of cardTexts([fields, params.run])) { check(text); check(cardLine(text, Infinity) ?? \"\"); }\n if (typeof params.text === \"string\") check(params.text);\n}\n\n/** How many cards a process remembers the message of; one past them is found in the history again. */\nexport const CARDS_REMEMBERED = 1_000;\n\n/** Per card (its chat or group, its kind and its id): the message of mine that carries it, as this process last saw it. */\nconst carriers = new WeakMap<ApiContext, Map<string, string>>();\nconst carrierKey = (linkId: string, kind: string, id: string) => `${linkId}\\n${kind}\\n${id}`;\n\nfunction carriersOf(ctx: ApiContext): Map<string, string> {\n let map = carriers.get(ctx);\n if (map) return map;\n carriers.set(ctx, (map = new Map()));\n const cards = map;\n // A newer card of mine stands for its id from the moment it is stored, whoever sent it (`engine call` too).\n ctx.hub.onEvent((event) => {\n if (event.type !== \"message.sent\" && event.type !== \"group.sent\") return;\n const message = event.message as { id?: unknown; card?: { kind?: unknown; id?: unknown } } | undefined;\n const card = message?.card;\n if (typeof message?.id !== \"string\" || typeof card?.id !== \"string\" || (card.kind !== \"task\" && card.kind !== \"routine\" && card.kind !== \"usage\")) return;\n carried(cards, carrierKey(event.type === \"group.sent\" ? `group:${String(event.group)}` : String(event.chat), card.kind, card.id), message.id);\n });\n return map;\n}\n\nfunction carried(cards: Map<string, string>, key: string, messageId: string): void {\n cards.delete(key);\n cards.set(key, messageId);\n if (cards.size > CARDS_REMEMBERED) cards.delete(cards.keys().next().value!);\n}\n\n/**\n * The latest message of mine in this chat or group with a card of this kind and id: the one an update edits. A card\n * this process sent or found before is read by its message's key, so its updates cost the same in a long chat as in a\n * short one; only a card not seen yet (the first update after a start, a one-shot) is looked for in the whole history.\n */\nasync function cardMessage(ctx: ApiContext, target: Target, kind: Kind, id: string): Promise<StoredMessage> {\n const cards = carriersOf(ctx), key = carrierKey(target.linkId, kind, id);\n const carries = (message: StoredMessage | undefined): message is StoredMessage => message?.sender === \"me\" && message.card?.kind === kind && message.card.id === id;\n const known = cards.get(key);\n if (known !== undefined) {\n const message = await node(ctx).getMessage(target.linkId, known);\n if (carries(message)) return message;\n // Deleted since, or it carries another card now: the history says which message stands for the id.\n cards.delete(key);\n }\n const messages = target.group ? await node(ctx).groupMessages({ groupId: target.id }) : await node(ctx).getMessages(target.linkId);\n // A card stored while the history was read is newer than what was read: that one stands for the id.\n if (cards.has(key)) return cardMessage(ctx, target, kind, id);\n for (let i = messages.length - 1; i >= 0; i--) {\n const message = messages[i]!;\n if (!carries(message)) continue;\n carried(cards, key, message.id);\n return message;\n }\n throw new CliError(\"not_found\", `No ${kind} ${JSON.stringify(id)} of yours in this ${target.group ? \"group\" : \"chat\"}: send it first with ghostly ${kind} send`);\n}\n\nfunction answer(target: Target, kind: Kind, id: string, messageId: string, extra: Record<string, unknown>): Record<string, unknown> {\n return { [target.group ? \"group\" : \"chat\"]: target.id, [kind]: id, messageId, ...extra };\n}\n\n/**\n * A task's parent is another task of mine in the same chat or group (WISP 405 · Status Cards § A task): one that is\n * not there is refused here, with its name, rather than sent to stand alone on every board (a reader keeps the field\n * and shows the task ungrouped). Send the parent first.\n */\nasync function parentThere(ctx: ApiContext, target: Target, card: StatusCard): Promise<void> {\n if (card.kind !== \"task\" || !card.parent) return;\n const parent = card.parent;\n await cardMessage(ctx, target, \"task\", parent).catch(() => {\n throw new CliError(\"not_found\", `--parent: no task ${JSON.stringify(parent)} of yours in this ${target.group ? \"group\" : \"chat\"}: send the parent first`);\n });\n}\n\n/** A new card: a message whose text is written from it, in a chat or a group. */\nasync function sendCard(ctx: ApiContext, params: Params, kind: Kind): Promise<Record<string, unknown>> {\n const target = chatOrGroup(ctx, str(params, \"chat\", true));\n const fields = fieldsOf(params);\n guardSecrets(fields, params, \"send\");\n const now = Date.now();\n const id = typeof fields.id === \"string\" && fields.id ? fields.id : `${kind}-${toBase64Url(randomBytes(6))}`;\n const card = checked(withRun({\n ...(kind === \"task\" ? { status: \"running\", startedAt: now, updatedAt: now } : kind === \"routine\" ? { state: \"active\" } : {}),\n ...fields, kind, id,\n }, params, now));\n await parentThere(ctx, target, card);\n const text = str(params, \"text\") ?? \"\";\n const wait = oneOf(params, \"wait\", target.group ? [\"none\", \"sent\"] as const : [\"none\", \"sent\", \"delivered\"] as const, \"none\");\n const ms = num(params, \"timeout\", 30, { min: 1, max: 3600 }) * 1000;\n if (target.group) {\n const result = await node(ctx).sendGroupMessage({ groupId: target.id, text, card });\n if (result.error || !result.messageId) throw new CliError(result.refused ? \"refused\" : \"unavailable\", result.error ?? \"Nothing to send\");\n carried(carriersOf(ctx), carrierKey(target.linkId, kind, id), result.messageId);\n const edges = wait === \"sent\" ? await waitForGroupFrame(ctx, target.id, result.messageId, undefined, ms) : undefined;\n return answer(target, kind, id, result.messageId, { card, ...(edges !== undefined && { edges }) });\n }\n const result = await node(ctx).sendMessage({ linkId: target.linkId, text, card });\n if (result.error) throw new CliError(result.refused ? \"refused\" : \"unavailable\", result.error);\n if (!result.messageId) throw new CliError(\"bad_request\", \"Nothing to send\");\n carried(carriersOf(ctx), carrierKey(target.linkId, kind, id), result.messageId);\n let message = await node(ctx).getMessage(target.linkId, result.messageId);\n if (wait !== \"none\") message = await waitForMessage(ctx, target.linkId, result.messageId, wait, ms);\n return answer(target, kind, id, result.messageId, { card, delivery: message?.delivery ?? null });\n}\n\n/** Edits the card's message with its merged card, now. */\nasync function editCard(ctx: ApiContext, target: Target, messageId: string, card: StatusCard, text: string): Promise<void> {\n const result = await node(ctx).editMessage({ linkId: target.linkId, messageId, text, card });\n if (result.error) throw new CliError(result.refused ? \"refused\" : \"unavailable\", result.error);\n}\n\n/**\n * An update of a card of mine: the fields given, merged over its latest state (what waits to go included). It goes at\n * once when the card's last update is `CARD_UPDATE_GAP_MS` old; otherwise it waits for that, merged with any update\n * that comes meanwhile (`queued: true`). A one-shot command waits the time out itself: it has no daemon to wait in.\n */\nasync function updateCard(ctx: ApiContext, params: Params, kind: Kind): Promise<Record<string, unknown>> {\n const target = chatOrGroup(ctx, str(params, \"chat\", true));\n const id = str(params, kind, true);\n const patch = fieldsOf(params);\n guardSecrets(patch, params, \"update\");\n const message = await cardMessage(ctx, target, kind, id);\n // A card message takes `STATUS_CARD_LIMITS.edits` updates (WISP 405 · Status Cards); past them, a new card goes on.\n if ((message.edit?.seq ?? 0) >= STATUS_CARD_LIMITS.edits)\n throw new CliError(\"refused\", `This ${kind}'s message took ${STATUS_CARD_LIMITS.edits} updates, the most one takes: start a new card with ghostly ${kind} send (a new --id, or the same one: the newest card of an id stands for it)`, { messageId: message.id, edits: message.edit!.seq });\n const pace = paceOf(ctx, `${target.linkId}\\n${message.id}`);\n const base = pace.next ?? (message.card as unknown as Record<string, unknown>);\n const merged = withRun(mergeCard(base, { ...patch, ...(kind === \"task\" && { updatedAt: Date.now() }) }), params);\n const card = checked(merged);\n if (isObject(patch) && patch.parent !== undefined) await parentThere(ctx, target, card);\n const text = str(params, \"text\") ?? \"\";\n const wait = oneOf(params, \"wait\", target.group ? [\"none\", \"sent\"] as const : [\"none\", \"confirmed\"] as const, \"none\");\n const ms = num(params, \"timeout\", 30, { min: 1, max: 3600 }) * 1000;\n const last = Math.max(pace.last, message.edit?.at ?? message.timestamp);\n const due = last + CARD_UPDATE_GAP_MS - Date.now();\n let queued = false;\n if (due > 0 && ctx.mode === \"daemon\") {\n // Merged into what goes next; the first update in the gap sets the timer, the later ones only change what it sends.\n pace.next = card as unknown as Record<string, unknown>;\n queued = true;\n if (!pace.timer) pace.flushed = new Promise<void>((resolve) => {\n const send = () => {\n const next = pace.next;\n clearTimeout(pace.timer);\n pace.timer = undefined; pace.next = undefined; pace.flush = undefined; pace.last = Date.now();\n void (next ? editCard(ctx, target, message.id, next as unknown as StatusCard, text) : Promise.resolve()).catch(() => {}).finally(resolve);\n };\n pace.flush = send;\n pace.timer = setTimeout(send, due);\n });\n if (wait !== \"none\") await pace.flushed;\n } else {\n if (due > 0) await new Promise((resolve) => setTimeout(resolve, due));\n pace.last = Date.now();\n await editCard(ctx, target, message.id, card, text);\n }\n let edited = await node(ctx).getMessage(target.linkId, message.id);\n if (wait === \"confirmed\" && edited?.edit?.pending) edited = await waitForEdit(ctx, target.linkId, message.id, ms);\n const edges = wait === \"sent\" && edited?.edit ? await waitForGroupFrame(ctx, target.id, message.id, edited.edit.seq, ms) : undefined;\n return answer(target, kind, id, message.id, {\n card, queued, edits: edited?.edit?.seq ?? 0,\n // An update still waiting for its time went nowhere yet: it is not confirmed, whatever the message's last edit says.\n ...(target.group ? { ...(edges !== undefined && { edges }) } : { confirmed: (!queued || wait !== \"none\") && !!edited && !edited.edit?.pending }),\n });\n}\n\n/**\n * Sends now every update still waiting for its time: the daemon is stopping, and the update it answered `queued` for\n * (often a task's last one, done or failed) would otherwise go with it, leaving the card running for good. Called\n * while the engine can still store the edit; it goes to the contact as any edit does, now or once they are back.\n */\nexport async function flushCardUpdates(ctx: ApiContext): Promise<void> {\n const waiting = [...(paces.get(ctx)?.values() ?? [])].filter((pace) => pace.flush);\n for (const pace of waiting) pace.flush!();\n await Promise.all(waiting.map((pace) => pace.flushed));\n}\n\n/** A usage card's fields: all of them are said again by each report, so one not given goes from the card. */\nconst USAGE_FIELDS = [\"left\", \"used\", \"limit\", \"label\", \"account\", \"window\", \"resetsAt\", \"updatedAt\", \"windows\"] as const;\n\n/** The id a bot's usage card goes by when it names none: one live card per chat. */\nexport const USAGE_CARD_ID = \"usage\";\n\n/**\n * One chat's usage report (WISP 405 § Usage): the bot's usage card there, kept as one message. The first report sends\n * it; every later one edits it in place (quiet: no sound, no unread, no move in the list), paced as a task's updates.\n * A report is whole: what it does not say goes from the card. A card at its edits' cap gives way to a new message.\n */\nasync function reportUsage(ctx: ApiContext, ref: string, fields: Record<string, unknown>, params: Params): Promise<Record<string, unknown>> {\n const target = chatOrGroup(ctx, ref);\n const id = typeof fields.id === \"string\" && fields.id ? fields.id : USAGE_CARD_ID;\n const report = { updatedAt: Date.now(), ...fields, kind: \"usage\", id };\n checked(report);\n const sent = await cardMessage(ctx, target, \"usage\", id).catch(() => undefined);\n const wait = oneOf(params, \"wait\", [\"none\", \"sent\"] as const, \"none\");\n if (!sent || (sent.edit?.seq ?? 0) >= STATUS_CARD_LIMITS.edits) {\n const answer = await sendCard(ctx, { ...params, chat: ref, card: report, wait }, \"usage\");\n return { ...answer, updated: false };\n }\n const patch: Record<string, unknown> = {};\n for (const key of USAGE_FIELDS) patch[key] = report[key as keyof typeof report] ?? null;\n const answer = await updateCard(ctx, { ...params, chat: ref, usage: id, card: patch, wait: wait === \"none\" ? \"none\" : target.group ? \"sent\" : \"confirmed\" }, \"usage\");\n return { ...answer, updated: true };\n}\n\n/** `usage send`: one chat or group, or (`all`) every 1:1 chat with a contact; a chat that refuses is said, not fatal. */\nasync function sendUsage(ctx: ApiContext, params: Params): Promise<Record<string, unknown>> {\n const fields = fieldsOf(params);\n if (!bool(params, \"all\")) return reportUsage(ctx, str(params, \"chat\", true), fields, params);\n if (str(params, \"chat\")) throw new CliError(\"bad_request\", \"Name a chat or --all, not both\");\n checked({ updatedAt: Date.now(), ...fields, kind: \"usage\", id: typeof fields.id === \"string\" && fields.id ? fields.id : USAGE_CARD_ID });\n const chats = state(ctx).links.filter((link) => link.profile && link.peerPubKeyZ32);\n const results: Record<string, unknown>[] = [];\n for (const link of chats) {\n try { results.push(await reportUsage(ctx, link.id, fields, params)); }\n catch (error) { results.push({ chat: link.id, error: error instanceof Error ? error.message : String(error) }); }\n }\n return { chats: results };\n}\n\nexport const STATUS_CARD_METHODS: Record<string, Method> = {\n /** WISP 405 · Status Cards: a task card in a chat or a group; the answer names the task's id and its message. */\n \"task.send\": (ctx, params) => sendCard(ctx, params, \"task\"),\n /** A task's update: the fields given, merged over its latest state, paced one per `CARD_UPDATE_GAP_MS`. */\n \"task.update\": (ctx, params) => updateCard(ctx, params, \"task\"),\n /** A routine card (its schedule, state, runs); `run` records one. */\n \"routine.send\": (ctx, params) => sendCard(ctx, params, \"routine\"),\n /** A routine's update, as a task's; `run` records a run as its last and the newest of its recent ones. */\n \"routine.update\": (ctx, params) => updateCard(ctx, params, \"routine\"),\n /** WISP 405 § Usage: how much of a quota the bot has left, kept as one card per chat; `all` for every 1:1 chat. */\n \"usage.send\": sendUsage,\n};\n","import { BUTTONS_CAPABILITY, STATUS_CARD_LIMITS, buttonLabelClash, checkStatusCard, randomBytes, toBase64Url, type ButtonsCard } from \"@ghostly/core\";\nimport type { LinkView, StoredMessage } from \"@ghostly/browser/shared/types\";\nimport type { Parsed } from \"./args\";\nimport { bool, chatOrGroup, messageOf, node, num, oneOf, str, type Method, type Params } from \"./apiKit\";\nimport { CliError } from \"./errors\";\nimport { findSecret } from \"../../../apps/ui/src/lib/parse/secrets\";\nimport { waitForEdit, waitForGroupFrame, waitForMessage } from \"./waits\";\n\n/*\n * Message buttons (WISP 406 · Message Buttons), the bot's side: a message whose text is the question, with up to six\n * buttons under it (`send --button yes:Yes`). A press comes back as a reply whose text is the button's label; the\n * engine marks it (`press`) and `listen` says `button.pressed`. `button update` then shows the answer (`chosen`) and\n * closes the buttons, by editing the message with the same buttons.\n */\n\n/**\n * `--button id:Label` (again for each, 1 to 6), `--style id=primary|neutral|danger`, `--once` and `--id`, as the\n * method's parameters: `buttons` and `buttonsId`. Nothing when no `--button` is given. The label may hold colons: the\n * id ends at the first one. Two labels that read the same (ignoring case and spaces at the ends), or a label that is\n * another button's id, are a usage error here; other bounds (ids, labels, how many) are the card's, checked when it is sent.\n */\nexport function buttonsOf(options: Parsed[\"options\"]): { buttons: Record<string, unknown>[]; buttonsId?: string } | undefined {\n const given = Array.isArray(options.button) ? options.button as string[] : [];\n if (!given.length) {\n for (const flag of [\"style\", \"once\", \"id\"] as const)\n if (options[flag] !== undefined) throw new CliError(\"usage\", `--${flag} goes with --button id:Label`);\n return undefined;\n }\n const buttons: Record<string, unknown>[] = given.map((value) => {\n const colon = value.indexOf(\":\");\n if (colon < 1 || colon === value.length - 1) throw new CliError(\"usage\", `--button takes id:Label, like yes:Yes, not ${JSON.stringify(value)}`);\n return { id: value.slice(0, colon), label: value.slice(colon + 1).trim() };\n });\n // Two buttons a typed answer could not tell apart: the same label, or a label that is another's id (ignoring case).\n const clash = buttonLabelClash(buttons);\n if (clash) {\n const [at, other] = [JSON.stringify(given[clash.at]), JSON.stringify(given[clash.with])];\n throw new CliError(\"usage\", clash.kind === \"label\"\n ? `--button ${at} repeats the label of --button ${other} (ignoring case and spaces): a typed answer could not tell them apart`\n : `--button ${at} has a label that is the id of --button ${other}: a typed answer could not tell them apart`);\n }\n for (const value of Array.isArray(options.style) ? options.style as string[] : []) {\n const eq = value.indexOf(\"=\");\n const button = eq > 0 ? buttons.find((b) => b.id === value.slice(0, eq)) : undefined;\n if (!button) throw new CliError(\"usage\", `--style takes id=primary|neutral|danger for one of the buttons, not ${JSON.stringify(value)}`);\n button.style = value.slice(eq + 1);\n }\n if (options.once === true) for (const button of buttons) button.once = true;\n return { buttons, ...(options.id !== undefined && { buttonsId: String(options.id) }) };\n}\n\n/** A buttons card checked by the sender's rule, or a `bad_request` that says what is wrong. */\nfunction checked(card: Record<string, unknown>): ButtonsCard {\n const result = checkStatusCard(card);\n if (\"error\" in result) throw new CliError(\"bad_request\", `Buttons: ${result.error}`, { card });\n return result.card as ButtonsCard;\n}\n\n/**\n * The card a send's `buttons` (and `buttonsId`) make, or undefined when it names none. `buttons` is a list of\n * `{id, label, style?, once?}`; the id of the question is made up (`ask-…`) unless given.\n */\nexport function buttonsCard(params: Params): ButtonsCard | undefined {\n const buttons = params.buttons;\n if (buttons === undefined || buttons === null) {\n if (params.buttonsId !== undefined) throw new CliError(\"bad_request\", \"buttonsId goes with buttons\");\n return undefined;\n }\n if (!Array.isArray(buttons)) throw new CliError(\"bad_request\", \"buttons must be a list of {id, label, style?, once?}\");\n const id = str(params, \"buttonsId\") ?? `ask-${toBase64Url(randomBytes(6))}`;\n return checked({ kind: \"buttons\", id, buttons });\n}\n\n/** Whether a 1:1 contact's app shows and presses buttons: null until its app said what it offers. */\nexport function pressable(link: LinkView): boolean | null {\n const peer = link.sessionOffers?.peer;\n return peer ? peer.includes(BUTTONS_CAPABILITY) : null;\n}\n\n/** The message of mine with buttons that `button update` names, by its id here (or the id both sides know). */\nfunction buttonsMessage(message: StoredMessage | undefined, ref: string, group: boolean): StoredMessage & { card: ButtonsCard } {\n if (!message) throw new CliError(\"not_found\", `No message ${JSON.stringify(ref)} of yours in this ${group ? \"group\" : \"chat\"}: the messageId send gave`);\n if (message.card?.kind !== \"buttons\") throw new CliError(\"bad_request\", \"That message has no buttons: send one with send --button id:Label\");\n return message as StoredMessage & { card: ButtonsCard };\n}\n\n/**\n * `button update`: the answer shown on a question's buttons (`chosen`), the buttons closed (`close`), or a new text\n * (`text`), by editing the message with the same buttons. What is not given stays as it was.\n */\nasync function updateButtons(ctx: Parameters<Method>[0], params: Params): Promise<Record<string, unknown>> {\n const target = chatOrGroup(ctx, str(params, \"chat\", true));\n const ref = str(params, \"message\", true);\n const chosen = str(params, \"chosen\");\n const close = bool(params, \"close\");\n const newText = str(params, \"text\");\n if (chosen === undefined && !close && newText === undefined) throw new CliError(\"bad_request\", \"Nothing to update: give chosen, close or text\");\n // A new text is a message like any other: the same guard as `send` and `edit`.\n if (newText !== undefined && !bool(params, \"force\")) {\n const secret = findSecret(newText);\n if (secret) throw new CliError(\"confirm\", `The text looks like ${secret.kind === \"cashu\" ? \"a Cashu token (money anyone who reads it can take)\" : \"a secret (a seed or a private key)\"}; update with --force if you mean to`, { kind: secret.kind });\n }\n const message = buttonsMessage(await messageOf(ctx, target.linkId, ref, (m) => m.sender === \"me\"), ref, target.group);\n if ((message.edit?.seq ?? 0) >= STATUS_CARD_LIMITS.edits)\n throw new CliError(\"refused\", `This message took ${STATUS_CARD_LIMITS.edits} updates, the most one takes: ask again with a new send`, { messageId: message.id, edits: message.edit!.seq });\n const before = message.card;\n // Said here, with the buttons it has, as a press says it: the card's own check names only the field.\n if (chosen !== undefined && !before.buttons.some((b) => b.id === chosen))\n throw new CliError(\"bad_request\", `That message has no button ${JSON.stringify(chosen)}; its buttons: ${before.buttons.map((b) => b.id).join(\", \")}`, { card: before });\n const card = checked({ ...before, ...(chosen !== undefined && { chosen }), ...((close || before.closed) && { closed: true }) });\n const text = newText ?? message.text;\n const wait = oneOf(params, \"wait\", target.group ? [\"none\", \"sent\"] as const : [\"none\", \"confirmed\"] as const, \"none\");\n const ms = num(params, \"timeout\", 30, { min: 1, max: 3600 }) * 1000;\n const result = await node(ctx).editMessage({ linkId: target.linkId, messageId: message.id, text, card });\n if (result.error) throw new CliError(result.refused ? \"refused\" : \"unavailable\", result.error);\n let edited = await node(ctx).getMessage(target.linkId, message.id);\n if (wait === \"confirmed\" && edited?.edit?.pending) edited = await waitForEdit(ctx, target.linkId, message.id, ms);\n const edges = wait === \"sent\" && edited?.edit ? await waitForGroupFrame(ctx, target.id, message.id, edited.edit.seq, ms) : undefined;\n return {\n [target.group ? \"group\" : \"chat\"]: target.id, buttons: card.id, messageId: message.id, card, edits: edited?.edit?.seq ?? 0,\n ...(target.group ? { ...(edges !== undefined && { edges }) } : { confirmed: !!edited && !edited.edit?.pending }),\n };\n}\n\n/**\n * `button press`: the other side of a question, as the app's tap: a press on a button of someone else's message, by\n * its id here (or the id both sides know). The engine sends a reply whose text is the button's label, and refuses what\n * an app would: a button the message does not have, closed buttons, a second answer past `once`, a second press\n * within a second. A one-shot in a chat waits until the press went out, as `send` does.\n */\nasync function pressButton(ctx: Parameters<Method>[0], params: Params): Promise<Record<string, unknown>> {\n const target = chatOrGroup(ctx, str(params, \"chat\", true));\n const ref = str(params, \"message\", true);\n const buttonId = str(params, \"button\", true);\n const where = target.group ? \"group\" : \"chat\";\n const message = await messageOf(ctx, target.linkId, ref, (m) => m.sender !== \"me\");\n if (!message) throw new CliError(\"not_found\", `No message ${JSON.stringify(ref)} from someone else in this ${where}: the id its event or history gave`);\n if (message.card?.kind !== \"buttons\") throw new CliError(\"bad_request\", \"That message has no buttons\");\n const button = message.card.buttons.find((b) => b.id === buttonId);\n if (!button) throw new CliError(\"not_found\", `That message has no button ${JSON.stringify(buttonId)}; its buttons: ${message.card.buttons.map((b) => b.id).join(\", \")}`);\n const wait = target.group\n ? oneOf(params, \"wait\", [\"none\", \"sent\"] as const, \"none\")\n : oneOf(params, \"wait\", [\"none\", \"sent\", \"delivered\"] as const, ctx.mode === \"one-shot\" ? \"sent\" : \"none\");\n const ms = num(params, \"timeout\", 30, { min: 1, max: 3600 }) * 1000;\n const result = await node(ctx).pressButton({ linkId: target.linkId, messageId: message.id, buttonId });\n if (result.error) throw new CliError(result.refused ? \"refused\" : \"unavailable\", result.error, result.paced ? { paced: true } : undefined);\n const replyId = result.messageId ?? null;\n const answer = { [where]: target.id, messageId: message.id, button: button.id, label: button.label, replyId };\n if (!replyId) return answer;\n if (target.group) {\n const edges = wait === \"sent\" ? await waitForGroupFrame(ctx, target.id, replyId, undefined, ms) : node(ctx).groupTaken({ groupId: target.id, messageId: replyId });\n return { ...answer, edges };\n }\n const reply = wait === \"none\" ? await node(ctx).getMessage(target.linkId, replyId) : await waitForMessage(ctx, target.linkId, replyId, wait, ms);\n return { ...answer, delivery: reply?.delivery ?? null };\n}\n\nexport const BUTTON_METHODS: Record<string, Method> = {\n /** WISP 406 · Message Buttons: a press on a button of someone else's message, as the app's tap. */\n \"button.press\": pressButton,\n /** WISP 406 · Message Buttons: the answer chosen on a question of mine, its buttons closed, or its text changed. */\n \"button.update\": updateButtons,\n};\n","import { readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { LinkView } from \"@ghostly/browser/shared/types\";\nimport { node, state, type ApiContext } from \"./apiKit\";\nimport { CliError } from \"./errors\";\n\n/**\n * `chat disconnect --hold <minutes>`: a chat kept off its direct link for a while. It is the chat's own \"DHT only\"\n * choice (WISP 400: either side choosing it keeps both off the live link, so the contact's app does not redial),\n * taken back when the time is up. Text still goes over the DHT (short texts), files wait. The holds are kept in\n * the profile folder: a daemon that restarts takes back the ones whose time is up, and never a DHT only the chat had\n * chosen before (that one is not a hold).\n */\nexport const HOLD_MAX_MINUTES = 7 * 24 * 60;\n\ninterface Hold { until: number; since: number }\ntype Holds = Record<string, Hold>;\n\nconst timers = new WeakMap<ApiContext, Map<string, ReturnType<typeof setTimeout>>>();\n\nfunction holdsFile(ctx: ApiContext): string {\n return join(ctx.runtime.paths.dir, \"holds.json\");\n}\n\nfunction readHolds(ctx: ApiContext): Holds {\n try { return JSON.parse(readFileSync(holdsFile(ctx), \"utf8\")) as Holds; } catch { return {}; }\n}\n\nfunction writeHolds(ctx: ApiContext, holds: Holds): void {\n const file = holdsFile(ctx);\n writeFileSync(file + \".tmp\", JSON.stringify(holds) + \"\\n\", { mode: 0o600 });\n renameSync(file + \".tmp\", file);\n}\n\nfunction timersOf(ctx: ApiContext): Map<string, ReturnType<typeof setTimeout>> {\n let map = timers.get(ctx);\n if (!map) timers.set(ctx, (map = new Map()));\n return map;\n}\n\nfunction schedule(ctx: ApiContext, linkId: string, until: number): void {\n const map = timersOf(ctx);\n clearTimeout(map.get(linkId));\n const timer = setTimeout(() => {\n map.delete(linkId);\n void releaseHold(ctx, linkId, \"expired\").catch((error: unknown) => process.stderr.write(`ghostly: the hold on ${linkId} could not end: ${error instanceof Error ? error.message : String(error)}\\n`));\n }, Math.max(0, until - Date.now()));\n // A one-shot leaves when its command is done; the next start takes the hold back.\n timer.unref?.();\n map.set(linkId, timer);\n}\n\n/** When the chat's hold ends, or null. */\nexport function holdOf(ctx: ApiContext, linkId: string): number | null {\n return readHolds(ctx)[linkId]?.until ?? null;\n}\n\n/** Keeps the chat off its direct link for `minutes` (again: from now). */\nexport async function holdChat(ctx: ApiContext, link: LinkView, minutes: number): Promise<number> {\n const holds = readHolds(ctx);\n if (link.deliveryMode === \"dht\" && !holds[link.id]) throw new CliError(\"refused\", \"This chat is on DHT only already, by its own choice: chat transport auto leaves it\");\n if (!link.profile) throw new CliError(\"unavailable\", \"A chat of the older protocol has no DHT only\");\n const until = Date.now() + minutes * 60_000;\n if (link.deliveryMode !== \"dht\") {\n try { await node(ctx).setChatTransport({ linkId: link.id, transport: \"dht\" }); } catch (error) {\n throw new CliError(\"unavailable\", error instanceof Error ? error.message : String(error));\n }\n }\n holds[link.id] = { until, since: holds[link.id]?.since ?? Date.now() };\n writeHolds(ctx, holds);\n schedule(ctx, link.id, until);\n ctx.hub.emit(\"chat.held\", `chat.held:${link.id}:${until}`, { chat: link.id, until });\n return until;\n}\n\n/**\n * Ends a hold: the chat leaves DHT only, unless someone chose otherwise meanwhile. Whether there was one. `reason`:\n * expired, connect (`chat connect`) or lifted (`--hold 0`).\n */\nexport async function releaseHold(ctx: ApiContext, linkId: string, reason: \"expired\" | \"connect\" | \"lifted\"): Promise<boolean> {\n const holds = readHolds(ctx);\n if (!holds[linkId]) return false;\n clearTimeout(timersOf(ctx).get(linkId));\n timersOf(ctx).delete(linkId);\n const link = state(ctx).links.find((l) => l.id === linkId);\n if (link?.deliveryMode === \"dht\") await node(ctx).setDeliveryMode({ linkId, mode: \"stream\" });\n delete holds[linkId];\n writeHolds(ctx, holds);\n ctx.hub.emit(\"chat.released\", `chat.released:${linkId}:${Date.now()}`, { chat: linkId, reason });\n return true;\n}\n\n/** At start: holds whose time is up end now, the others are timed again. */\nexport function resumeHolds(ctx: ApiContext): void {\n for (const [linkId, hold] of Object.entries(readHolds(ctx))) schedule(ctx, linkId, hold.until);\n}\n","import type { GroupView, LinkView, MessageFile, StoredMessage } from \"@ghostly/browser/shared/types\";\nimport type { StatusCard } from \"@ghostly/core\";\n\n/**\n * The JSON shapes the CLI and the daemon answer with. They are the contract bots code against (WISP 1100), kept\n * apart from the engine's views: those change with the app, these only by adding fields.\n */\n\nexport interface ChatJson {\n id: string;\n /** The label set here, else the name the contact gave, else null. */\n name: string | null;\n label: string | null;\n peerName: string | null;\n /** The contact's key for this chat (z-base-32). */\n peer: string;\n createdAt: number;\n lastMessageAt: number;\n /** A live session carries the chat now, in this run (text, files, payments go at once). */\n live: boolean;\n /** The transport of the live session, or null. */\n transport: string | null;\n /** How text goes now: `stream` (live), `dht` (the DHT floor, 256 bytes), `hold`, or `unavailable`. */\n text: string;\n /** The pairing's state: connecting, waiting, ready, failed. */\n pairing: string | null;\n /** How far the first pairing got (publishing, waiting, resolving, on-dht, live, …), while it is under way. */\n stage: string | null;\n verified: boolean;\n /**\n * The invite this side made for the chat, while it waits for the contact: `<hidden>` unless asked for\n * (`--show-secret`), since whoever reads the code can take the chat. Null once the chat is paired, or without one.\n */\n invite: string | null;\n /** A chat of the older protocol (WISP 402). */\n compatibility: boolean;\n /** The contact sent a picture (checked by the engine; `engine getState` has it). */\n peerPicture: boolean;\n}\n\n/**\n * A live session carries the chat now, in this run: its link is open and, on a paired chat, the session authenticated\n * on a transport. The engine's own rule for a chat's connection line (`observeTransport`). `textDelivery` alone says\n * how a text would go, and nothing the engine remembers from an earlier run (a chat that was live when the app last\n * ran, WISP 100) counts: until the contact answers again the link is not open and the pairing is `connecting`.\n */\nexport function isLive(link: LinkView): boolean {\n if (link.textDelivery !== \"stream\" || link.dataLink !== \"open\") return false;\n return !link.profile || (link.pairing?.status === \"ready\" && !!link.pairing.transport);\n}\n\nexport function chatJson(link: LinkView, showSecret?: boolean): ChatJson {\n const label = link.label?.trim() || null;\n const peerName = link.peerNick?.trim() || null;\n const live = isLive(link);\n return {\n id: link.id,\n name: label ?? peerName,\n label,\n peerName,\n peer: link.peerPubKeyZ32,\n createdAt: link.createdAt,\n lastMessageAt: link.lastMessageAt,\n live,\n transport: live ? (link.pairing?.transport ?? link.pairingProgress?.transport ?? null) : null,\n text: link.textDelivery ?? (link.profile ? \"unavailable\" : \"dht\"),\n pairing: link.pairing?.status ?? null,\n stage: link.pairingProgress?.stage ?? null,\n verified: !!link.peerVerified,\n invite: link.pairing?.status === \"ready\" || !link.inviteCode ? null : showSecret === true ? link.inviteCode : \"<hidden>\",\n compatibility: !link.profile,\n peerPicture: !!link.peerAvatar,\n };\n}\n\n/** A chat's details: the summary plus what its connection panel shows. */\nexport function chatDetailsJson(link: LinkView, showSecret?: boolean) {\n return {\n ...chatJson(link, showSecret),\n progress: link.pairingProgress ?? null,\n transports: { mine: link.availableTransports ?? [], peer: link.peerTransports ?? [], preferred: link.preferredTransport ?? null, fallback: link.transportFallback ?? null, automatic: link.transportAutomatic ?? null, relayed: link.relayedTransports ?? [] },\n wait: link.transportWait ?? null,\n lastAttempt: link.liveAttempt ?? null,\n liveSince: link.transportLive?.since ?? null,\n rttMs: link.transportRttMs ?? null,\n relays: link.transportRelayed?.relays ?? null,\n deliveryMode: link.deliveryMode ?? null,\n capabilities: link.capabilities ?? null,\n hold: link.hold ?? null,\n peerOnline: link.peerOnline,\n peerLastSeenAt: link.peerLastSeenAt || null,\n history: link.transportHistory ?? null,\n errors: link.transportErrors ?? null,\n comparisonCode: link.pairing?.code ?? null,\n };\n}\n\nexport interface MessageJson {\n id: string;\n chat: string;\n from: \"me\" | \"peer\";\n text: string;\n /** Its place in the history, which is ordered by it: when I sent it, or when it came here (never the sender's clock). */\n timestamp: number;\n /** Received: when its sender says it was sent, by its own clock. */\n sentAt?: number;\n delivery: string | null;\n deliveryError: string | null;\n via: string;\n nick: string | null;\n member?: string;\n mentions?: { key: string; offset: number; length: number }[];\n mentioned?: boolean;\n /** A file or voice message: its id is what `file wait`, `file save` and the `file.*` events name. */\n file?: { id: string; name: string; size: number; mime: string; voice?: { duration: number; peaks?: number[] }; image?: { width: number; height: number } };\n paymentId?: string;\n event?: unknown;\n /**\n * The message this one answers (WISP 400 § Replies). `id`: the original's message id here when it is in this chat\n * (`found`), else the id the reply named. `from`: null when nothing says (only the id came, over the DHT).\n */\n replyTo?: { id: string; snippet: string; from: \"me\" | \"peer\" | null; member?: string; found: boolean; button?: string };\n /** Reactions to it, one per person (WISP 400 § Reactions): `by` is `me`, `peer` or a member's key. */\n reactions?: { by: string; emoji: string; at: number }[];\n /** An edited text (WISP 400 § Edits): `text` is the latest version, `edits` its number, `editedAt` when it was made. */\n edits?: number;\n editedAt?: number;\n /** Mine: the contact has not confirmed the latest edit yet. */\n editPending?: boolean;\n /** A forwarded message (WISP 400 § Forwards): how many times it has been forwarded. Never who wrote it first. */\n forwarded?: number;\n /** A bot's task or routine (WISP 405 · Status Cards), as its version says it; `text` is its fallback. Or its buttons. */\n card?: StatusCard;\n /**\n * A reply that pressed a button of a message of mine (WISP 406 · Message Buttons), as this side's engine took it:\n * `messageId` the question here, `button` its id, `label`, `inferred` when only the text named it.\n */\n press?: { messageId: string; button: string; label: string; inferred?: true };\n}\n\nexport function messageJson(message: StoredMessage): MessageJson {\n return {\n id: message.id,\n chat: message.linkId,\n from: message.sender,\n text: message.text,\n timestamp: message.timestamp,\n ...(message.sentAt !== undefined ? { sentAt: message.sentAt } : {}),\n // A received message is delivered by definition; one of mine without a state went before states existed.\n delivery: message.delivery ?? (message.sender === \"peer\" ? null : \"sent\"),\n deliveryError: message.deliveryError ?? null,\n via: message.via,\n nick: message.nick ?? null,\n ...(message.member ? { member: message.member } : {}),\n ...(message.mentions?.length ? { mentions: message.mentions.map((m) => ({ key: m.k, offset: m.o, length: m.l })) } : {}),\n ...(message.mentioned ? { mentioned: true } : {}),\n ...(message.file ? { file: fileJson(message.file) } : {}),\n ...(message.paymentId ? { paymentId: message.paymentId } : {}),\n ...(message.event ? { event: message.event } : {}),\n ...(message.replyTo ? { replyTo: { id: message.replyTo.messageId ?? message.replyTo.id, snippet: message.replyTo.snippet, from: message.replyTo.from ?? null,\n ...(message.replyTo.member ? { member: message.replyTo.member } : {}), found: !!message.replyTo.messageId,\n ...(message.replyTo.button ? { button: message.replyTo.button } : {}) } } : {}),\n ...(reactionsJson(message).length ? { reactions: reactionsJson(message) } : {}),\n ...(message.edit ? { edits: message.edit.seq, editedAt: message.edit.at, ...(message.edit.pending ? { editPending: true } : {}) } : {}),\n ...(message.forwarded ? { forwarded: message.forwarded } : {}),\n ...(message.card ? { card: message.card } : {}),\n ...(message.press ? { press: { messageId: message.press.messageId, button: message.press.button, label: message.press.label, ...(message.press.inferred ? { inferred: true as const } : {}) } } : {}),\n };\n}\n\n/**\n * A message's file; a voice note's length (ms) and loudness bars ride along (older releases said only `voice: true`),\n * and a picture's size as it is shown when its sender sent it.\n */\nexport function fileJson(file: MessageFile): NonNullable<MessageJson[\"file\"]> {\n return {\n id: file.id, name: file.name, size: file.size, mime: file.mime,\n ...(file.voice ? { voice: { duration: file.voice.duration, ...(file.voice.peaks?.length ? { peaks: [...file.voice.peaks] } : {}) } } : {}),\n ...(file.image ? { image: { width: file.image.width, height: file.image.height } } : {}),\n };\n}\n\n/** A message's reactions shown now (taken-back ones left out), oldest first. */\nexport function reactionsJson(message: StoredMessage): { by: string; emoji: string; at: number }[] {\n return Object.entries(message.reactions ?? {}).filter(([, r]) => r.e).sort(([, a], [, b]) => a.at - b.at).map(([by, r]) => ({ by, emoji: r.e, at: r.at }));\n}\n\n/**\n * A 1:1 chat's message with its author named: the engine keeps a contact's message without a name (the name rides on\n * the chat), so the name the contact gave fills `nick`, as a group's roster does. Mine keep null. Before, `nick` was\n * always null in a 1:1 chat, and so was an agent turn's `untrusted.name`.\n */\nexport function chatMessageJson(message: StoredMessage, link: Pick<LinkView, \"peerNick\"> | undefined): MessageJson {\n const json = messageJson(message);\n if (json.nick || message.sender !== \"peer\") return json;\n const nick = link?.peerNick?.trim();\n return nick ? { ...json, nick } : json;\n}\n\n/**\n * A group's message with its author named: mesh and community messages are kept with the member's key only (the\n * name rides on the edge or the roster), so the roster fills `nick` when the message has none, and for an author no\n * longer in it (removed, or back with a new member key) the name the group knew them by.\n */\nexport function groupMessageJson(message: StoredMessage, group: Pick<GroupView, \"members\" | \"formerNames\"> | undefined): MessageJson {\n const json = messageJson(message);\n if (json.nick || !message.member) return json;\n const nick = group?.members.find((m) => m.key === message.member)?.nick ?? group?.formerNames?.[message.member];\n return nick ? { ...json, nick } : json;\n}\n\n/**\n * A group as the CLI prints it. The entry link lets anyone who reads it join, so it is `<hidden>` unless asked for\n * (`--show-secret`, or `group link`); null when the group has none.\n */\nexport function groupJson(group: GroupView, showSecret = false) {\n return {\n id: group.id,\n name: group.name,\n profile: group.profile,\n status: group.status ?? (group.invitation ? \"invited\" : null),\n statusReason: group.statusReason ?? null,\n admin: group.isAdmin,\n me: group.myKey ?? null,\n canSend: group.canSend,\n link: group.entryLink ? (showSecret ? group.entryLink : \"<hidden>\") : null,\n lastMessageAt: group.lastMessageAt,\n // Past 16 members with hubs (WISP 902 · Group Mesh § Hubs): who is one, who is reached through them, what the admin chose.\n members: group.members.map((m) => ({ key: m.key, name: m.nick ?? null, role: m.role, me: m.me, online: m.online,\n ...(m.hub ? { hub: true } : {}), ...(m.viaHub ? { viaHub: true } : {}), ...(m.hubRole ? { hubRole: m.hubRole } : {}) })),\n ...(group.hubs ? { hubs: group.hubs } : {}),\n invitation: group.invitation ? { chat: group.invitation.linkId, accepted: group.invitation.accepted, viaLink: !!group.invitation.viaLink, stage: group.invitation.stage ?? null } : null,\n community: group.community ?? null,\n picture: !!group.picture,\n };\n}\n","import { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { MAX_AVATAR_LENGTH, MAX_AVATAR_SIDE, MAX_GROUP_NAME_LENGTH, groupName, sanitizeAvatar } from \"@ghostly/core\";\nimport type { GroupView } from \"@ghostly/browser/shared/types\";\nimport { bool, chatOf, groupOf, node, state, str, type ApiContext, type Method, type Params } from \"./apiKit\";\nimport { CliError } from \"./errors\";\nimport { groupJson } from \"./views\";\n\n/**\n * Group administration and pictures (WISP 1100, phase 3): what the app's group menu and profile screen do. The\n * engine enforces who may do what (the admin); these check the arguments and name members.\n */\n\n/**\n * A member by key, unique key prefix or name; never this profile itself. A whole key is that member, whatever another\n * member calls itself; a prefix and a name stand side by side (two members is an error, not a guess).\n */\nexport function findMember(group: GroupView, ref: string) {\n const lower = ref.toLowerCase();\n const others = group.members.filter((m) => !m.me);\n const byKey = others.filter((m) => m.key === ref);\n const matches = byKey.length ? byKey : others.filter((m) => m.key.startsWith(ref) || m.nick?.trim().toLowerCase() === lower);\n if (matches.length === 1) return matches[0];\n throw new CliError(matches.length ? \"bad_request\" : \"not_found\", matches.length ? `${JSON.stringify(ref)} names more than one member` : `No member ${JSON.stringify(ref)} in ${group.name}`);\n}\n\n/**\n * A picture as Ghostly sends it: a square JPEG data URL, checked as a contact would check it. The app scales and\n * crops any image to 128×128; here the file must already be a JPEG within the bounds (no image library on Node).\n */\nexport async function pictureFrom(path: string): Promise<string> {\n let bytes: Buffer;\n try { bytes = await readFile(resolve(path)); } catch { throw new CliError(\"not_found\", `No file ${path}`); }\n const url = `data:image/jpeg;base64,${bytes.toString(\"base64\")}`;\n const checked = sanitizeAvatar(url);\n if (!checked) throw new CliError(\"bad_request\", `A picture is a JPEG of at most ${MAX_AVATAR_SIDE}×${MAX_AVATAR_SIDE} px and about ${Math.floor(MAX_AVATAR_LENGTH * 0.74 / 1000)} KB (128×128 is what the app sends): resize it first`);\n return checked;\n}\n\nasync function picture(params: Params): Promise<string | null> {\n if (bool(params, \"clear\")) return null;\n return pictureFrom(str(params, \"path\", true));\n}\n\nconst view = (ctx: ApiContext, id: string, params: Params) => groupJson(state(ctx).groups.find((g) => g.id === id) ?? groupOf(ctx, { group: id }), bool(params, \"showSecret\"));\n\nexport const GROUP_ADMIN_METHODS: Record<string, Method> = {\n async \"group.invite\"(ctx, params) {\n const group = groupOf(ctx, params);\n const link = chatOf(ctx, params);\n await node(ctx).inviteToGroup({ groupId: group.id, linkId: link.id });\n return view(ctx, group.id, params);\n },\n async \"group.remove\"(ctx, params) {\n const group = groupOf(ctx, params);\n await node(ctx).removeGroupMember({ groupId: group.id, key: findMember(group, str(params, \"member\", true)).key });\n return view(ctx, group.id, params);\n },\n async \"group.admin\"(ctx, params) {\n const group = groupOf(ctx, params);\n await node(ctx).makeGroupAdmin({ groupId: group.id, key: findMember(group, str(params, \"member\", true)).key });\n return view(ctx, group.id, params);\n },\n async \"group.hub\"(ctx, params) {\n const group = groupOf(ctx, params);\n const role = str(params, \"role\") ?? \"auto\";\n if (![\"pin\", \"exclude\", \"auto\"].includes(role)) throw new CliError(\"bad_request\", \"role is pin, exclude or auto\");\n await node(ctx).setGroupHub({ groupId: group.id, key: findMember(group, str(params, \"member\", true)).key, role: role === \"auto\" ? null : role as \"pin\" | \"exclude\" });\n return view(ctx, group.id, params);\n },\n async \"group.rename\"(ctx, params) {\n const group = groupOf(ctx, params);\n const name = groupName(str(params, \"name\", true));\n if (!name) throw new CliError(\"bad_request\", `A group's name is 1 to ${MAX_GROUP_NAME_LENGTH} characters on one line`);\n await node(ctx).renameGroup({ groupId: group.id, name });\n return view(ctx, group.id, params);\n },\n async \"group.rotate\"(ctx, params) {\n const group = groupOf(ctx, params);\n await node(ctx).rotateGroup({ groupId: group.id });\n return view(ctx, group.id, params);\n },\n async \"group.link\"(ctx, params) {\n const group = groupOf(ctx, params);\n if (params.on === false) {\n await node(ctx).disableGroupLink({ groupId: group.id });\n return { group: group.id, link: null };\n }\n const { link } = await node(ctx).enableGroupLink({ groupId: group.id, ...(bool(params, \"reset\") ? { reset: true } : {}) });\n return { group: group.id, link };\n },\n async \"group.picture\"(ctx, params) {\n const group = groupOf(ctx, params);\n await node(ctx).setGroupPicture({ groupId: group.id, picture: await picture(params) });\n return { group: group.id, picture: !bool(params, \"clear\") };\n },\n async \"profile.picture\"(ctx, params) {\n const avatar = await picture(params);\n await node(ctx).updateSettings({ settings: { avatar: avatar ?? \"\" } });\n return { picture: avatar !== null };\n },\n};\n","import { createHash } from \"node:crypto\";\nimport { chatOrGroup, findGroup, node, state, type ApiContext, type Params } from \"./apiKit\";\n\n/**\n * The id an agent turn names its message by (docs/CLI.md § Agent turns). A message is stored under an id its sender\n * chose most of (`peer_<the id they sent>`), so a turn carries this one instead, made here from the chat (or group) and\n * that id: `t_` and 32 hex digits, in which nobody can write a word. Every command that takes a message's id takes it.\n */\nexport function turnRef(where: string, messageId: string): string {\n return \"t_\" + createHash(\"sha256\").update(`${where}\\n${messageId}`).digest(\"hex\").slice(0, 32);\n}\n\nconst TURN_REF = /^t_[0-9a-f]{32}$/;\nconst isRef = (value: unknown): value is string => typeof value === \"string\" && TURN_REF.test(value);\n\n/**\n * `params` with each turn's id in `message`, `reply` and `messages` as the id its message is stored under: looked for\n * among the chat's (or group's) newest messages first, where a turn's almost always is, then in all of them. One that\n * names no message stays as given, and its command says so as for any unknown id.\n */\nexport async function withStoredIds(ctx: ApiContext, params: Params): Promise<Params> {\n const wanted = new Set([params.message, params.reply, ...(Array.isArray(params.messages) ? params.messages : [])].filter(isRef));\n if (!wanted.size) return params;\n let target: { id: string; linkId: string; group: boolean } | null = null;\n try {\n if (typeof params.group === \"string\") { const group = findGroup(state(ctx).groups, params.group); target = { id: group.id, linkId: `group:${group.id}`, group: true }; }\n else if (typeof params.chat === \"string\") target = chatOrGroup(ctx, params.chat);\n } catch { /* the command says what is wrong with its chat or group */ }\n if (!target) return params;\n const { id, linkId, group } = target;\n const found = new Map<string, string>();\n const look = (messages: readonly { id: string }[]) => {\n for (const message of messages) { const ref = turnRef(id, message.id); if (wanted.has(ref)) found.set(ref, message.id); }\n return found.size === wanted.size;\n };\n if (!look((await node(ctx).messagePage({ linkId, limit: 200 })).messages)) look(group ? await node(ctx).groupMessages({ groupId: id }) : await node(ctx).getMessages(linkId));\n const stored = (value: unknown) => (isRef(value) ? found.get(value) ?? value : value);\n return {\n ...params,\n ...(params.message !== undefined && { message: stored(params.message) }),\n ...(params.reply !== undefined && { reply: stored(params.reply) }),\n ...(Array.isArray(params.messages) && { messages: params.messages.map(stored) }),\n };\n}\n","import type { BitcoinScriptType } from \"@ghostly/core\";\n\n/**\n * Person-facing steps to sign Ghostly's proof statement for a Bitcoin address, per common wallet: which\n * menu, what to paste where, which format to pick. Ghostly never sees the wallet or its keys; the person\n * pastes the signature back.\n *\n * Only what each wallet's own documentation or UI shows is claimed here. Hardware wallets differ by model\n * and firmware (ADAPTER-ROADMAP.md, \"Hardware and signing\"); their entries point to the vendor's guide.\n */\n\nexport type BitcoinSigningFormat = \"bip322\" | \"legacy\";\n\nexport interface BitcoinWalletGuide {\n id: string;\n name: string;\n /** Address types this wallet can sign for in a format Ghostly accepts. */\n scripts: readonly BitcoinScriptType[];\n format: BitcoinSigningFormat;\n steps: readonly string[];\n /** How to use the test networks, for Testnet mode. */\n testnet?: string;\n note?: string;\n docs?: string;\n}\n\n/** What a proof proves and what it does not; shown next to every Bitcoin proof, given or received. */\nexport const BITCOIN_PROOF_LIMITS = [\n \"Proves: whoever made this signature could sign with the key behind this address when they signed.\",\n \"Does not prove the address holds any balance, now or ever.\",\n \"Does not prove they sent or received any past payment.\",\n \"Does not prove they would pay you, or spend from this address at all.\",\n] as const;\n\nexport const BITCOIN_WALLET_GUIDES: readonly BitcoinWalletGuide[] = [\n {\n id: \"sparrow\",\n name: \"Sparrow Wallet\",\n scripts: [\"p2wpkh\", \"p2tr\", \"p2pkh\"],\n format: \"bip322\",\n steps: [\n \"Open the wallet that holds the address, then Tools → Sign/Verify Message.\",\n \"Address: paste the address you are proving.\",\n \"Message: paste the statement Ghostly shows, exactly as it is.\",\n \"Format: BIP322 (Simple) for a bc1q… or bc1p… address (tb1… on test networks). For a legacy 1… address, Standard (Electrum).\",\n \"Sign Message, then copy the whole Signature (it starts with smp…) and paste it into Ghostly.\",\n ],\n testnet: \"Tools → Restart In → Testnet or Signet, with a wallet on that network.\",\n note: \"Sparrow offers BIP322 (Simple) for software wallets only; a connected hardware wallet signs the Standard (Electrum) format, which Ghostly accepts only for a legacy 1… address.\",\n docs: \"https://sparrowwallet.com/docs/\",\n },\n {\n id: \"bitcoin-core\",\n name: \"Bitcoin Core\",\n scripts: [\"p2pkh\"],\n format: \"legacy\",\n steps: [\n \"Bitcoin Core signs messages in the legacy format only, for a legacy address (1… on mainnet, m… or n… on test networks). Make one in the Receive tab with the Base58 (Legacy) type, or with `getnewaddress \\\"\\\" legacy`.\",\n \"GUI: File → Sign message…; paste the address, paste the statement, Sign Message, copy the signature.\",\n \"Command line: bitcoin-cli signmessage \\\"<address>\\\" '<statement>' (single quotes keep the statement's double quotes intact).\",\n ],\n testnet: \"Run it with -signet, -testnet4 or -regtest; add the same flag to bitcoin-cli.\",\n docs: \"https://developer.bitcoin.org/reference/rpc/signmessage.html\",\n },\n {\n id: \"electrum\",\n name: \"Electrum\",\n scripts: [\"p2pkh\"],\n format: \"legacy\",\n steps: [\n \"Electrum signs in the legacy format, which Ghostly accepts only for a legacy 1… address (m…/n… on test networks); its default bc1… wallets need another wallet.\",\n \"Addresses tab (View → Show Addresses), right-click the address → Sign/verify message.\",\n \"Paste the statement into Message, click Sign, copy the Signature and paste it into Ghostly.\",\n ],\n testnet: \"Start Electrum with --testnet or --signet.\",\n docs: \"https://electrum.readthedocs.io/\",\n },\n {\n id: \"coldcard\",\n name: \"COLDCARD\",\n scripts: [\"p2wpkh\", \"p2tr\", \"p2pkh\"],\n format: \"bip322\",\n steps: [\n \"Follow COLDCARD's BIP-322 guide for your firmware: it signs the statement for one of its addresses via MicroSD, NFC or QR, depending on the model.\",\n \"Give it the statement exactly as Ghostly shows it and the address you are proving.\",\n \"Paste the signature it produces into Ghostly.\",\n ],\n note: \"Older firmware signs only the legacy format (Ghostly accepts that for a legacy 1… address) and limits message length and characters.\",\n docs: \"https://coldcard.com/docs/bip322/\",\n },\n {\n id: \"trezor\",\n name: \"Trezor Suite\",\n scripts: [\"p2pkh\"],\n format: \"legacy\",\n steps: [\n \"Trezor signs messages in the legacy format; Ghostly accepts it for an address of a Legacy account (1…).\",\n \"Open the Legacy account and choose Sign & verify in its menu.\",\n \"Paste the statement into Message, pick the address, Sign, confirm on the device, copy the signature into Ghostly.\",\n ],\n },\n {\n id: \"other\",\n name: \"Another wallet\",\n scripts: [\"p2wpkh\", \"p2tr\", \"p2sh\", \"p2pkh\"],\n format: \"bip322\",\n steps: [\n \"Look for \\\"Sign message\\\" in the wallet. Choose BIP-322 when it asks for a format.\",\n \"Sign the statement exactly as Ghostly shows it, with the address you are proving.\",\n \"A nested SegWit address (3…, or 2… on test networks) needs a full BIP-322 signature (it starts with ful…).\",\n \"Paste the signature into Ghostly; it tells you right away if it does not match.\",\n ],\n },\n];\n\n/** The guides that can sign for an address of this type. */\nexport function guidesFor(script: BitcoinScriptType): BitcoinWalletGuide[] {\n return BITCOIN_WALLET_GUIDES.filter(g => g.scripts.includes(script));\n}\n","import { BitcoinAddressError, decodeAnyBitcoinAddress, verifyBitcoinMessage, type BitcoinMessageVerdict, type BitcoinProofNetwork, type BitcoinScriptType, type IdentityStatement } from \"@ghostly/core\";\nimport type { ExternalToolSigner, IdentityProofProvider, InstructionStep } from \"../contract\";\nimport { BITCOIN_PROOF_LIMITS, BITCOIN_WALLET_GUIDES, guidesFor, type BitcoinWalletGuide } from \"../bitcoinWallets\";\n\n/**\n * Bitcoin address proofs: the person signs the statement with the key behind an address, in their own\n * wallet, and pastes the signature. BIP-322 (simple or full) or, for a legacy P2PKH address only, the\n * legacy `signmessage` format; checked on this device by `verifyBitcoinMessage` (@ghostly/core, pinned to\n * BIP-322 2.0.0). No blockchain lookup: the proof says nothing about coins. See\n * docs/wisps/306-bitcoin.md.\n *\n * The address's own prefix says its network: a proof for a test-network address (tb1…, bcrt1…, m…, n…, 2…)\n * is verified as such and labelled \"test network\" wherever it is shown.\n *\n * TODO(ghostly-wallet signer): \"Sign with my Ghostly wallet\" belongs here as an `in-app` signer once an\n * on-chain source can sign messages. None can today: `OnchainProvider` (engine/paymentAdapters/providers/\n * onchain.ts) has no such method, Bitcoin Core's `signmessage` is legacy-only, and bdk-wallet-web has no\n * message signing. The BDK source keeps its phrase in the engine, so the path is an optional\n * `signMessage?(address, message)` on `OnchainProvider`, implemented in bdk.ts with a BIP-322 simple\n * witness for its own P2WPKH/P2TR address, exposed through an engine RPC that shows the statement and\n * asks first; never sign silently, and never hand the key to the UI.\n */\n\nexport interface BitcoinEvidence {\n /** As the wallet printed it: `smp…`/`ful…`, unprefixed BIP-322, or a legacy base64 signature. */\n signature: string;\n}\n\nconst MAX_SIGNATURE = 8192;\nconst SIGNATURE = /^(smp|ful|pof)?[A-Za-z0-9+/]+={0,2}$/;\n\nconst SCRIPT_NAMES: Record<BitcoinScriptType, string> = {\n p2wpkh: \"native SegWit (bc1q…)\", p2tr: \"Taproot (bc1p…)\", p2sh: \"nested SegWit (3…)\", p2pkh: \"legacy (1…)\",\n p2wsh: \"script / multisig (P2WSH)\", \"witness-unknown\": \"future SegWit version\",\n};\n\nfunction normalizeAddress(input: string): string {\n const value = input.trim();\n let info;\n try { info = decodeAnyBitcoinAddress(value); } catch (e) {\n throw Object.assign(new Error(e instanceof BitcoinAddressError ? e.message : \"Enter a Bitcoin address\"), { cause: e });\n }\n if (info.type === \"p2wsh\" || info.type === \"witness-unknown\")\n throw new Error(\"Ghostly can check proofs for single-key addresses only (bc1q…, bc1p…, 3…, 1…), not multisig or script addresses\");\n // Bech32 may be written in upper case; its canonical form is lower case. Base58 is case-sensitive.\n return info.witnessVersion === undefined ? value : value.toLowerCase();\n}\n\nconst isTestAddress = (address: string) => { try { return decodeAnyBitcoinAddress(address).network === \"testnet\"; } catch { return false; } };\n\nfunction shortAddress(address: string): string {\n return `${address.slice(0, address.startsWith(\"bcrt1\") ? 9 : 7)}…${address.slice(-4)}${isTestAddress(address) ? \" · test network\" : \"\"}`;\n}\n\n/** Wallet steps for this statement's address: the guide's own, or why this wallet cannot sign for it. */\nfunction walletInstructions(guide: BitcoinWalletGuide, statement: IdentityStatement) {\n const address = statement.binding.subject;\n const info = decodeAnyBitcoinAddress(address);\n const steps: InstructionStep[] = [];\n if (!guide.scripts.includes(info.type)) {\n const others = guidesFor(info.type).filter(g => g.id !== \"other\").map(g => g.name);\n steps.push({ text: `${guide.name} cannot sign for a ${SCRIPT_NAMES[info.type]} address in a format Ghostly accepts.${others.length ? ` Try ${others.join(\" or \")}, or another wallet that signs BIP-322.` : \" Use a wallet that signs BIP-322.\"}` });\n } else {\n steps.push({ text: \"Sign exactly this statement, as one line, without adding a line break:\", copy: statement.text });\n steps.push({ text: \"With this address:\", copy: address });\n for (const text of guide.steps) steps.push({ text });\n if (guide.id === \"bitcoin-core\") steps.push({ text: `Command line${info.network === \"testnet\" ? \" (add -signet, -testnet4 or -regtest)\" : \"\"}:`, copy: `bitcoin-cli signmessage \"${address}\" '${statement.text}'` });\n if (info.network === \"testnet\" && guide.testnet) steps.push({ text: `Test networks: ${guide.testnet}` });\n if (guide.note) steps.push({ text: guide.note });\n }\n steps.push({ text: \"This shows you can sign for the address. It does not show a balance, a past payment, or that you would pay.\" });\n return { steps, paste: { label: \"Signature\", placeholder: \"smp… (BIP-322) or the base64 signature your wallet shows\", multiline: true } };\n}\n\nfunction cleanSignature(pasted: string): string {\n const signature = pasted.replace(/\\s+/g, \"\");\n if (!signature) throw new Error(\"Paste the signature your wallet made\");\n if (signature.length > MAX_SIGNATURE || !SIGNATURE.test(signature)) throw new Error(\"That is not a Bitcoin message signature: paste the whole signature, as the wallet shows it\");\n return signature;\n}\n\nfunction check(statement: IdentityStatement, signature: string): { verdict: Extract<BitcoinMessageVerdict, { state: \"valid\" }>; network: BitcoinProofNetwork } {\n const address = statement.binding.subject;\n const network = decodeAnyBitcoinAddress(address).network;\n const verdict = verifyBitcoinMessage({ address, message: statement.text, signature, network });\n if (verdict.state !== \"valid\") throw new Error(verdict.reason);\n return { verdict, network };\n}\n\nfunction walletSigner(guide: BitcoinWalletGuide): ExternalToolSigner<BitcoinEvidence> {\n return {\n id: guide.id, kind: \"external-tool\", label: guide.name,\n description: guide.format === \"legacy\" ? \"Legacy message signature: for a legacy 1… address only.\" : \"BIP-322 message signature.\",\n instructions: statement => walletInstructions(guide, statement),\n parse(pasted, statement) {\n const signature = cleanSignature(pasted);\n // Say right away when it does not match, while the wallet is still open.\n check(statement, signature);\n return { signature };\n },\n };\n}\n\nexport const bitcoin: IdentityProofProvider<BitcoinEvidence> = {\n id: \"bitcoin\",\n label: \"Bitcoin address\",\n category: \"self-custodied\",\n summary: \"Sign a message in your own wallet\",\n description: \"Proves you can sign with the key behind a Bitcoin address: your wallet signs the statement and you paste the signature.\",\n limits: \"It does not prove a balance, a past payment, or that you would pay.\",\n platforms: [\"web\", \"extension\", \"desktop\"],\n subject: {\n label: \"Bitcoin address\",\n placeholder: \"bc1q… (tb1… on test networks)\",\n help: BITCOIN_PROOF_LIMITS.join(\" \"),\n normalize: normalizeAddress,\n short: shortAddress,\n },\n // BIP-21\n publicUri: address => `bitcoin:${address}`,\n validity: { defaultDays: 90, maxDays: 365 },\n privacy: \"Nothing: the signature is checked on this device, with no blockchain lookup. Contacts you share it with learn the address.\",\n signers: BITCOIN_WALLET_GUIDES.map(walletSigner),\n parseEvidence(raw) {\n const e = raw as BitcoinEvidence;\n if (!e || typeof e !== \"object\" || Array.isArray(e) || Object.keys(e).join(\",\") !== \"signature\" || typeof e.signature !== \"string\" ||\n e.signature.length > MAX_SIGNATURE || !SIGNATURE.test(e.signature)) throw new Error(\"That is not a Bitcoin message signature\");\n return { signature: e.signature };\n },\n async verify(statement, evidence) {\n const { verdict, network } = check(statement, evidence.signature);\n const format = verdict.format === \"legacy\" ? \"Legacy signmessage\" : verdict.format === \"bip322-full\" ? \"BIP-322 full\" : \"BIP-322 simple\";\n const script = { p2pkh: \"P2PKH\", p2wpkh: \"P2WPKH\", \"p2sh-p2wpkh\": \"P2SH-P2WPKH\", p2tr: \"P2TR\" }[verdict.script];\n return { subject: statement.binding.subject, source: `${format} signature, ${script}${network === \"testnet\" ? \", test network\" : \"\"}` };\n },\n};\n","import { BunkerSigner, type BunkerPointer } from 'nostr-tools/nip46';\nimport { SimplePool } from 'nostr-tools/pool';\nimport { generateSecretKey } from 'nostr-tools/pure';\nimport type { NostrProofEvent } from '@ghostly/core';\n\nexport interface NostrSigner {\n getPublicKey(): Promise<string>;\n signEvent(event: { kind: number; created_at: number; tags: string[][]; content: string }): Promise<NostrProofEvent>;\n}\nexport function extensionSigner(): NostrSigner | undefined {\n return (globalThis as typeof globalThis & { nostr?: NostrSigner }).nostr;\n}\n/** No NIP-05 lookup, arbitrary fetch, private key import or automatic relay switching. */\nexport function parseProofBunker(input: string): BunkerPointer {\n if (input.length > 4096) throw new Error('Bunker URL too long');\n let uri: URL;\n try { uri = new URL(input); } catch { throw new Error('Enter a bunker:// connection URL from your signer'); }\n const relays = uri.searchParams.getAll('relay');\n if (uri.protocol !== 'bunker:' || !/^[a-f0-9]{64}$/.test(uri.hostname) || uri.username || uri.password || uri.port || uri.hash || (uri.pathname && uri.pathname !== '/') ||\n relays.length < 1 || relays.length > 3) throw new Error('Invalid bunker connection URL');\n for (const address of relays) {\n const relay = new URL(address);\n if (relay.username || relay.password || relay.hash || !(relay.protocol === 'wss:' ||\n (relay.protocol === 'ws:' && ['127.0.0.1', 'localhost', '[::1]'].includes(relay.hostname))))\n throw new Error('Use secure wss:// relays (ws:// is allowed only on loopback for local testing)');\n }\n return { pubkey: uri.hostname, relays, secret: uri.searchParams.get('secret') };\n}\n/**\n * Runs `work` with the person's Nostr signer: the page's NIP-07 extension, or a NIP-46 bunker. `permissions`\n * is what the bunker is asked for (NIP-46 `connect`): the identity proof's `sign_event:30078` by default;\n * the social layer asks for the kinds it publishes.\n */\nexport async function withNostrSigner<T>(options: { bunker?: string; signal: AbortSignal; onAuth(url: string): void; permissions?: string; appName?: string }, work: (signer: NostrSigner) => Promise<T>): Promise<T> {\n options.signal.throwIfAborted();\n let signer: BunkerSigner | undefined;\n let pool: SimplePool | undefined;\n let clientKey: Uint8Array | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let abort: (() => void) | undefined;\n let closed = false;\n const guard: NostrSigner = {\n getPublicKey: async () => { if (closed) throw new Error('Signer request cancelled'); const key = await active!.getPublicKey(); if (closed) throw new Error('Signer request cancelled'); return key; },\n signEvent: async event => { if (closed) throw new Error('Signer request cancelled'); const signed = await active!.signEvent(event); if (closed) throw new Error('Signer request cancelled'); return signed; },\n };\n let active: NostrSigner | undefined;\n try {\n const timeout = new Promise<never>((_, reject) => {\n abort = () => { closed = true; reject(new Error('Signing cancelled')); };\n options.signal.addEventListener('abort', abort, { once: true });\n timer = setTimeout(() => { closed = true; reject(new Error('Signer timed out. Your Ghostly identity is unchanged.')); }, 120_000);\n });\n const action = async () => {\n if (options.bunker) {\n const bp = parseProofBunker(options.bunker);\n pool = new SimplePool(); clientKey = generateSecretKey();\n signer = BunkerSigner.fromBunker(clientKey, bp, { pool, skipSwitchRelays: true, onauth: url => {\n try { const u = new URL(url); if (!closed && u.protocol === 'https:' && !u.username && !u.password) options.onAuth(u.href); } catch { /* Invalid signer URL: never navigate. */ }\n } });\n await signer.sendRequest('connect', [bp.pubkey, bp.secret ?? '', options.permissions ?? 'sign_event:30078', JSON.stringify({ name: options.appName ?? 'Ghostly — optional conversation proof' })]);\n active = signer;\n } else {\n active = extensionSigner();\n if (!active) throw new Error('No NIP-07 signer in this window. Use a bunker connection from your Nostr signer.');\n }\n if (closed) throw new Error('Signer request cancelled');\n return work(guard);\n };\n return await Promise.race([action(), timeout]);\n } finally {\n closed = true;\n if (timer) clearTimeout(timer);\n if (abort) options.signal.removeEventListener('abort', abort);\n await signer?.close(); pool?.destroy(); clientKey?.fill(0);\n }\n}\n","import { schnorr } from \"@noble/curves/secp256k1.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { npubEncode, decode as nip19Decode } from \"nostr-tools/nip19\";\nimport { utf8Encode, type IdentityStatement } from \"@ghostly/core\";\nimport type { IdentityProofProvider, InAppSigner, SignerSession } from \"../contract\";\nimport { extensionSigner, withNostrSigner } from \"../nostr\";\n\n/** A signed Nostr event (NIP-01). The evidence of a Nostr proof. */\nexport interface NostrEvent { id: string; pubkey: string; created_at: number; kind: number; tags: string[][]; content: string; sig: string }\n\n/**\n * NIP-78 application data (kind 30078): no new kind is claimed. Signers show it as such; it is never\n * published to a relay — the event travels only to the contacts the person shares it with.\n */\nexport const NOSTR_PROOF_KIND = 30078;\nconst HEX64 = /^[a-f0-9]{64}$/;\n\n/** The exact event a signer is asked to sign: the statement as content, dated at issue, expiring with it. */\nexport function nostrProofTemplate(statement: IdentityStatement) {\n const { issuedAt, expiresAt } = statement.binding;\n return { kind: NOSTR_PROOF_KIND, created_at: issuedAt, content: statement.text,\n tags: [[\"d\", \"ghostly-identity-proof\"], [\"expiration\", String(expiresAt)]] };\n}\n\nconst hex = (b: Uint8Array) => Array.from(b, x => x.toString(16).padStart(2, \"0\")).join(\"\");\nconst unhex = (s: string) => Uint8Array.from(s.match(/../g)!, x => parseInt(x, 16));\n\nfunction normalizeNostrKey(input: string): string {\n const value = input.trim();\n if (HEX64.test(value.toLowerCase())) return value.toLowerCase();\n if (value.startsWith(\"npub1\")) {\n try { const decoded = nip19Decode(value); if (decoded.type === \"npub\") return decoded.data; } catch { /* fall through */ }\n }\n throw new Error(\"Enter a Nostr public key: npub1… or 64 hex characters\");\n}\n\nfunction nostrSigner(id: \"nip07\" | \"nip46\"): InAppSigner<NostrEvent> {\n return {\n id, kind: \"in-app\",\n label: id === \"nip07\" ? \"Browser extension (NIP-07)\" : \"Remote signer (NIP-46)\",\n description: id === \"nip07\" ? \"Approve in the Nostr extension of this browser.\" : \"Paste the bunker:// link from your signer app, then approve there.\",\n fields: id === \"nip46\" ? [{ name: \"bunker\", label: \"Signer connection link\", kind: \"secret\", placeholder: \"bunker://…\" }] : undefined,\n platforms: id === \"nip07\" ? [\"web\", \"desktop\"] : undefined,\n available: id === \"nip07\" ? () => !!extensionSigner() : undefined,\n run: (ctx, work) => withNostrSigner({ bunker: id === \"nip46\" ? ctx.values.bunker : undefined, signal: ctx.signal, onAuth: ctx.onAuthUrl }, signer => {\n const session: SignerSession<NostrEvent> = {\n subject: async () => normalizeNostrKey(await signer.getPublicKey()),\n sign: async statement => {\n ctx.onProgress(\"Approve the Ghostly identity statement in your signer.\");\n return (await signer.signEvent(nostrProofTemplate(statement))) as NostrEvent;\n },\n };\n return work(session);\n }),\n };\n}\n\nexport const nostr: IdentityProofProvider<NostrEvent> = {\n id: \"nostr\",\n label: \"Nostr\",\n category: \"self-custodied\",\n summary: \"Sign once with your Nostr signer\",\n description: \"Proves you hold a Nostr key: your signer signs the statement, your key never leaves it.\",\n platforms: [\"web\", \"extension\", \"desktop\"],\n subject: {\n label: \"Public key\", placeholder: \"npub1…\",\n normalize: normalizeNostrKey,\n short: key => { const npub = npubEncode(key); return `${npub.slice(0, 10)}…${npub.slice(-4)}`; },\n },\n // NIP-21\n publicUri: key => `nostr:${npubEncode(key)}`,\n validity: { defaultDays: 90, maxDays: 365 },\n privacy: \"Nothing: the signature is checked on this device. The proof is never published to a relay.\",\n signers: [nostrSigner(\"nip07\"), nostrSigner(\"nip46\")],\n parseEvidence(raw) {\n const e = raw as NostrEvent;\n if (!e || typeof e !== \"object\" || Array.isArray(e) || Object.keys(e).sort().join(\",\") !== \"content,created_at,id,kind,pubkey,sig,tags\" ||\n typeof e.id !== \"string\" || !HEX64.test(e.id) || typeof e.pubkey !== \"string\" || !HEX64.test(e.pubkey) ||\n typeof e.sig !== \"string\" || !/^[a-f0-9]{128}$/.test(e.sig) || !Number.isSafeInteger(e.kind) || !Number.isSafeInteger(e.created_at) ||\n typeof e.content !== \"string\" || e.content.length > 2048 || !Array.isArray(e.tags) || e.tags.length > 4 ||\n !e.tags.every(t => Array.isArray(t) && t.length <= 4 && t.every(x => typeof x === \"string\" && x.length <= 128)))\n throw new Error(\"That is not a signed Nostr event\");\n return { id: e.id, pubkey: e.pubkey, created_at: e.created_at, kind: e.kind, tags: e.tags.map(t => [...t]), content: e.content, sig: e.sig };\n },\n async verify(statement, event) {\n const expected = nostrProofTemplate(statement);\n if (event.pubkey !== statement.binding.subject) throw new Error(\"Signed by another Nostr key\");\n if (event.kind !== expected.kind || event.created_at !== expected.created_at || event.content !== expected.content ||\n JSON.stringify(event.tags) !== JSON.stringify(expected.tags)) throw new Error(\"The signer did not sign the exact Ghostly statement\");\n // NIP-01 id, recomputed here: never trust the id or a verification flag the event carries.\n const id = hex(sha256(utf8Encode(JSON.stringify([0, event.pubkey, event.created_at, event.kind, event.tags, event.content]))));\n if (event.id !== id) throw new Error(\"The Nostr event id does not match its content\");\n let valid: boolean;\n try { valid = schnorr.verify(unhex(event.sig), unhex(id), unhex(event.pubkey)); } catch { valid = false; }\n if (!valid) throw new Error(\"Invalid Nostr signature\");\n return { subject: event.pubkey, source: \"Nostr signature (NIP-01, BIP-340)\" };\n },\n // The public profile, follows and notes of a verified key are the Nostr social layer's\n // (engine/nostrSocial.ts): loaded on request from the relays the person configured, not here.\n};\n","import {\n DOMAIN_FILE_MAX_BYTES, decodeDnsResponse, dnsProofName, encodeDnsQuery, isPublicAddress, nip05RootKey, normalizeDomain,\n recordsFromTxt, recordsFromWellKnown, recordsName, toBase64Url, txtValue, wellKnownUrl,\n type DnsResponse, type DnsType, type DomainMethod, type DomainRecord,\n} from '@ghostly/core';\nimport type { IdentityFetch } from './contract';\n\n/**\n * Network side of domain proofs: DNS TXT through DNS over HTTPS and the\n * `/.well-known/` files, under strict limits. Nothing here trusts the prover:\n * the domain comes from the signed binding, the URLs are built here, every\n * answer is bounded and parsed strictly, and lookups are cached only briefly.\n *\n * What a check reveals, and to whom:\n * - DNS: the chosen resolver learns that this device looked up `_ghostly.<domain>`\n * (the next one in DOH_RESOLVERS too, when the chosen one gives no answer).\n * The domain's own servers see only the resolver.\n * - HTTPS / NIP-05: the resolver learns the domain (its addresses are looked up\n * first, to refuse private networks), and the domain's web server — run by\n * the person being checked — sees this device's IP address and when.\n */\n\nexport type DohResolverId = 'quad9' | 'cloudflare' | 'google';\nexport interface DohResolver { id: DohResolverId; name: string; url: string }\n/** Public resolvers that answer RFC 8484 wire-format GETs with CORS (checked 2026-09-23). */\nexport const DOH_RESOLVERS: readonly DohResolver[] = [\n { id: 'quad9', name: 'Quad9', url: 'https://dns.quad9.net/dns-query' },\n { id: 'cloudflare', name: 'Cloudflare', url: 'https://cloudflare-dns.com/dns-query' },\n { id: 'google', name: 'Google', url: 'https://dns.google/dns-query' },\n];\nexport const DEFAULT_RESOLVER: DohResolverId = 'quad9';\nexport const resolverById = (id: string | null | undefined): DohResolver =>\n DOH_RESOLVERS.find(r => r.id === id) ?? DOH_RESOLVERS.find(r => r.id === DEFAULT_RESOLVER)!;\n\n/** The person's resolver choice, per browser profile (UI and engine share the origin). */\nexport const RESOLVER_SETTING = 'ghostly-doh-resolver';\nexport function chosenResolver(): DohResolver {\n try { return resolverById(globalThis.localStorage?.getItem(RESOLVER_SETTING)); } catch { return resolverById(undefined); }\n}\nexport function chooseResolver(id: DohResolverId): void {\n try { globalThis.localStorage?.setItem(RESOLVER_SETTING, resolverById(id).id); } catch { /* not persisted: the default stays */ }\n}\n\nexport const DNS_MAX_BYTES = 16 * 1024;\n/**\n * Lookups are shared between checks for the DNS TTL, capped: never staler than the resolver itself would\n * be, so a re-check sees a removal as soon as DNS does. Files have no TTL and are only shared in flight.\n */\nconst CACHE_MAX_MS = 30_000, CACHE_FILE_MS = 0;\n\n/** The contract's `ctx.fetch`: HTTPS GET, no credentials, bounded, redirects refused unless asked, with raw bytes. */\nexport type DomainFetch = IdentityFetch;\n\nexport interface DomainLookupOptions {\n fetch: DomainFetch;\n resolver?: DohResolverId;\n signal?: AbortSignal;\n /** Milliseconds. */\n now?: () => number;\n}\n\n/** A check that did not confirm the record, with a message for people. */\nexport class DomainCheckError extends Error {}\n\n/** A resolver that gave no DNS answer at all: not reached, or it refused the request. The next one may answer. */\nclass ResolverUnavailable extends DomainCheckError {}\n\n/** One RFC 8484 GET to one resolver. The query has ID 0 and no client subnet, so it carries only the name. */\nasync function askResolver(resolver: DohResolver, name: string, type: DnsType, options: DomainLookupOptions): Promise<DnsResponse> {\n let response: Awaited<ReturnType<DomainFetch>>;\n try {\n response = await options.fetch(`${resolver.url}?dns=${toBase64Url(encodeDnsQuery(name, type))}`, {\n headers: { accept: 'application/dns-message' }, maxBytes: DNS_MAX_BYTES, signal: options.signal, redirect: 'error' });\n } catch (e) {\n throw /too large/i.test(String(e))\n ? new DomainCheckError(`${resolver.name} sent an answer that is too large`)\n : new ResolverUnavailable(`${resolver.name} could not be reached`);\n }\n if (response.status !== 200 || !/^application\\/dns-message\\b/i.test(response.contentType))\n throw new ResolverUnavailable(`${resolver.name} refused the lookup`);\n let answer: DnsResponse;\n try { answer = decodeDnsResponse(response.bytes, name, type); } catch { throw new DomainCheckError(`${resolver.name} sent an unusable answer`); }\n if (answer.rcode !== 0 && answer.rcode !== 3) throw new DomainCheckError(`${resolver.name} could not resolve ${name}`);\n return answer;\n}\n\n/**\n * A lookup through the chosen resolver, and through the others in turn (in list order) only when it gives no DNS\n * answer at all. Quad9, for one, cannot be read from WebKit (the Mac desktop app, Safari): its HTTP/3 answers carry\n * no CORS header. An answer, even \"no such name\" or a failure, is never asked again elsewhere. `resolver`: who answered.\n */\nexport async function dohQuery(name: string, type: DnsType, options: DomainLookupOptions): Promise<DnsResponse & { resolver: DohResolverId }> {\n const chosen = resolverById(options.resolver);\n let first: DomainCheckError | undefined;\n for (const resolver of [chosen, ...DOH_RESOLVERS.filter(r => r !== chosen)]) {\n try { return { ...await askResolver(resolver, name, type, options), resolver: resolver.id }; }\n catch (e) {\n if (!(e instanceof ResolverUnavailable) || options.signal?.aborted) throw e;\n first ??= e;\n }\n }\n throw first!;\n}\n\n/**\n * Refuses a domain whose addresses are not all public before its web server is\n * contacted, so a proof cannot make a contact's app probe their own network.\n * The browser resolves again when it fetches; a name that changes its answer in\n * between (rebinding) is not fully excluded by this check — see the draft.\n */\nexport async function assertPublicHost(domain: string, options: DomainLookupOptions): Promise<void> {\n const [v4, v6] = await Promise.all([dohQuery(domain, 'A', options), dohQuery(domain, 'AAAA', options)]);\n const addresses = [...v4.answers, ...v6.answers].map(a => a.data);\n if (!addresses.length) throw new DomainCheckError(`${domain} has no address`);\n if (!addresses.every(isPublicAddress)) throw new DomainCheckError(`${domain} points to a private network address, so it was not contacted`);\n}\n\n/** A well-known file: https only, no redirects, bounded. `undefined` when the server says it is not there. */\nexport async function fetchWellKnown(domain: string, method: 'https' | 'nip05', options: DomainLookupOptions): Promise<string | undefined> {\n await assertPublicHost(domain, options);\n const url = wellKnownUrl(domain, method);\n let response: Awaited<ReturnType<DomainFetch>>;\n try {\n response = await options.fetch(url, { headers: { accept: 'application/json' }, maxBytes: DOMAIN_FILE_MAX_BYTES, signal: options.signal, redirect: 'error' });\n } catch (e) {\n throw new DomainCheckError(/too large/i.test(String(e)) ? `${url} is too large`\n : `${domain} could not be reached. The file must be served at exactly this address, without a redirect, with the header Access-Control-Allow-Origin: *`);\n }\n if (response.status === 404 || response.status === 410) return;\n if (response.status >= 300 && response.status < 400) throw new DomainCheckError(`${domain} answered with a redirect; the file must be served at exactly ${url}`);\n if (response.status !== 200) throw new DomainCheckError(`${domain} answered ${response.status}`);\n try { return new TextDecoder('utf-8', { fatal: true }).decode(response.bytes); } catch { throw new DomainCheckError(`${url} is not text`); }\n}\n\nexport interface DomainLookup {\n domain: string;\n method: DomainMethod;\n /** DNS / HTTPS: the Ghostly records found. */\n records: DomainRecord[];\n /** NIP-05: the Nostr key named for the domain itself. */\n nostrKey?: string;\n /** A record or file exists at all (as opposed to nothing there). */\n found: boolean;\n /** The resolver reported DNSSEC validation of the TXT answer. */\n dnssec: boolean;\n resolver: DohResolverId;\n}\n\nasync function lookup(domain: string, method: DomainMethod, options: DomainLookupOptions): Promise<DomainLookup & { ttlMs: number }> {\n const resolver = resolverById(options.resolver).id;\n if (method === 'dns') {\n const answer = await dohQuery(dnsProofName(domain), 'TXT', options);\n const values: string[] = [];\n for (const record of answer.answers.slice(0, 32)) { try { values.push(txtValue(record.data)); } catch { /* not text: not ours */ } }\n const ttlMs = answer.answers.length ? Math.min(...answer.answers.map(a => a.ttl)) * 1000 : 0;\n return { domain, method, resolver: answer.resolver, records: recordsFromTxt(values), found: answer.answers.length > 0, dnssec: answer.authenticated, ttlMs };\n }\n const text = await fetchWellKnown(domain, method, options);\n const base = { domain, method, resolver, dnssec: false, records: [] as DomainRecord[], ttlMs: CACHE_FILE_MS };\n if (text === undefined) return { ...base, found: false };\n try {\n return method === 'nip05' ? { ...base, found: true, nostrKey: nip05RootKey(text) } : { ...base, found: true, records: recordsFromWellKnown(text) };\n } catch { throw new DomainCheckError(`${wellKnownUrl(domain, method)} is not a valid ${method === 'nip05' ? 'NIP-05' : 'Ghostly'} file`); }\n}\n\nconst cache = new Map<string, { until: number; result: Promise<DomainLookup & { ttlMs: number }> }>();\n/** Tests only. */\nexport function clearDomainCache(): void { cache.clear(); }\n\n/** What a domain publishes by one method. Concurrent and back-to-back checks share one lookup. */\nexport async function lookupDomain(domainInput: string, method: DomainMethod, options: DomainLookupOptions): Promise<DomainLookup> {\n const domain = normalizeDomain(domainInput), now = options.now ?? Date.now, resolver = resolverById(options.resolver).id;\n const id = `${resolver}|${method}|${domain}`;\n const hit = cache.get(id);\n if (hit && hit.until > now()) { const { ttlMs: _ttl, ...result } = await hit.result; return result; }\n const started = now();\n const entry = { until: started + CACHE_MAX_MS, result: lookup(domain, method, { ...options, resolver }) };\n cache.set(id, entry);\n if (cache.size > 128) cache.delete(cache.keys().next().value!);\n try {\n const { ttlMs, ...result } = await entry.result;\n entry.until = started + Math.min(CACHE_MAX_MS, ttlMs);\n return result;\n } catch (e) {\n if (cache.get(id) === entry) cache.delete(id);\n throw e;\n }\n}\n\n/** Throws unless `domain` publishes `record` by `method` (DNS or HTTPS). Returns what was found. */\nexport async function assertDomainRecord(domain: string, method: 'dns' | 'https', record: DomainRecord, options: DomainLookupOptions): Promise<DomainLookup> {\n const found = await lookupDomain(domain, method, options);\n if (recordsName(found.records, record)) return found;\n const where = method === 'dns' ? `The TXT record at ${dnsProofName(domain)}` : wellKnownUrl(domain);\n if (found.records.some(r => r.key === record.key)) throw new DomainCheckError(`${where} is for another proof of this key`);\n if (found.records.length) throw new DomainCheckError(`${where} names another Ghostly key: this proof was replaced or removed`);\n throw new DomainCheckError(method === 'dns'\n ? `No Ghostly TXT record at ${dnsProofName(domain)}${found.found ? '' : ' (DNS changes can take a few minutes)'}`\n : found.found ? `${wellKnownUrl(domain)} names no Ghostly proof` : `${wellKnownUrl(domain)} was not found`);\n}\n\n/** Throws unless the domain's NIP-05 root name is `nostrKey`. */\nexport async function assertNip05(domain: string, nostrKey: string, options: DomainLookupOptions): Promise<DomainLookup> {\n const found = await lookupDomain(domain, 'nip05', options);\n if (found.nostrKey === nostrKey) return found;\n const url = wellKnownUrl(domain, 'nip05');\n throw new DomainCheckError(!found.found ? `${url} was not found` : found.nostrKey\n ? `${url} names another Nostr key for ${domain}` : `${url} has no root name \"_\" for ${domain}`);\n}\n","import { dnsProofName, domainTxtRecord, domainWellKnownFile, normalizeDomain, wellKnownUrl, type DomainRecord, type IdentityStatement } from \"@ghostly/core\";\nimport type { IdentityProofProvider, InAppSigner, PublishSigner, SignerSession } from \"../contract\";\nimport { assertDomainRecord, assertNip05, chosenResolver, resolverById } from \"../domain\";\nimport { extensionSigner, withNostrSigner } from \"../nostr\";\nimport { nostr, nostrProofTemplate, type NostrEvent } from \"./nostr\";\n\n/**\n * A domain the person controls (draft WISP 304, docs/wisps/304-domain.md).\n *\n * The statement (\"I control domain:example.com and authorize the Ghostly key K …\") is vouched for by\n * publishing, once, under the domain: a TXT record at `_ghostly.<domain>` or the file\n * `/.well-known/ghostly.json`, naming the proof key K and the statement id. The id commits the record to\n * the whole statement (dates and nonce too), so one published record vouches for exactly one proof. The\n * record names no contact and no conversation; sharing per contact is the contract's presentation.\n *\n * People who already run NIP-05 can instead sign the statement with the Nostr key their\n * `/.well-known/nostr.json` names for the domain itself (`_`): the proof holds only if both agree.\n */\nexport type DomainEvidence = { method: \"dns\" } | { method: \"https\" } | { method: \"nip05\"; event: NostrEvent };\n\nconst recordFor = (s: IdentityStatement): DomainRecord => ({ key: s.binding.key, proof: s.id });\nconst exactKeys = (raw: unknown, keys: string) => !!raw && typeof raw === \"object\" && !Array.isArray(raw) && Object.keys(raw).sort().join(\",\") === keys;\n\nconst dnsSigner: PublishSigner<DomainEvidence> = {\n id: \"dns\", kind: \"publish\", label: \"DNS TXT record\",\n description: \"Add a TXT record at your DNS host. Contacts' apps look it up through a DNS-over-HTTPS resolver.\",\n instructions(statement) {\n const domain = statement.binding.subject;\n return { steps: [\n { text: `Open the DNS settings of ${domain} at your registrar or DNS host and add a TXT record with this name. Some hosts add the domain themselves: then enter only the part before ${domain}.`, copy: dnsProofName(domain) },\n { text: \"Value (one string, exactly as shown):\", copy: domainTxtRecord(recordFor(statement)) },\n { text: \"Set the TTL to 300 seconds or less, so removing the record takes effect quickly. Then press Check: DNS changes can take a few minutes to appear.\" },\n { text: \"The record stays public while the proof is in use. Removing it withdraws this proof from every contact at their next check.\" },\n ] };\n },\n evidence: () => ({ method: \"dns\" }),\n};\n\nconst httpsSigner: PublishSigner<DomainEvidence> = {\n id: \"https\", kind: \"publish\", label: \"File on your website\",\n description: \"Upload a small file to your website. Contacts' apps download it from your server.\",\n instructions(statement) {\n const domain = statement.binding.subject;\n return { steps: [\n { text: `Create this file on the web server of ${domain}:`, copy: wellKnownUrl(domain) },\n { text: \"With exactly this content:\", copy: domainWellKnownFile([recordFor(statement)]) },\n { text: \"Serve it over HTTPS at exactly that address, without any redirect (not even to www), and with this header so browsers may read it:\", copy: \"Access-Control-Allow-Origin: *\" },\n { text: \"Then press Check. The file stays public while the proof is in use; deleting it withdraws this proof from every contact at their next check.\" },\n ] };\n },\n evidence: () => ({ method: \"https\" }),\n};\n\nconst domainField = { name: \"domain\", label: \"Domain\", kind: \"text\", placeholder: \"example.com\",\n help: \"Its /.well-known/nostr.json must name this Nostr key for \\\"_\\\" (the domain itself).\" } as const;\n\n/** NIP-05: the statement is signed by the Nostr key the domain's nostr.json names for itself. */\nfunction nip05Signer(id: \"nip05-nip07\" | \"nip05-nip46\"): InAppSigner<DomainEvidence> {\n const extension = id === \"nip05-nip07\";\n return {\n id, kind: \"in-app\",\n label: extension ? \"NIP-05 with a browser extension\" : \"NIP-05 with a remote signer\",\n description: \"Already run NIP-05? Sign with the Nostr key your domain's nostr.json names for itself. No new record needed.\",\n fields: extension ? [domainField] : [domainField, { name: \"bunker\", label: \"Signer connection link\", kind: \"secret\", placeholder: \"bunker://…\" }],\n platforms: extension ? [\"web\", \"desktop\"] : undefined,\n available: extension ? () => !!extensionSigner() : undefined,\n run: (ctx, work) => {\n const domain = normalizeDomain(ctx.values.domain ?? \"\");\n return withNostrSigner({ bunker: extension ? undefined : ctx.values.bunker, signal: ctx.signal, onAuth: ctx.onAuthUrl }, signer => {\n const session: SignerSession<DomainEvidence> = {\n subject: async () => domain,\n sign: async statement => {\n ctx.onProgress(\"Approve the Ghostly identity statement in your signer.\");\n return { method: \"nip05\", event: (await signer.signEvent(nostrProofTemplate(statement))) as NostrEvent };\n },\n };\n return work(session);\n });\n },\n };\n}\n\nexport const domain: IdentityProofProvider<DomainEvidence> = {\n id: \"domain\",\n label: \"Domain\",\n category: \"self-custodied\",\n summary: \"A DNS record or a file on your site\",\n description: \"Proves you control a domain name: publish a DNS TXT record or a file on its website, once. Contacts' apps look it up when you share it and can check again later.\",\n limits: \"Does not prove who runs the domain, only that whoever added the proof could publish under it.\",\n platforms: [\"web\", \"extension\", \"desktop\"],\n subject: {\n label: \"Domain\", placeholder: \"example.com\",\n help: \"A domain or subdomain you control. A proof for alice.example.com is published under alice.example.com.\",\n normalize: normalizeDomain,\n },\n publicUri: domain => `https://${domain}`,\n validity: { defaultDays: 90, maxDays: 365 },\n recheck: { afterSeconds: 86_400 },\n privacy: \"DNS: the contact's app asks the DNS-over-HTTPS resolver chosen in its settings (Quad9 by default; the next of Quad9, Cloudflare and Google when it cannot be reached) for the record, \" +\n \"so that resolver learns the domain was checked. Website or NIP-05: the resolver learns the domain, and the domain's web server sees the contact's IP address.\",\n signers: [dnsSigner, httpsSigner, nip05Signer(\"nip05-nip07\"), nip05Signer(\"nip05-nip46\")],\n parseEvidence(raw) {\n const e = raw as DomainEvidence;\n if (exactKeys(raw, \"method\") && (e.method === \"dns\" || e.method === \"https\")) return { method: e.method };\n if (exactKeys(raw, \"event,method\") && e.method === \"nip05\") return { method: \"nip05\", event: nostr.parseEvidence((e as { event: unknown }).event) };\n throw new Error(\"That is not domain proof evidence\");\n },\n async verify(statement, evidence, ctx) {\n const subject = statement.binding.subject;\n if (normalizeDomain(subject) !== subject) throw new Error(\"The domain is not in canonical form\");\n const resolver = chosenResolver();\n const options = { fetch: ctx.fetch, signal: ctx.signal, resolver: resolver.id, now: () => ctx.now * 1000 };\n if (evidence.method === \"nip05\") {\n // The same checks as a Nostr proof, for the key that signed; then the domain must name that key.\n await nostr.verify({ ...statement, binding: { ...statement.binding, subject: evidence.event.pubkey } }, evidence.event, ctx);\n await assertNip05(subject, evidence.event.pubkey, options);\n return { subject, source: `Nostr signature by the key ${wellKnownUrl(subject, \"nip05\")} names for ${subject}` };\n }\n const found = await assertDomainRecord(subject, evidence.method, recordFor(statement), options);\n return { subject, source: evidence.method === \"dns\"\n ? `DNS TXT record at ${dnsProofName(subject)}, via ${resolverById(found.resolver).name}${found.dnssec ? \", DNSSEC validated\" : \"\"}`\n : `File at ${wellKnownUrl(subject)}` };\n },\n};\n","import { fromBase64Url, toBase64Url, utf8Encode } from '@ghostly/core';\nimport type * as OpenPGP from 'openpgp';\n\n/**\n * OpenPGP identity proofs (RFC 9580, and the RFC 4880 v4 keys people actually have).\n *\n * The person signs a Ghostly statement once — the binding by which their\n * OpenPGP key authorizes a Ghostly proof key (see PROOFS.md); each chat\n * then gets a presentation the app signs itself. They sign with their own tooling —\n * `gpg --clearsign`, `gpg --detach-sign --armor`, a YubiKey or another OpenPGP\n * card behind gpg — and pastes the result with their public key. No secret key\n * ever enters Ghostly: a pasted private key is refused before it is parsed.\n *\n * What is shared with a contact is small and self-contained: one signature\n * packet and a minimal copy of the public key (the primary key, its user IDs\n * with their self-signatures, the subkey that signed). The contact verifies it\n * locally with the statement their own app builds; nothing is fetched.\n *\n * A key's user IDs are claims its holder wrote. Possession of the key proves\n * control of the key, not that a name or an email in a user ID belongs to the\n * person — the UI must say so wherever user IDs are shown.\n *\n * OpenPGP.js is loaded lazily (the \"lightweight\" build: ~233 KB minified,\n * ~64 KB gzipped, plus a ~46 KB curve chunk only for ECDSA keys), so nobody\n * pays for it until an OpenPGP proof is opened.\n */\n\nexport const PGP_SCHEME = 'openpgp/1';\n\n/** Strict size limits. Pasted input is the person's own; shared input comes from a peer. */\nexport const PGP_LIMITS = {\n /** A clearsigned statement or an armored detached signature. RSA-4096 with the statement is ~1.5 KB. */\n pastedSignature: 16 * 1024,\n /** An armored public key as exported, before it is minimized. Flooded keys are refused, not trimmed. */\n pastedKey: 256 * 1024,\n /** One signature packet, binary. RSA-4096 with the usual subpackets is ~600 bytes. */\n sharedSignature: 1536,\n /** The minimal key, binary. Both fit, base64url, in the contract's 16 KiB of evidence JSON. */\n sharedKey: 9 * 1024,\n /** User IDs kept in the minimal key and shown. */\n userIds: 16,\n /** Bytes per user ID. */\n userIdLength: 256,\n /** A keyserver response. */\n keyserverResponse: 256 * 1024,\n} as const;\n\n/** Clock difference tolerated between the signer's machine and the verifier. */\nexport const PGP_CLOCK_SKEW = 300;\n\nexport interface PgpEvidence {\n scheme: typeof PGP_SCHEME;\n /** base64url of the minimal transferable public key (binary packets). */\n key: string;\n /** base64url of exactly one signature packet over the statement. */\n signature: string;\n}\n\nexport interface PgpKeyInfo {\n /** Primary key fingerprint, uppercase hex (40 characters for v4, 64 for v6). */\n fingerprint: string;\n /** The key that signs: the primary key or a signing subkey. */\n signingFingerprint: string;\n version: 4 | 6;\n /** \"Ed25519\", \"RSA 3072\", \"ECDSA P-256\", … for the signing key. */\n algorithm: string;\n createdAt: number;\n /** Seconds, or null when the key does not expire. */\n expiresAt: number | null;\n /** Self-certified, unrevoked user IDs: claims by the key holder, not verified facts. */\n userIds: string[];\n}\n\nexport interface PgpVerified extends PgpKeyInfo {\n /** Signature creation time, seconds. */\n signedAt: number;\n}\n\nexport interface PgpVerifyOptions {\n /** Verification time, seconds. The key and its signing subkey must be valid now. */\n now?: number;\n /** Refuse signatures made before this time (seconds), e.g. the start of the binding's validity. */\n signedAfter?: number;\n /** The primary key fingerprint the statement names: any other key is refused, even with a valid signature. */\n expectedFingerprint?: string;\n}\n\ntype Pgp = typeof OpenPGP;\nlet loading: Promise<Pgp> | undefined;\n/** The only place OpenPGP.js is imported: a separate chunk, fetched on first use. */\nexport function loadOpenPgp(): Promise<Pgp> {\n loading ??= import('openpgp/lightweight').then(m => m as unknown as Pgp).catch(e => { loading = undefined; throw e; });\n return loading;\n}\n\nfunction config(pgp: Pgp): OpenPGP.PartialConfig {\n const { hash, publicKey, curve } = pgp.enums;\n return {\n minRSABits: 2048,\n maxUserIDLength: PGP_LIMITS.userIdLength,\n enableParsingV5Entities: false,\n ignoreUnsupportedPackets: true,\n ignoreMalformedPackets: false,\n // Self-signatures from older keys may still use SHA-1; the proof signature may not (below).\n rejectHashAlgorithms: new Set([hash.md5, hash.ripemd]),\n rejectMessageHashAlgorithms: new Set([hash.md5, hash.ripemd, hash.sha1]),\n rejectPublicKeyAlgorithms: new Set([publicKey.elgamal, publicKey.dsa]),\n rejectCurves: new Set([curve.secp256k1]),\n };\n}\n\n/** The allowlist, checked on the primary key and on the key that signs. */\nfunction allowedAlgorithm(packet: OpenPGP.PublicKeyPacket | OpenPGP.SecretKeyPacket | OpenPGP.PublicSubkeyPacket | OpenPGP.SecretSubkeyPacket, signing: boolean): string {\n const { algorithm, bits, curve } = packet.getAlgorithmInfo();\n const names: Record<string, string> = { nistP256: 'P-256', nistP384: 'P-384', nistP521: 'P-521',\n brainpoolP256r1: 'brainpoolP256r1', brainpoolP384r1: 'brainpoolP384r1', brainpoolP512r1: 'brainpoolP512r1' };\n if ((algorithm === 'rsaEncryptSign' || (algorithm === 'rsaSign' && signing)) && bits && bits >= 2048) return `RSA ${bits}`;\n if (algorithm === 'ed25519' || (algorithm === 'eddsaLegacy' && curve === 'ed25519Legacy')) return 'Ed25519';\n if (algorithm === 'ed448') return 'Ed448';\n if (algorithm === 'ecdsa' && curve && names[curve]) return `ECDSA ${names[curve]}`;\n const shown = algorithm.startsWith('rsa') ? `RSA ${bits ?? '?'}` : curve ? `${algorithm} ${curve}` : algorithm;\n throw new PgpProofError(`This key uses ${shown}, which Ghostly does not accept. Use Ed25519, ECDSA (P-256, P-384, P-521, brainpool) or RSA with at least 2048 bits.`);\n}\nconst ALLOWED_HASHES = new Set([8, 9, 10, 11, 12, 14]); // SHA-256, -384, -512, -224, SHA3-256, SHA3-512\n\nexport class PgpProofError extends Error { override name = 'PgpProofError'; }\n\n/** Armored blocks in pasted text; more BEGIN lines than any real paste has are refused before the regex scans. */\nfunction armorBlocks(text: string): RegExpExecArray[] {\n if ((text.match(/-----BEGIN PGP /g) ?? []).length > 4) throw new PgpProofError('Paste only what gpg printed: one signature and your public key.');\n return [...text.matchAll(ARMOR)];\n}\n// A clearsigned message has no END line of its own: it ends with its signature block.\nconst ARMOR = /-----BEGIN PGP SIGNED MESSAGE-----[\\s\\S]*?-----END PGP SIGNATURE-----|-----BEGIN PGP ([A-Z ,/0-9]+)-----[\\s\\S]*?-----END PGP \\1-----/g;\n/** The single armored block in pasted text (terminal output may surround it). */\nfunction armoredBlock(text: string, what: 'key' | 'signature'): { type: string; block: string } {\n if (/PRIVATE KEY BLOCK/.test(text))\n throw new PgpProofError('This is a PRIVATE key. Never paste it anywhere: Ghostly only needs your public key (gpg --armor --export).');\n const blocks = armorBlocks(text);\n if (blocks.length === 0) throw new PgpProofError(what === 'key' ? 'Paste an armored public key: it starts with -----BEGIN PGP PUBLIC KEY BLOCK-----.' : 'Paste the whole signed text: it starts with -----BEGIN PGP SIGNED MESSAGE----- (or -----BEGIN PGP SIGNATURE----- for a detached signature).');\n if (blocks.length > 1) throw new PgpProofError(`Paste only one ${what === 'key' ? 'public key' : 'signature'}.`);\n return { type: blocks[0][1] ?? 'SIGNED MESSAGE', block: blocks[0][0] };\n}\n\nfunction secondsOf(date: Date | number | null | typeof Infinity): number | null {\n if (date === null || date === Infinity) return null;\n return Math.floor((date instanceof Date ? date.getTime() : date) / 1000);\n}\nconst hexOf = (bytes: Uint8Array) => Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('').toUpperCase();\nconst nowSeconds = () => Math.floor(Date.now() / 1000);\n/** Clearsigned text as it is hashed: trailing whitespace dropped, no final newline. */\nexport function canonicalStatement(statement: string): string {\n return statement.replace(/\\r\\n?/g, '\\n').split('\\n').map(l => l.replace(/[ \\t]+$/, '')).join('\\n').replace(/\\n+$/, '');\n}\n\n/**\n * One paste holding what gpg printed: the signed statement (clearsigned, or a detached signature) and,\n * optionally, the public key. Anything around the blocks (prompts, blank lines) is ignored.\n */\nexport function splitPgpPaste(pasted: string): { signature: string; publicKey?: string } {\n if (typeof pasted !== 'string' || pasted.length > PGP_LIMITS.pastedKey + PGP_LIMITS.pastedSignature) throw new PgpProofError('This is too large. Paste only what gpg printed.');\n if (/PRIVATE KEY BLOCK/.test(pasted)) throw new PgpProofError('This is a PRIVATE key. Never paste it anywhere: Ghostly only needs your public key (gpg --armor --export).');\n const blocks = armorBlocks(pasted).map(m => ({ type: m[1] ?? 'SIGNED MESSAGE', block: m[0] }));\n const keys = blocks.filter(b => b.type === 'PUBLIC KEY BLOCK'), others = blocks.filter(b => b.type !== 'PUBLIC KEY BLOCK');\n if (keys.length > 1) throw new PgpProofError('Paste only one public key.');\n if (others.length === 0) throw new PgpProofError('Paste the signed statement too: it starts with -----BEGIN PGP SIGNED MESSAGE-----.');\n if (others.length > 1) throw new PgpProofError('Paste only one signature.');\n return { signature: others[0].block, publicKey: keys[0]?.block };\n}\n\n/** Refuses a statement that `gpg --clearsign` would alter (dash-escaping, trailing spaces) or that is not plain text. */\nexport function assertSignableStatement(statement: string): void {\n if (!statement || statement.length > 4096 || statement !== canonicalStatement(statement) || /^-/m.test(statement) || /[^\\x20-\\x7e\\n]/.test(statement))\n throw new PgpProofError('Statement is not signable as plain text');\n}\n\n/** Why a key or a subkey is not usable now, in words the person can act on. */\nasync function explainInvalidKey(key: OpenPGP.PublicKey, keyId: OpenPGP.KeyID | undefined, at: Date, cfg: OpenPGP.PartialConfig, when: 'now' | 'signing'): Promise<never> {\n const past = when === 'signing' ? ' when it signed' : '';\n if (await key.isRevoked(undefined, undefined, at, cfg as OpenPGP.Config).catch(() => false))\n throw new PgpProofError(`This key has been revoked${past}. A revoked key proves nothing.`);\n try { await key.verifyPrimaryKey(at, undefined, cfg as OpenPGP.Config); }\n catch (e) {\n const expiry = await key.getExpirationTime(undefined, cfg as OpenPGP.Config).catch(() => null);\n if (expiry instanceof Date && expiry <= at) throw new PgpProofError(`This key expired on ${expiry.toISOString().slice(0, 10)}${past}. Extend its expiry (gpg --quick-set-expire) and export it again.`);\n throw new PgpProofError(`This key is not valid${past}: ${message(e)}`);\n }\n const sub = keyId ? key.getSubkeys(keyId)[0] : undefined;\n if (sub) {\n if (sub.revocationSignatures.length && await sub.verify(at, cfg as OpenPGP.Config).then(() => false, e => /revoked/i.test(message(e))))\n throw new PgpProofError(`The subkey that signed (${sub.getFingerprint().toUpperCase()}) has been revoked${past}.`);\n const expiry = await sub.getExpirationTime(at, cfg as OpenPGP.Config).catch(() => null);\n if (expiry instanceof Date && expiry <= at) throw new PgpProofError(`The subkey that signed expired on ${expiry.toISOString().slice(0, 10)}${past}.`);\n throw new PgpProofError(`The subkey that signed is not a valid signing subkey${past}.`);\n }\n throw new PgpProofError(`The key that signed is not a valid signing key${past}.`);\n}\nconst message = (e: unknown) => e instanceof Error ? e.message : String(e);\n\n/** Parses exactly one public key; refuses private keys, v3/v5 keys and key rings. */\nasync function readOneKey(pgp: Pgp, input: { armored: string } | { binary: Uint8Array }): Promise<OpenPGP.PublicKey> {\n const cfg = config(pgp);\n let keys: OpenPGP.Key[];\n try {\n keys = 'armored' in input ? await pgp.readKeys({ armoredKeys: input.armored, config: cfg }) : await pgp.readKeys({ binaryKeys: input.binary, config: cfg });\n } catch (e) { throw new PgpProofError(`Could not read this public key: ${message(e)}`); }\n if (keys.length !== 1) throw new PgpProofError('Paste one public key, not a key ring.');\n const [key] = keys;\n if (key.isPrivate()) throw new PgpProofError('This is a PRIVATE key. Ghostly only needs your public key (gpg --armor --export).');\n const version = key.keyPacket.version;\n if (version !== 4 && version !== 6) throw new PgpProofError(`OpenPGP v${version} keys are not accepted; use a v4 (RFC 4880) or v6 (RFC 9580) key.`);\n return key as OpenPGP.PublicKey;\n}\n\n/** Valid, unrevoked, self-certified user IDs at `at`. */\nasync function validUserIds(key: OpenPGP.PublicKey, at: Date, cfg: OpenPGP.PartialConfig): Promise<OpenPGP.User[]> {\n const valid: OpenPGP.User[] = [];\n for (const user of key.users) {\n if (!user.userID || valid.length >= PGP_LIMITS.userIds) continue;\n if (await user.verify(at, cfg as OpenPGP.Config).then(() => true, () => false)) valid.push(user);\n }\n return valid;\n}\n\n/**\n * The transferable key a contact receives: primary key and its direct/revocation\n * signatures, valid user IDs with self-certifications only (third-party\n * certifications and photos dropped), and the one subkey that signed.\n */\nasync function minimalKey(pgp: Pgp, key: OpenPGP.PublicKey, signer: OpenPGP.PublicKey | OpenPGP.Subkey, at: Date, cfg: OpenPGP.PartialConfig): Promise<Uint8Array> {\n const packets = new pgp.PacketList<OpenPGP.AnyPacket>();\n packets.push(key.keyPacket, ...key.revocationSignatures);\n // Direct-key self-signatures carry v6 key properties; OpenPGP.js keeps them here.\n const direct = (key as unknown as { directSignatures?: OpenPGP.SignaturePacket[] }).directSignatures ?? [];\n packets.push(...direct);\n for (const user of await validUserIds(key, at, cfg)) packets.push(user.userID!, ...user.selfCertifications, ...user.revocationSignatures);\n if (signer !== key) {\n const sub = signer as OpenPGP.Subkey;\n packets.push(sub.keyPacket, ...sub.bindingSignatures, ...sub.revocationSignatures);\n }\n const bytes = new pgp.PublicKey(packets).write();\n if (bytes.length > PGP_LIMITS.sharedKey) throw new PgpProofError('This key is too large to share, even without third-party certifications. Remove old user IDs or use a smaller key.');\n return bytes;\n}\n\nasync function keyInfo(key: OpenPGP.PublicKey, signer: OpenPGP.PublicKey | OpenPGP.Subkey, algorithm: string, at: Date, cfg: OpenPGP.PartialConfig): Promise<PgpKeyInfo> {\n const expiry = await key.getExpirationTime(undefined, cfg as OpenPGP.Config).catch(() => null);\n const subExpiry = signer !== key ? await (signer as OpenPGP.Subkey).getExpirationTime(at, cfg as OpenPGP.Config).catch(() => null) : null;\n const expires = [secondsOf(expiry), secondsOf(subExpiry)].filter((x): x is number => x !== null);\n return {\n fingerprint: key.getFingerprint().toUpperCase(),\n signingFingerprint: signer.getFingerprint().toUpperCase(),\n version: key.keyPacket.version as 4 | 6,\n algorithm,\n createdAt: secondsOf(key.getCreationTime())!,\n expiresAt: expires.length ? Math.min(...expires) : null,\n userIds: (await validUserIds(key, at, cfg)).map(u => u.userID!.userID),\n };\n}\n\n/** What a pasted public key is, before anything is signed: fingerprint and user IDs to show. */\nexport async function readPgpPublicKey(armored: string, options: { now?: number } = {}): Promise<PgpKeyInfo> {\n if (typeof armored !== 'string' || armored.length > PGP_LIMITS.pastedKey) throw new PgpProofError('This public key is too large. Export it with --export-options export-minimal.');\n const { type, block } = armoredBlock(armored, 'key');\n if (type !== 'PUBLIC KEY BLOCK') throw new PgpProofError('Paste a public key block (gpg --armor --export).');\n const pgp = await loadOpenPgp();\n const cfg = config(pgp);\n const key = await readOneKey(pgp, { armored: block });\n const at = new Date((options.now ?? nowSeconds()) * 1000);\n allowedAlgorithm(key.keyPacket, false);\n let signer: OpenPGP.PublicKey | OpenPGP.Subkey;\n try { signer = await key.getSigningKey(undefined, at, undefined, cfg as OpenPGP.Config) as OpenPGP.PublicKey | OpenPGP.Subkey; }\n catch { return explainInvalidKey(key, undefined, at, cfg, 'now'); }\n return keyInfo(key, signer, allowedAlgorithm(signer.keyPacket, true), at, cfg);\n}\n\nasync function readSignaturePacket(pgp: Pgp, input: { armored: string } | { binary: Uint8Array }): Promise<OpenPGP.SignaturePacket> {\n let sig: OpenPGP.Signature;\n try {\n sig = 'armored' in input ? await pgp.readSignature({ armoredSignature: input.armored, config: config(pgp) }) : await pgp.readSignature({ binarySignature: input.binary, config: config(pgp) });\n } catch (e) { throw new PgpProofError(`Could not read this signature: ${message(e)}`); }\n const packets = sig.packets.filterByTag(pgp.enums.packet.signature) as unknown as OpenPGP.SignaturePacket[];\n if (sig.packets.length !== 1 || packets.length !== 1) throw new PgpProofError('Sign with one key only: this signature has several signers.');\n return packets[0];\n}\n\n/**\n * The core check, shared by the person (on paste) and the contact (on receipt):\n * one signature over the statement, by a key or signing subkey of `key` that is\n * valid both when it signed and now, with allowed algorithms and hash.\n */\nasync function verifyPacket(pgp: Pgp, key: OpenPGP.PublicKey, sig: OpenPGP.SignaturePacket, statement: string, options: PgpVerifyOptions): Promise<{ info: PgpVerified; signer: OpenPGP.PublicKey | OpenPGP.Subkey }> {\n const cfg = config(pgp);\n const now = options.now ?? nowSeconds();\n const at = new Date(now * 1000);\n const { signature } = pgp.enums;\n if (sig.signatureType !== signature.text && sig.signatureType !== signature.binary) throw new PgpProofError('This is not a signature over a text or a file.');\n if (sig.hashAlgorithm === null || !ALLOWED_HASHES.has(sig.hashAlgorithm)) throw new PgpProofError('This signature uses a weak hash (SHA-1 or older). Sign again with gpg\\'s default (SHA-256 or stronger): --digest-algo SHA256.');\n if (!sig.created) throw new PgpProofError('This signature has no creation time.');\n const signedAt = secondsOf(sig.created)!;\n if (signedAt > now + PGP_CLOCK_SKEW) throw new PgpProofError('This signature is dated in the future. Check the clock of the machine that signed.');\n if (options.expectedFingerprint !== undefined && key.getFingerprint().toUpperCase() !== options.expectedFingerprint.toUpperCase())\n throw new PgpProofError('This key is not the one the statement names.');\n const issuer = key.getKeys(sig.issuerKeyID)[0];\n if (!issuer) throw new PgpProofError(`This signature was made by another key (${sig.issuerKeyID.toHex().toUpperCase()}), not the public key given.`);\n if (sig.issuerFingerprint && hexOf(sig.issuerFingerprint) !== issuer.getFingerprint().toUpperCase())\n throw new PgpProofError('This signature was made by another key, not the public key given.');\n allowedAlgorithm(key.keyPacket, false);\n // Valid now (not expired, not revoked, signing-capable with its back-signature)…\n let signer: OpenPGP.PublicKey | OpenPGP.Subkey;\n try { signer = await key.getSigningKey(sig.issuerKeyID, at, undefined, cfg as OpenPGP.Config) as OpenPGP.PublicKey | OpenPGP.Subkey; }\n catch { return explainInvalidKey(key, sig.issuerKeyID, at, cfg, 'now'); }\n const algorithm = allowedAlgorithm(signer.keyPacket, true);\n // …and when it signed.\n const created = new Date(Math.min(signedAt, now) * 1000);\n try { await key.getSigningKey(sig.issuerKeyID, created, undefined, cfg as OpenPGP.Config); }\n catch { return explainInvalidKey(key, sig.issuerKeyID, created, cfg, 'signing'); }\n // After the key checks, so an expired or revoked key is reported as such.\n if (options.signedAfter !== undefined && signedAt < options.signedAfter - PGP_CLOCK_SKEW) throw new PgpProofError('This signature is older than the statement it should sign.');\n // A detached signature over a file holds its final newline; clearsigned text does not.\n const text = canonicalStatement(statement);\n const packets = new pgp.PacketList<OpenPGP.AnyPacket>(); packets.push(sig);\n const detached = new pgp.Signature(packets as OpenPGP.PacketList<OpenPGP.SignaturePacket>);\n let verified = false;\n for (const candidate of [text, `${text}\\n`, `${text}\\r\\n`]) {\n const message = await pgp.createMessage({ binary: utf8Encode(candidate) });\n const result = await pgp.verify({ message, signature: detached, verificationKeys: key, date: new Date(Math.max(now, signedAt) * 1000), config: cfg });\n if (result.signatures.length === 1 && await result.signatures[0].verified.then(() => true, () => false)) { verified = true; break; }\n }\n if (!verified) throw new PgpProofError('This signature is not over the Ghostly statement (it signs other text, or it was changed).');\n return { info: { ...await keyInfo(key, signer, algorithm, at, cfg), signedAt }, signer };\n}\n\n/**\n * The person's side: checks what they pasted against the statement and\n * produces the evidence to share. Either a clearsigned statement or an\n * armored detached signature is accepted.\n */\nexport async function preparePgpEvidence(input: { statement: string; signature: string; publicKey: string }, options: PgpVerifyOptions = {}): Promise<{ evidence: PgpEvidence; verified: PgpVerified }> {\n if (typeof input.signature !== 'string' || input.signature.length > PGP_LIMITS.pastedSignature) throw new PgpProofError('This signature is too large. Paste only the output of gpg --clearsign.');\n if (typeof input.publicKey !== 'string' || input.publicKey.length > PGP_LIMITS.pastedKey) throw new PgpProofError('This public key is too large. Export it with --export-options export-minimal.');\n const { type, block } = armoredBlock(input.signature, 'signature');\n const keyBlock = armoredBlock(input.publicKey, 'key');\n if (keyBlock.type !== 'PUBLIC KEY BLOCK') throw new PgpProofError('Paste a public key block (gpg --armor --export).');\n if (type === 'MESSAGE') throw new PgpProofError('This is a signed message (gpg --sign). Use gpg --clearsign, or --detach-sign --armor.');\n if (type === 'PUBLIC KEY BLOCK') throw new PgpProofError('This is a public key; paste it in the public key field and the signature here.');\n const pgp = await loadOpenPgp();\n const key = await readOneKey(pgp, { armored: keyBlock.block });\n let sig: OpenPGP.SignaturePacket;\n if (type === 'SIGNED MESSAGE') {\n let cleartext: OpenPGP.CleartextMessage;\n try { cleartext = await pgp.readCleartextMessage({ cleartextMessage: block, config: config(pgp) }); }\n catch (e) { throw new PgpProofError(`Could not read this signed text: ${message(e)}`); }\n if (canonicalStatement(cleartext.getText()) !== canonicalStatement(input.statement))\n throw new PgpProofError('This signs different text. Sign exactly the statement shown, without editing it.');\n // Typed as private in OpenPGP.js's declarations, public at runtime.\n const packets = (cleartext as unknown as { signature: OpenPGP.Signature }).signature.packets;\n if (packets.length !== 1 || !(packets[0] instanceof pgp.SignaturePacket)) throw new PgpProofError('Sign with one key only: this signature has several signers.');\n sig = packets[0];\n } else if (type === 'SIGNATURE') sig = await readSignaturePacket(pgp, { armored: block });\n else throw new PgpProofError('Paste the output of gpg --clearsign (or gpg --detach-sign --armor).');\n const { info, signer } = await verifyPacket(pgp, key, sig, input.statement, options);\n const single = new pgp.PacketList<OpenPGP.AnyPacket>(); single.push(sig);\n const signature = new pgp.Signature(single as OpenPGP.PacketList<OpenPGP.SignaturePacket>).write();\n if (signature.length > PGP_LIMITS.sharedSignature) throw new PgpProofError('This signature is too large to share.');\n const at = new Date((options.now ?? nowSeconds()) * 1000);\n const evidence: PgpEvidence = { scheme: PGP_SCHEME, key: toBase64Url(await minimalKey(pgp, key, signer, at, config(pgp))), signature: toBase64Url(signature) };\n return { evidence, verified: info };\n}\n\nconst b64 = /^[A-Za-z0-9_-]+$/;\n/** Untrusted evidence off the wire: exact shape, bounded, canonical base64url. Checked before any OpenPGP parsing. */\nexport function parsePgpEvidence(raw: unknown): PgpEvidence {\n const e = raw as PgpEvidence;\n if (!e || typeof e !== 'object' || Array.isArray(e) || e.scheme !== PGP_SCHEME || Object.keys(e).sort().join(',') !== 'key,scheme,signature' ||\n typeof e.key !== 'string' || typeof e.signature !== 'string' || !b64.test(e.key) || !b64.test(e.signature) ||\n e.key.length > Math.ceil(PGP_LIMITS.sharedKey * 4 / 3) || e.signature.length > Math.ceil(PGP_LIMITS.sharedSignature * 4 / 3))\n throw new PgpProofError('Invalid OpenPGP proof');\n let canonical: boolean;\n try { canonical = toBase64Url(fromBase64Url(e.key)) === e.key && toBase64Url(fromBase64Url(e.signature)) === e.signature; } catch { canonical = false; }\n if (!canonical) throw new PgpProofError('Invalid OpenPGP proof encoding');\n return { scheme: PGP_SCHEME, key: e.key, signature: e.signature };\n}\n\n/** The contact's side: verifies shared evidence against the statement their own app built. */\nexport async function verifyPgpEvidence(raw: PgpEvidence, statement: string, options: PgpVerifyOptions = {}): Promise<PgpVerified> {\n const evidence = parsePgpEvidence(raw);\n const keyBytes = fromBase64Url(evidence.key), sigBytes = fromBase64Url(evidence.signature);\n const pgp = await loadOpenPgp();\n const key = await readOneKey(pgp, { binary: keyBytes });\n const sig = await readSignaturePacket(pgp, { binary: sigBytes });\n return (await verifyPacket(pgp, key, sig, statement, options)).info;\n}\n\n/** Groups of four, as gpg prints fingerprints. */\nexport function formatFingerprint(fingerprint: string): string {\n return fingerprint.replace(/(.{4})/g, '$1 ').trim();\n}\n\n/** keys.openpgp.org publishes a user ID only after its owner confirms the email. */\nexport const KEYSERVER = 'https://keys.openpgp.org';\nexport interface KeyserverKey { armored: string; source: typeof KEYSERVER; fetchedAt: number }\n/**\n * Fetches a public key from keys.openpgp.org. Only ever called when the person\n * asks for it, after the UI said this contacts that server.\n */\nexport async function fetchKeyFromKeyserver(query: string, options: { signal?: AbortSignal; fetch?: typeof fetch; now?: number } = {}): Promise<KeyserverKey> {\n const q = query.trim().replace(/\\s+/g, '').replace(/^0x/i, '');\n let path: string;\n if (/^[A-Fa-f0-9]{40}$/.test(q) || /^[A-Fa-f0-9]{64}$/.test(q)) path = `/vks/v1/by-fingerprint/${q.toUpperCase()}`;\n else if (/^[^\\s@<>]{1,64}@[^\\s@<>]{1,190}$/.test(query.trim())) path = `/vks/v1/by-email/${encodeURIComponent(query.trim())}`;\n else throw new PgpProofError('Enter a full fingerprint (40 hex characters) or an email address.');\n const response = await (options.fetch ?? fetch)(`${KEYSERVER}${path}`, { signal: options.signal, credentials: 'omit', referrerPolicy: 'no-referrer', redirect: 'error' });\n if (response.status === 404) throw new PgpProofError('keys.openpgp.org has no key for this. Paste your public key instead.');\n if (!response.ok) throw new PgpProofError(`keys.openpgp.org answered ${response.status}. Paste your public key instead.`);\n const length = Number(response.headers.get('content-length') ?? 0);\n if (length > PGP_LIMITS.keyserverResponse) throw new PgpProofError('The key from keys.openpgp.org is too large.');\n const armored = await response.text();\n if (armored.length > PGP_LIMITS.keyserverResponse) throw new PgpProofError('The key from keys.openpgp.org is too large.');\n return { armored, source: KEYSERVER, fetchedAt: options.now ?? nowSeconds() };\n}\n\n/**\n * Emails keys.openpgp.org vouches for on this key: it publishes a user ID only\n * once the email's owner confirmed it, so every user ID in its copy is one the\n * keyserver checked. That is the keyserver's check, and says nothing about a name.\n */\nexport async function keyserverVerifiedEmails(keyserverArmored: string, fingerprint: string, options: { now?: number } = {}): Promise<string[]> {\n const info = await readPgpPublicKey(keyserverArmored, options);\n if (info.fingerprint !== fingerprint.toUpperCase()) throw new PgpProofError('keys.openpgp.org returned a different key.');\n const emails = info.userIds.map(u => /<([^<>\\s]+@[^<>\\s]+)>\\s*$/.exec(u)?.[1] ?? (/^[^<>\\s]+@[^<>\\s]+$/.test(u) ? u : undefined));\n return [...new Set(emails.filter((e): e is string => !!e).map(e => e.toLowerCase()))];\n}\n","import type { IdentityStatement } from \"@ghostly/core\";\nimport type { ExternalToolSigner, IdentityProofProvider, SignerInstructions } from \"../contract\";\nimport {\n KEYSERVER, PgpProofError, assertSignableStatement, fetchKeyFromKeyserver, formatFingerprint, keyserverVerifiedEmails, parsePgpEvidence,\n preparePgpEvidence, splitPgpPaste, verifyPgpEvidence, type PgpEvidence,\n} from \"../openpgp\";\n\n/**\n * OpenPGP keys, signed with the person's own gpg — a YubiKey or another OpenPGP card works unchanged,\n * since gpg drives it. The subject is the primary key's fingerprint. See docs/wisps/305-openpgp.md.\n */\n\n/** A v4 (40 hex) or v6 (64 hex) fingerprint, as gpg prints it or not: spaces and 0x are dropped. */\nexport function normalizeFingerprint(input: string): string {\n const value = input.trim().replace(/^0x/i, \"\").replace(/\\s+/g, \"\").toUpperCase();\n if (/^[A-F0-9]{40}$/.test(value) || /^[A-F0-9]{64}$/.test(value)) return value;\n if (/^[A-F0-9]{8}$|^[A-F0-9]{16}$/.test(value)) throw new PgpProofError(\"Use the full fingerprint, not a key ID: gpg --fingerprint shows it.\");\n throw new PgpProofError(\"Enter your key's fingerprint: 40 hex characters (gpg --fingerprint).\");\n}\n\n/** The user ID is its holder's own words: shown with that caveat, never as a checked name. */\nconst USER_ID_SOURCE = \"User ID on the key, written by its holder: not proof that the name or email is theirs\";\nconst KEYSERVER_SOURCE = \"Email confirmed to keys.openpgp.org by its owner: the keyserver's check, not Ghostly's\";\n\nfunction steps(statement: IdentityStatement, exportKey: boolean): SignerInstructions {\n assertSignableStatement(statement.text);\n // The statement is one line of space-separated fields with no quote in it, so it is safe in single quotes.\n if (statement.text.includes(\"'\")) throw new PgpProofError(\"Statement is not signable as plain text\");\n const fpr = normalizeFingerprint(statement.binding.subject);\n const sign = `gpg --local-user ${fpr} --clearsign --output - ghostly-identity.txt`;\n return {\n steps: [\n { text: \"Save the statement to a file (it is one line, with no newline at the end):\", copy: `printf '%s' '${statement.text}' > ghostly-identity.txt` },\n exportKey\n ? { text: \"Sign it and export your public key, then copy everything this prints:\", copy: `${sign} && gpg --armor --export --export-options export-minimal ${fpr}` }\n : { text: \"Sign it, then copy everything this prints:\", copy: sign },\n { text: \"On a YubiKey or another OpenPGP card, gpg asks for the card's PIN, and for a touch if the card requires one. Without a shell, save the statement below in a text file and sign that file.\", copy: statement.text },\n ],\n paste: { label: exportKey ? \"Signed statement and public key\" : \"Signed statement\", placeholder: \"-----BEGIN PGP SIGNED MESSAGE-----\", multiline: true },\n };\n}\n\nasync function evidenceFrom(statement: IdentityStatement, signature: string, publicKey: string): Promise<PgpEvidence> {\n const { evidence } = await preparePgpEvidence({ statement: statement.text, signature, publicKey },\n { expectedFingerprint: normalizeFingerprint(statement.binding.subject), signedAfter: statement.binding.issuedAt });\n return evidence;\n}\n\nconst gpg: ExternalToolSigner<PgpEvidence> = {\n id: \"gpg\", kind: \"external-tool\", label: \"gpg\",\n description: \"Sign with gpg and paste what it prints, public key included. Nothing is looked up.\",\n instructions: statement => steps(statement, true),\n async parse(pasted, statement) {\n const { signature, publicKey } = splitPgpPaste(pasted);\n if (!publicKey) throw new PgpProofError(\"Paste your public key too: the second command prints it (-----BEGIN PGP PUBLIC KEY BLOCK-----).\");\n return evidenceFrom(statement, signature, publicKey);\n },\n};\n\nconst gpgKeyserver: ExternalToolSigner<PgpEvidence> = {\n id: \"gpg-keys-openpgp-org\", kind: \"external-tool\", label: \"gpg, key from keys.openpgp.org\",\n description: \"Sign with gpg and paste the signature; Ghostly then fetches your public key from keys.openpgp.org, which learns which key was looked up.\",\n instructions: statement => steps(statement, false),\n async parse(pasted, statement) {\n const { signature, publicKey } = splitPgpPaste(pasted);\n // Only here, on the person's choice of this signer, is the keyserver contacted.\n const key = publicKey ?? (await fetchKeyFromKeyserver(statement.binding.subject)).armored;\n return evidenceFrom(statement, signature, key);\n },\n};\n\nexport const openpgp: IdentityProofProvider<PgpEvidence> = {\n id: \"openpgp\",\n label: \"OpenPGP key\",\n category: \"self-custodied\",\n summary: \"Sign with gpg and paste the result\",\n description: \"Proves you hold an OpenPGP key: sign the statement with gpg (a YubiKey works too) and paste the result.\",\n limits: \"Holding a key does not prove the names or emails in its user IDs.\",\n platforms: [\"web\", \"extension\", \"desktop\"],\n subject: {\n label: \"Key fingerprint\", placeholder: \"40 hex characters, from gpg --fingerprint\",\n help: \"The primary key's full fingerprint. Holding a key does not prove the names or emails in its user IDs.\",\n normalize: normalizeFingerprint,\n short: fpr => `…${formatFingerprint(fpr.slice(-16))}`,\n },\n // The fingerprint URI scheme OpenPGP apps share keys with.\n publicUri: fpr => `openpgp4fpr:${fpr}`,\n validity: { defaultDays: 90, maxDays: 365 },\n privacy: `Nothing: the signature is checked on this device. ${KEYSERVER.replace(\"https://\", \"\")} is contacted only if you choose it for your key, or ask it to confirm emails.`,\n signers: [gpg, gpgKeyserver],\n lookupLabel: \"Check emails with keys.openpgp.org\",\n parseEvidence: raw => parsePgpEvidence(raw),\n async verify(statement, evidence, ctx) {\n ctx.signal.throwIfAborted();\n const { binding } = statement;\n const verified = await verifyPgpEvidence(evidence, statement.text, { now: ctx.now, signedAfter: binding.issuedAt, expectedFingerprint: normalizeFingerprint(binding.subject) });\n const subkey = verified.signingFingerprint !== verified.fingerprint;\n const [userId] = verified.userIds;\n return {\n subject: verified.fingerprint,\n source: `OpenPGP signature (${verified.algorithm}${subkey ? \" signing subkey\" : \"\"}, v${verified.version} key)`,\n ...(verified.expiresAt !== null && verified.expiresAt < binding.expiresAt ? { expiresAt: verified.expiresAt } : {}),\n ...(userId ? { display: { name: userId, source: USER_ID_SOURCE, fetchedAt: ctx.now } } : {}),\n };\n },\n /** On request only: which emails on this key keys.openpgp.org confirmed, and whether it holds a revocation. */\n async lookupDisplay(subject, { signal }) {\n const fpr = normalizeFingerprint(subject);\n const found = await fetchKeyFromKeyserver(fpr, { signal });\n const emails = await keyserverVerifiedEmails(found.armored, fpr);\n if (!emails.length) return undefined;\n return { name: emails[0], url: `${KEYSERVER}/search?q=${fpr}`, source: KEYSERVER_SOURCE, fetchedAt: found.fetchedAt };\n },\n};\n","import { parseSshPublicKey, sameSshKey, type SshPublicKey } from '@ghostly/core';\nimport type { IdentityFetch } from './contract';\n\n/** \"This account publishes that key\", without OAuth: GitHub and GitLab publish every account's SSH\n * public keys, and only the account holder can add one. The verifier's own lookup, through the\n * engine's bounded `ctx.fetch`; it tells the forge (and the network) which account was checked. */\n\nexport type SshForge = 'github' | 'gitlab';\nexport const SSH_FORGE_LABEL: Record<SshForge, string> = { github: 'GitHub', gitlab: 'GitLab' };\n/** The only host each forge lookup contacts, for the privacy line. */\nexport const SSH_FORGE_HOST: Record<SshForge, string> = { github: 'api.github.com', gitlab: 'gitlab.com' };\n\nexport type SshForgeStatus = 'linked' | 'not-listed' | 'no-account';\n\nconst MAX_BODY = 256 * 1024;\nconst MAX_KEYS = 200;\n\nexport function validForgeLogin(forge: SshForge, login: string): boolean {\n if (typeof login !== 'string') return false;\n // GitHub: 1-39 alphanumerics or single inner hyphens. GitLab: letters, digits, _ . - ;\n // not starting with - and not ending in .git/.atom. Capped at 64 so it fits every label.\n return forge === 'github'\n ? /^[A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9])){0,38}$/.test(login)\n : /^[A-Za-z0-9_.][A-Za-z0-9_.-]{0,63}$/.test(login) && !/\\.(?:git|atom)$/i.test(login);\n}\n\nasync function get(fetcher: IdentityFetch, url: string, signal?: AbortSignal): Promise<{ status: 404 } | { status: 200; body: unknown }> {\n const response = await fetcher(url, { maxBytes: MAX_BODY, signal });\n if (response.status === 404) return { status: 404 };\n if (response.status === 403 || response.status === 429) throw new Error(`${new URL(url).host} is limiting lookups; check again later`);\n if (response.status !== 200) throw new Error(`${new URL(url).host} answered HTTP ${response.status}`);\n try { return { status: 200, body: JSON.parse(response.text) }; } catch { throw new Error(`Unexpected answer from ${new URL(url).host}`); }\n}\n\nfunction keysFrom(body: unknown): SshPublicKey[] {\n if (!Array.isArray(body)) throw new Error('Unexpected key list');\n const keys: SshPublicKey[] = [];\n for (const entry of body.slice(0, MAX_KEYS)) {\n const line = (entry as { key?: unknown } | null)?.key;\n if (typeof line !== 'string') continue;\n try { keys.push(parseSshPublicKey(line)); } catch { /* A key type Ghostly cannot verify never matches. */ }\n }\n return keys;\n}\n\n/** The account's published keys, or null when there is no such account. */\nasync function listKeys(forge: SshForge, login: string, fetcher: IdentityFetch, signal?: AbortSignal): Promise<SshPublicKey[] | null> {\n if (forge === 'github') {\n const r = await get(fetcher, `https://api.github.com/users/${encodeURIComponent(login)}/keys?per_page=100`, signal);\n return r.status === 404 ? null : keysFrom(r.body);\n }\n const users = await get(fetcher, `https://gitlab.com/api/v4/users?username=${encodeURIComponent(login)}`, signal);\n const list = users.status === 200 && Array.isArray(users.body) ? users.body as { id?: unknown; username?: unknown }[] : [];\n const user = list.find(u => typeof u?.username === 'string' && u.username.toLowerCase() === login.toLowerCase());\n if (!user) return null;\n if (!Number.isSafeInteger(user.id) || (user.id as number) <= 0) throw new Error('Unexpected answer from gitlab.com');\n const r = await get(fetcher, `https://gitlab.com/api/v4/users/${user.id as number}/keys?per_page=100`, signal);\n return r.status === 404 ? null : keysFrom(r.body);\n}\n\n// Only concurrent identical lookups share a request. What stays checked is the verifier's stored\n// result, which the app offers to re-check once the provider's `recheck` time has passed.\nconst inFlight = new WeakMap<IdentityFetch, Map<string, Promise<SshPublicKey[] | null>>>();\n\n/** Whether `key` is one of `login`'s published SSH keys. Throws when the forge cannot be asked. */\nexport async function checkSshForge(forge: SshForge, login: string, key: Pick<SshPublicKey, 'blob'>, fetcher: IdentityFetch, signal?: AbortSignal): Promise<SshForgeStatus> {\n if (!validForgeLogin(forge, login)) throw new Error(`Not a ${SSH_FORGE_LABEL[forge]} username`);\n let pending = inFlight.get(fetcher);\n if (!pending) inFlight.set(fetcher, pending = new Map());\n const map = pending, id = `${forge}:${login.toLowerCase()}`;\n let lookup = map.get(id);\n if (!lookup) {\n lookup = listKeys(forge, login, fetcher, signal).finally(() => map.delete(id));\n map.set(id, lookup);\n }\n const keys = await lookup;\n if (!keys) return 'no-account';\n return keys.some(k => sameSshKey(k, key)) ? 'linked' : 'not-listed';\n}\n","import {\n parseSshPublicKey, parseSshSignature, sshSignCommand, SshSigError, SSHSIG_MAX_ARMOR, SSHSIG_NAMESPACE, utf8Encode, verifySshSignature,\n type IdentityStatement, type SshSignatureCheck,\n} from \"@ghostly/core\";\nimport type { ExternalToolSigner, IdentityProofProvider, VerifyContext } from \"../contract\";\nimport { checkSshForge, SSH_FORGE_HOST, SSH_FORGE_LABEL, validForgeLogin, type SshForge } from \"../sshForges\";\n\n/**\n * SSH keys (draft WISP 307-ssh). The person signs the binding statement once with their own\n * `ssh-keygen -Y sign -n ghostly` and pastes the armored SSHSIG; the private key never enters Ghostly.\n *\n * - `ssh` the subject is the key's SHA-256 fingerprint; checked on this device, no network.\n * - `ssh-github` the subject is a GitHub account: the signing key must be one of the SSH keys GitHub\n * - `ssh-gitlab` publishes for it (GitLab alike). No OAuth: only the account holder can add a key, so\n * \"this account lists the key\" links the two. Re-checked, since keys get removed.\n */\n\nexport interface SshEvidence { signature: string }\n\nconst FINGERPRINT = /^SHA256:[A-Za-z0-9+/]{43}$/;\n\nfunction normalizeKeySubject(input: string): string {\n const value = input.trim();\n if (FINGERPRINT.test(value)) return value;\n try { return parseSshPublicKey(value).fingerprint; }\n catch (e) {\n // Say why a real key is refused (a DSA key, a small RSA key); anything else gets the instruction.\n if (e instanceof SshSigError && /^(Unsupported SSH key type|RSA keys must)/.test(e.message)) throw Object.assign(new Error(e.message), { cause: e });\n throw Object.assign(new Error(\"Paste your public key (the one line in ~/.ssh/id_ed25519.pub) or its SHA256: fingerprint\"), { cause: e });\n }\n}\n\nfunction normalizeLogin(forge: SshForge, input: string): string {\n const login = input.trim().replace(/^@/, \"\").toLowerCase();\n if (!validForgeLogin(forge, login)) throw new Error(`Enter a ${SSH_FORGE_LABEL[forge]} username`);\n return login;\n}\n\n/** The armor, re-wrapped canonically, so evidence is the same whatever the clipboard did to it. */\nfunction canonicalArmor(pasted: string): string {\n const parsed = parseSshSignature(pasted);\n if (parsed.namespace !== SSHSIG_NAMESPACE) throw new Error(`That signature is for \"${parsed.namespace}\", not \"${SSHSIG_NAMESPACE}\": run the command with -n ${SSHSIG_NAMESPACE}`);\n const body = pasted.replace(/\\r\\n/g, \"\\n\").trim().split(\"\\n\").map(l => l.trim()).slice(1, -1).join(\"\");\n return `-----BEGIN SSH SIGNATURE-----\\n${body.match(/.{1,70}/g)!.join(\"\\n\")}\\n-----END SSH SIGNATURE-----\\n`;\n}\n\n/** Exactly the statement; or with one trailing newline, which an editor adds when the statement is\n * saved to a file and signed there (the Windows route). Nothing else is accepted. */\nasync function verifyStatementSignature(statement: IdentityStatement, evidence: SshEvidence): Promise<SshSignatureCheck> {\n try { return await verifySshSignature(evidence.signature, statement.bytes); }\n catch (e) {\n if (!(e instanceof SshSigError) || !/does not match this statement/.test(e.message)) throw e;\n try { return await verifySshSignature(evidence.signature, utf8Encode(`${statement.text}\\n`)); }\n catch (again) { throw Object.assign(new Error(\"The signature is not over this statement: copy the command again and sign exactly what it shows\"), { cause: again }); }\n }\n}\n\nconst keyKind = (c: SshSignatureCheck) => c.securityKey\n ? `security key, touch${c.securityKey.userVerified ? \" and PIN\" : \"\"} confirmed`\n : c.key.type === \"ssh-rsa\" ? `RSA ${c.key.bits}` : c.key.type.replace(/^ssh-|^ecdsa-sha2-/, \"\");\n\nfunction sshKeygenSigner(forAccount?: SshForge): ExternalToolSigner<SshEvidence> {\n return {\n id: \"ssh-keygen\", kind: \"external-tool\", label: \"ssh-keygen\",\n description: forAccount\n ? `Sign with an SSH key that your ${SSH_FORGE_LABEL[forAccount]} account lists (Settings → SSH keys).`\n : \"Sign with OpenSSH's ssh-keygen (8.1 or later): a key file, ssh-agent, or a FIDO security key.\",\n instructions: statement => {\n let command: string | undefined;\n try { command = sshSignCommand(statement.text); } catch { command = undefined; }\n return {\n steps: [\n ...(command ? [{ text: \"Run this in a terminal (macOS, Linux, or Git Bash/WSL on Windows). If your key is not ~/.ssh/id_ed25519, change the path after -f; a .pub path works when the private key is in ssh-agent or on a security key.\", copy: command }] : []),\n { text: `${command ? \"Or save\" : \"Save\"} this statement to a file, run ssh-keygen -Y sign -n ${SSHSIG_NAMESPACE} -f <your key> <file>, and paste the <file>.sig it writes.`, copy: statement.text },\n { text: \"Paste the whole signature it prints, from -----BEGIN SSH SIGNATURE----- to -----END SSH SIGNATURE-----. Your private key never leaves your computer.\" },\n ],\n paste: { label: \"SSH signature\", placeholder: \"-----BEGIN SSH SIGNATURE-----\", multiline: true },\n };\n },\n parse(pasted, statement) {\n const signature = canonicalArmor(pasted);\n if (!forAccount) {\n const { key } = parseSshSignature(signature);\n if (key.fingerprint !== statement.binding.subject) throw new Error(`That signature was made by ${key.fingerprint}, not the key you entered (${statement.binding.subject})`);\n }\n return { signature };\n },\n };\n}\n\nfunction parseSshEvidence(raw: unknown): SshEvidence {\n const e = raw as SshEvidence;\n if (!e || typeof e !== \"object\" || Array.isArray(e) || Object.keys(e).join(\",\") !== \"signature\" ||\n typeof e.signature !== \"string\" || e.signature.length > SSHSIG_MAX_ARMOR) throw new Error(\"That is not an SSH signature\");\n return { signature: e.signature };\n}\n\nconst shortFingerprint = (f: string) => `${f.slice(0, 15)}…${f.slice(-4)}`;\n\nexport const ssh: IdentityProofProvider<SshEvidence> = {\n id: \"ssh\",\n label: \"SSH key\",\n category: \"self-custodied\",\n summary: \"Sign once with ssh-keygen\",\n description: \"Proves you hold an SSH key, including FIDO security keys: sign the statement once with ssh-keygen and paste the signature.\",\n platforms: [\"web\", \"extension\", \"desktop\"],\n subject: {\n label: \"Public key\", placeholder: \"ssh-ed25519 AAAA… (or SHA256:…)\",\n help: \"Your public key, the one line in the .pub file. Contacts see its fingerprint.\",\n normalize: normalizeKeySubject, short: shortFingerprint,\n },\n validity: { defaultDays: 90, maxDays: 365 },\n privacy: \"Nothing: the signature is checked on this device. The key's fingerprint links every chat you share it in.\",\n signers: [sshKeygenSigner()],\n parseEvidence: parseSshEvidence,\n async verify(statement, evidence) {\n const check = await verifyStatementSignature(statement, evidence);\n if (check.key.fingerprint !== statement.binding.subject) throw new Error(\"Signed by another SSH key\");\n return { subject: statement.binding.subject, source: `SSH signature (${keyKind(check)})` };\n },\n};\n\nfunction forgeProvider(forge: SshForge): IdentityProofProvider<SshEvidence> {\n const label = SSH_FORGE_LABEL[forge], host = SSH_FORGE_HOST[forge];\n return {\n id: `ssh-${forge}`,\n label: `${label} (SSH key)`,\n category: \"self-custodied\",\n summary: `A key your ${label} account lists`,\n description: `Proves you hold an SSH key your ${label} account publishes: sign the statement once with ssh-keygen. No ${label} login.`,\n limits: `Does not prove a ${label} login: only that a key the account lists is yours, for as long as it stays listed.`,\n platforms: [\"web\", \"extension\", \"desktop\"],\n subject: { label: `${label} username`, placeholder: forge === \"github\" ? \"octocat\" : \"username\", normalize: input => normalizeLogin(forge, input) },\n publicUri: login => `https://${forge === \"github\" ? \"github.com\" : \"gitlab.com\"}/${login}`,\n validity: { defaultDays: 90, maxDays: 365 },\n // Keys get removed from accounts; the contact's app offers \"Check again\" after this.\n recheck: { afterSeconds: 600 },\n privacy: `Your contact's app asks ${host} for this account's public SSH keys, when you share it and on each re-check: ${label} learns the account was looked up, and from which IP address. So does this app when you add it.`,\n signers: [sshKeygenSigner(forge)],\n parseEvidence: parseSshEvidence,\n async verify(statement, evidence, ctx: VerifyContext) {\n const login = statement.binding.subject;\n if (normalizeLogin(forge, login) !== login) throw new Error(`Not a ${label} username`);\n const check = await verifyStatementSignature(statement, evidence);\n const status = await checkSshForge(forge, login, check.key, ctx.fetch, ctx.signal);\n if (status === \"no-account\") throw new Error(`${label} has no account ${login}`);\n if (status === \"not-listed\") throw new Error(`${label}: ${login} does not list the key that signed (${check.key.fingerprint})`);\n return { subject: login, source: `${label}: ${login} (via published SSH key)` };\n },\n };\n}\n\nexport const sshGithub = forgeProvider(\"github\");\nexport const sshGitlab = forgeProvider(\"gitlab\");\n","import type { FoundUpdate } from \"../../../apps/ui/src/lib/updates\";\nimport type { EngineEvent, RpcRequest, RpcResponse } from \"./shared/rpc\";\nimport type { OidcPlatform } from \"./proofs/oidc/providers\";\nimport type { OidcWindow } from \"./proofs/oidc/flow\";\nimport type { AtprotoHost } from \"./proofs/atproto/oauth\";\nimport type { CallMedia } from \"@ghostly/core\";\n\n/**\n * What differs between the places this peer runs. The extension keeps the\n * peer in an offscreen document and reaches it through extension messaging;\n * the web app runs it in the page. Everything else is shared.\n */\nexport interface EngineConnection {\n send(request: RpcRequest): void;\n}\n\n/**\n * How a client finds a new version of itself and puts it in place. The web\n * app reloads the tab; an extension from the store reloads itself; an unpacked\n * extension can only point at the download. A host that leaves this out has no\n * updater, and the UI never mentions one.\n */\nexport interface UpdateSource {\n /** Where someone installing by hand should go. */\n downloadUrl: string;\n /** The newest published version, or null when this client already runs it. */\n check(): Promise<FoundUpdate | null>;\n /** Never called for `apply: \"manual\"`. `onProgress` is used where the platform reports it. */\n install(update: FoundUpdate, onProgress?: (fraction: number) => void): Promise<void>;\n}\n\nexport interface BrowserHost {\n version: string;\n /** Something the user should know about this client, shown in the sidebar. */\n notice?: string;\n /** Left out where this client has no way to learn about new versions. */\n updates?: UpdateSource;\n /**\n * Where this host frames mini-apps (WISP 1200): its runner page, served with the runner's policy as a header. Left\n * out where it runs none (the extension, until it gets its own runner).\n */\n appRunner?: string;\n /** The runner for apps granted `internet` (WISP 1200 · Permissions), under a policy that allows HTTPS and WSS. */\n appNetRunner?: string;\n /**\n * The host serves its runner itself, always with the runner's policy (Desktop: Rust's `ghostly-app` scheme, to app\n * windows only), so the page does not ask for its header. Left out where a web server serves it (it may be one\n * without the header).\n */\n appRunnerServed?: boolean;\n features: {\n /** Can this host reach web apps on the user's machine? A web page only can if they allow it with CORS. */\n shareLocalServices: boolean;\n /** Can this host show a contact's web app on an origin of its own? */\n openServices: boolean;\n /** Can this host restart as another local profile (WISP 04)? The page's own peer, or the extension's offscreen one. */\n profiles?: boolean;\n };\n /**\n * Starts a new peer in place of one that stopped for good (\"Clear all data\"). Left out where the peer lives in the\n * page: reloading the page starts it again. The extension's peer outlives its pages, so it is restarted on purpose.\n */\n restartEngine?(): Promise<void>;\n /** Reaches the peer. `onDisconnect` fires when it goes away; the client then connects again. */\n connect(onMessage: (message: EngineEvent | RpcResponse) => void, onDisconnect: () => void): Promise<EngineConnection>;\n /**\n * Asks the user for access to a local origin, where the platform has such a thing: `originPattern` is the\n * extension's host pattern (`http://localhost/*`), `origin` the exact address with its port (`http://localhost:3400`),\n * which the desktop app has Rust ask about in a native dialog.\n */\n requestLocalAccess(originPattern: string, origin: string): Promise<boolean>;\n /** The profile no longer shares any app at `origin` (desktop: off Rust's list of addresses it may reach). */\n forgetLocalAccess?(origin: string): Promise<void>;\n openService(peerPubKeyZ32: string, serviceId: string): Promise<void>;\n /**\n * Opens a `lightning:` or `bitcoin:` link in a wallet on this device. Left out where a plain link\n * already does it (a web page); the desktop app and the extension have to hand it to the system.\n */\n openPaymentLink?(uri: string): Promise<void>;\n /**\n * Puts the app's window in or out of full screen. The desktop app on Linux has it: WebKitGTK's element full screen\n * aborts the app there, so the page fills the window with the video instead (apps/desktop/src/fullscreen.rs).\n */\n fullscreenWindow?(on: boolean): Promise<void>;\n /**\n * Hands a link to the system's share sheet, pointing at `anchor` (the button, in CSS pixels).\n * Resolves to false where the platform has no sheet to show; left out where the page's own Web\n * Share API is the way (a web page).\n */\n shareText?(text: string, anchor?: ShareAnchor): Promise<boolean>;\n /**\n * The clipboard's text, read by the platform itself, for a paste button the user just clicked.\n * Left out where the page's `navigator.clipboard.readText()` is the way (a web page, the\n * extension); the desktop app reads natively because WKWebView asks for a second click.\n */\n readClipboardText?(): Promise<string>;\n /**\n * Files copied in a file manager, or a picture, read by the platform itself for a paste whose event\n * carried neither files nor text (a webview that hides them from the page). Left out where the page\n * sees them in the paste event (a browser, WebKit on macOS).\n */\n readClipboardFiles?(): Promise<ClipboardFile[]>;\n /**\n * Where calls get their media, when not from the page's own WebRTC. Ghostly Desktop on Linux: WebKitGTK has\n * no WebRTC there, and GStreamer runs the calls. Left out where the page has it.\n */\n callMedia?: CallMedia;\n /** Signing in with an OpenID Connect provider for an identity proof. Left out where the platform cannot. */\n oidc?: OidcHost;\n /** Signing in to the person's AT Protocol server (Bluesky or another PDS) for an identity proof. Left out where the platform cannot. */\n atproto?: AtprotoHost;\n /**\n * Opens Pubky Passport's authorize page (`https://passport.pubky.app/authorize#d=…`, the request in its fragment)\n * outside the app. The desktop app hands it to the system browser; left out where the page opens a popup itself.\n */\n openPubkyPassport?(url: string): Promise<void>;\n /**\n * A Pubky cookie session's requests to the homeserver, made outside the page with a cookie jar of their own: the\n * desktop app, whose WKWebView drops the homeserver's session cookie (a third-party one there). Left out where\n * the page's own fetch keeps it.\n */\n pubkyCookieSession?(): PubkyCookieSession;\n /**\n * Keeps the computer from sleeping while idle, or lets it sleep again (\"Keep this computer awake\", WISP 06 § User\n * experience): the desktop app, so a phone can take the profile over while the person is out. Resolves to whether\n * it is held now. Left out where the platform cannot (a web page, the extension).\n */\n keepAwake?(on: boolean): Promise<boolean>;\n}\n\n/** One approval's cookie session, outside the page: its cookies never reach the page, and `close` forgets them. */\nexport interface PubkyCookieSession {\n fetch(request: { url: string; method: string; headers: [string, string][]; body: Uint8Array | null }): Promise<{ status: number; headers: [string, string][]; body: Uint8Array }>;\n close(): void;\n}\n\n/** Where a share sheet points: the button that opened it, in CSS pixels from the page's top left. */\nexport interface ShareAnchor { x: number; y: number; width: number; height: number }\n\nexport interface OidcHost {\n platform: OidcPlatform;\n /**\n * Call it straight from the click: the web app opens its popup and the\n * extension asks for the `identity` permission before anything is awaited,\n * or the browser blocks them.\n */\n open(): Promise<OidcWindow>;\n}\n\nlet current: BrowserHost | null = null;\n\n/** Must be called before anything in `platform/` is used. */\nexport function setBrowserHost(host: BrowserHost): void {\n current = host;\n}\n\nexport function getBrowserHost(): BrowserHost {\n if (!current) throw new Error(\"No browser host configured\");\n return current;\n}\n\n/**\n * One thing a paste brought, held by the platform: a file (its name) or a picture (no name: the page\n * names it). `read` hands its bytes a step at a time; `done` says the page has what it wants of\n * it (or gave up), so the platform lets its copy go: nothing is read after it.\n */\nexport interface ClipboardFile {\n name: string | null;\n size: number;\n mime: string | null;\n read(offset: number, length: number): Promise<Uint8Array>;\n done?(): void;\n}\n","/**\n * The OpenID Connect providers Ghostly can ask for a signed ID token. A contact\n * verifies that token against the provider's published keys, with no Ghostly\n * server in between, so a provider is only listed when its ID tokens are signed\n * and its keys and sign-in page are reachable from a browser without a client\n * secret. See docs/wisps/3xx-oidc-proofs.md for the providers that are not\n * (Facebook, X, LinkedIn, GitHub) and why.\n *\n * Client IDs are public configuration, identical in every build, because the\n * contact's app checks `aud` against them. They stay empty until the OAuth\n * clients are registered (docs/OIDC-PROVIDERS.md); a provider without a client\n * ID is neither offered nor accepted.\n */\n\nexport type OidcProviderId = 'google' | 'microsoft' | 'apple' | 'gitlab' | 'twitch' | 'test';\nexport type OidcAlg = 'RS256' | 'ES256';\nexport type OidcPlatform = 'web' | 'extension' | 'desktop';\n/** What the person chooses to put in the token beyond the account identifier. */\nexport type OidcReveal = 'email' | 'profile';\n\nexport interface OidcProvider {\n id: OidcProviderId;\n name: string;\n /** Exact `iss`. Microsoft's contains the tenant: see `issuerFor`. */\n issuer: string;\n /** Other spellings the provider documents for the same issuer. */\n issuerAliases?: string[];\n discovery: string;\n authorizationEndpoint: string;\n /** Pinned rather than discovered, so a changed discovery document cannot redirect key lookup. */\n jwksUri: string;\n /** Only for `code`: the token endpoint, reached with PKCE and no secret. */\n tokenEndpoint?: string;\n /** Best effort after a `code` exchange: the access token Ghostly never wanted is revoked at once. */\n revocationEndpoint?: string;\n /** Pinned per provider; the token header's `alg` must be one of these. */\n algs: OidcAlg[];\n /**\n * `id_token` returns only the signed ID token in the fragment: no access\n * token, no code, nothing to exchange, so no secret. `code id_token` is\n * Apple's shape (its code needs a secret; Ghostly drops it). `code` is for\n * providers without an ID-token-only response, exchanged with PKCE.\n */\n responseType: 'id_token' | 'code id_token' | 'code';\n /** Which extra claims this provider can add, and the scope (or claims request) for each. */\n reveal: Partial<Record<OidcReveal, { scope?: string; claims?: string[] }>>;\n /** What the provider puts in the token whatever the person picks. Shown before sign-in. */\n alwaysIncluded: string;\n /** Extra authorization parameters, e.g. an account chooser. */\n extraParams?: Record<string, string>;\n /** Client IDs per platform. Desktop signs in through the web callback page, so it uses the web client. */\n clientIds: Partial<Record<OidcPlatform, string>>;\n}\n\nexport const TEST_ISSUER = /^https:\\/\\/[a-z0-9-]+(\\.[a-z0-9-]+)*\\.test$/;\nconst MICROSOFT_TENANT = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;\n\nexport const OIDC_PROVIDERS: Record<Exclude<OidcProviderId, 'test'>, OidcProvider> = {\n google: {\n id: 'google', name: 'Google',\n issuer: 'https://accounts.google.com', issuerAliases: ['accounts.google.com'],\n discovery: 'https://accounts.google.com/.well-known/openid-configuration',\n authorizationEndpoint: 'https://accounts.google.com/o/oauth2/v2/auth',\n jwksUri: 'https://www.googleapis.com/oauth2/v3/certs',\n algs: ['RS256'], responseType: 'id_token',\n reveal: { email: { scope: 'email' }, profile: { scope: 'profile' } },\n alwaysIncluded: 'A Google account identifier.',\n extraParams: { prompt: 'select_account' },\n clientIds: { web: '', extension: '' },\n },\n microsoft: {\n // `common` takes work, school and personal accounts; the issuer names the account's tenant.\n id: 'microsoft', name: 'Microsoft',\n issuer: 'https://login.microsoftonline.com/{tenantid}/v2.0',\n discovery: 'https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration',\n authorizationEndpoint: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',\n jwksUri: 'https://login.microsoftonline.com/common/discovery/v2.0/keys',\n algs: ['RS256'], responseType: 'id_token',\n reveal: { email: { scope: 'email' }, profile: { scope: 'profile' } },\n alwaysIncluded: 'A Microsoft account identifier and the directory (tenant) the account belongs to.',\n extraParams: { prompt: 'select_account', response_mode: 'fragment' },\n clientIds: { web: '', extension: '' },\n },\n apple: {\n // Apple only returns the ID token in the fragment when no scope is asked\n // for; name and email need `form_post`, which a static page cannot read.\n id: 'apple', name: 'Apple',\n issuer: 'https://appleid.apple.com',\n discovery: 'https://appleid.apple.com/.well-known/openid-configuration',\n authorizationEndpoint: 'https://appleid.apple.com/auth/authorize',\n jwksUri: 'https://appleid.apple.com/auth/keys',\n algs: ['RS256'], responseType: 'code id_token',\n reveal: {},\n alwaysIncluded: 'An Apple ID identifier that only Ghostly sees. Apple cannot add your email here without a Ghostly server.',\n extraParams: { response_mode: 'fragment' },\n clientIds: { web: '', extension: '' },\n },\n gitlab: {\n id: 'gitlab', name: 'GitLab',\n issuer: 'https://gitlab.com',\n discovery: 'https://gitlab.com/.well-known/openid-configuration',\n authorizationEndpoint: 'https://gitlab.com/oauth/authorize',\n tokenEndpoint: 'https://gitlab.com/oauth/token',\n revocationEndpoint: 'https://gitlab.com/oauth/revoke',\n jwksUri: 'https://gitlab.com/oauth/discovery/keys',\n algs: ['RS256'], responseType: 'code',\n reveal: { email: { scope: 'email' }, profile: { scope: 'profile' } },\n alwaysIncluded: 'A GitLab.com user ID and the groups you belong to directly: GitLab adds them to every ID token.',\n clientIds: { web: '', extension: '' },\n },\n twitch: {\n // Twitch's email claim needs `user:read:email`, an API scope: not asked for.\n id: 'twitch', name: 'Twitch',\n issuer: 'https://id.twitch.tv/oauth2',\n discovery: 'https://id.twitch.tv/oauth2/.well-known/openid-configuration',\n authorizationEndpoint: 'https://id.twitch.tv/oauth2/authorize',\n jwksUri: 'https://id.twitch.tv/oauth2/keys',\n algs: ['RS256'], responseType: 'id_token',\n reveal: { profile: { claims: ['preferred_username'] } },\n alwaysIncluded: 'A Twitch user ID.',\n extraParams: { force_verify: 'true' },\n clientIds: { web: '', extension: '' },\n },\n};\n\n/**\n * The local issuer the e2e suite runs (e2e/support/oidcIssuer.ts). It exists\n * only in a build made with VITE_OIDC_TEST_ISSUER, and only on a `.test` host\n * (a name reserved for testing, never a real issuer); a release build has no\n * such provider, so a token from it is refused like any unknown issuer.\n */\nexport function testProvider(issuer: string): OidcProvider {\n if (!TEST_ISSUER.test(issuer)) throw new Error('A test issuer lives on a .test host');\n const base = issuer;\n return {\n id: 'test', name: 'Test issuer', issuer: base,\n discovery: `${base}/.well-known/openid-configuration`,\n authorizationEndpoint: `${base}/authorize`,\n jwksUri: `${base}/jwks`,\n algs: ['RS256', 'ES256'], responseType: 'id_token',\n reveal: { email: { scope: 'email' }, profile: { scope: 'profile' } },\n alwaysIncluded: 'A test account identifier.',\n clientIds: { web: 'ghostly-e2e', extension: 'ghostly-e2e', desktop: 'ghostly-e2e' },\n };\n}\n\nfunction configuredTestIssuer(): string | undefined {\n try {\n const value = import.meta.env?.VITE_OIDC_TEST_ISSUER as string | undefined;\n return value && TEST_ISSUER.test(value) ? value : undefined;\n } catch { return undefined; }\n}\n\n/** Every provider this build knows, configured or not. */\nexport function oidcProviders(testIssuer = configuredTestIssuer()): OidcProvider[] {\n return [...Object.values(OIDC_PROVIDERS), ...(testIssuer ? [testProvider(testIssuer)] : [])];\n}\n\nexport function oidcProvider(id: string, testIssuer = configuredTestIssuer()): OidcProvider | undefined {\n return oidcProviders(testIssuer).find(p => p.id === id);\n}\n\n/** Every client ID this provider has, on any platform: a contact may use another platform than the prover. */\nexport function audiences(provider: OidcProvider): string[] {\n return [...new Set(Object.values(provider.clientIds).filter((id): id is string => !!id))];\n}\n\nexport function clientIdFor(provider: OidcProvider, platform: OidcPlatform): string {\n return provider.clientIds[platform] || provider.clientIds.web || '';\n}\n\n/** A provider is offered only once its client ID for this platform exists. */\nexport function isConfigured(provider: OidcProvider, platform: OidcPlatform): boolean {\n return !!clientIdFor(provider, platform);\n}\n\n/** The exact issuer a token from this provider must carry, or undefined when its claims do not name one. */\nexport function issuerFor(provider: OidcProvider, claims: { tid?: unknown }): string[] {\n if (provider.id !== 'microsoft') return [provider.issuer, ...(provider.issuerAliases ?? [])];\n return typeof claims.tid === 'string' && MICROSOFT_TENANT.test(claims.tid) ? [provider.issuer.replace('{tenantid}', claims.tid)] : [];\n}\n\n/** The provider a proof's subject (an issuer, as `OidcProvider.issuer` spells it) names. */\nexport function providerForIssuer(issuer: string, list = oidcProviders()): OidcProvider | undefined {\n return list.find(p => p.issuer === issuer);\n}\n","/**\n * A provider's signing keys, fetched from its pinned JWKS URL with limits and\n * cached. A key ID that is not in the cached set triggers one early refetch\n * (providers rotate keys), rate-limited so that a stream of made-up key IDs\n * cannot turn a contact's app into a request generator.\n */\n\nexport interface Jwk {\n kty: string; kid?: string; use?: string; alg?: string; key_ops?: string[];\n n?: string; e?: string; crv?: string; x?: string; y?: string;\n /** Microsoft adds the issuer each key signs for. */\n issuer?: string;\n}\n/**\n * How keys are fetched. In `verify` this is the engine's `ctx.fetch` (HTTPS GET, no credentials, no\n * redirects, time-out, size cap); `cacheControl` is used when the fetcher reports it.\n */\nexport type JwksFetch = (url: string, options: { maxBytes: number }) => Promise<{ status: number; text: string; cacheControl?: string | null }>;\n\nexport const JWKS_MAX_BYTES = 64 * 1024;\nexport const JWKS_MAX_KEYS = 32;\nconst MIN_TTL_MS = 5 * 60_000;\nconst MAX_TTL_MS = 24 * 60 * 60_000;\nconst DEFAULT_TTL_MS = 60 * 60_000;\n/** A missing `kid` refetches at most this often per URL. */\nexport const REFETCH_INTERVAL_MS = 60_000;\n\ninterface Entry { keys: Jwk[]; expires: number; fetched: number }\n\nfunction allowedUrl(url: string): boolean {\n try {\n const u = new URL(url);\n return u.protocol === 'https:' && !u.username && !u.password && !u.hash;\n } catch { return false; }\n}\n\nfunction ttl(cacheControl: string | null | undefined): number {\n const maxAge = /(?:^|[,\\s])max-age=(\\d+)/i.exec(cacheControl ?? '')?.[1];\n const ms = maxAge ? Number(maxAge) * 1000 : DEFAULT_TTL_MS;\n return Math.min(MAX_TTL_MS, Math.max(MIN_TTL_MS, ms));\n}\n\nfunction parseKeys(text: string): Jwk[] {\n if (text.length > JWKS_MAX_BYTES) throw new Error('Provider key set too large');\n let doc: { keys?: unknown };\n try { doc = JSON.parse(text) as { keys?: unknown }; } catch { throw new Error('Invalid provider key set'); }\n if (!doc || !Array.isArray(doc.keys) || doc.keys.length > JWKS_MAX_KEYS) throw new Error('Invalid provider key set');\n return doc.keys.filter((k): k is Jwk => !!k && typeof k === 'object' && typeof (k as Jwk).kty === 'string');\n}\n\nexport class JwksCache {\n private entries = new Map<string, Entry>();\n private inflight = new Map<string, Promise<Entry>>();\n constructor(private options: { now?: () => number } = {}) {}\n private now() { return (this.options.now ?? Date.now)(); }\n\n private load(url: string, fetcher: JwksFetch): Promise<Entry> {\n const running = this.inflight.get(url);\n if (running) return running;\n const task = (async () => {\n if (!allowedUrl(url)) throw new Error('Provider key URL refused');\n const response = await fetcher(url, { maxBytes: JWKS_MAX_BYTES });\n if (response.status !== 200) throw new Error(`Provider keys unavailable (${response.status})`);\n const keys = parseKeys(response.text);\n const fetched = this.now();\n const entry = { keys, fetched, expires: fetched + ttl(response.cacheControl) };\n this.entries.set(url, entry);\n return entry;\n })();\n this.inflight.set(url, task);\n return task.finally(() => this.inflight.delete(url));\n }\n\n /** The key with this `kid`, refetching once when it is unknown (rotation) or the cache expired. */\n async key(url: string, kid: string, fetcher: JwksFetch): Promise<Jwk | undefined> {\n let entry = this.entries.get(url);\n if (!entry || entry.expires <= this.now()) entry = await this.load(url, fetcher);\n let found = entry.keys.find(k => k.kid === kid);\n if (!found && this.now() - entry.fetched >= REFETCH_INTERVAL_MS) {\n entry = await this.load(url, fetcher);\n found = entry.keys.find(k => k.kid === kid);\n }\n return found;\n }\n\n clear(): void { this.entries.clear(); }\n}\n\n/** One cache per app: every contact's proofs share the providers' keys. */\nexport const sharedJwksCache = new JwksCache();\n","import { fromBase64Url, toBase64Url, utf8Decode, utf8Encode } from '@ghostly/core';\nimport { audiences, issuerFor, type OidcAlg, type OidcProvider, type OidcProviderId } from './providers';\nimport { sharedJwksCache, type Jwk, type JwksCache, type JwksFetch } from './jwks';\n\n/**\n * Checks an ID token the way a contact must: the provider's signature under\n * a key from its own JWKS, the pinned `alg`, the issuer, `aud` = one of\n * Ghostly's client IDs for that provider, the time window, and `nonce` = the\n * hash of the exact Ghostly proof statement. Nothing here talks to a Ghostly\n * server, and nothing here trusts what the token says about its own key.\n */\n\n/** Seconds of clock difference accepted on `iat`, `nbf` and `exp`. */\nexport const CLOCK_TOLERANCE = 60;\n/** An ID token that claims to live longer than a day is not a sign-in result. */\nconst MAX_LIFETIME = 24 * 60 * 60;\n/** Room for Microsoft's larger tokens, well inside a peer frame. */\nexport const MAX_ID_TOKEN_LENGTH = 6144;\nconst SEGMENT = /^[A-Za-z0-9_-]+$/;\nconst REFUSED_HEADERS = ['crit', 'jku', 'jwk', 'x5u', 'x5c', 'zip', 'enc'];\n\nexport class OidcVerificationError extends Error {\n constructor(message: string) { super(message); this.name = 'OidcVerificationError'; }\n}\nconst refuse = (message: string): never => { throw new OidcVerificationError(message); };\n\n/** What a contact is shown: only claims the provider signed, bounded. */\nexport interface OidcIdentity {\n provider: OidcProviderId;\n issuer: string;\n subject: string;\n audience: string;\n issuedAt: number;\n expiresAt: number;\n email?: string;\n emailVerified?: boolean;\n name?: string;\n username?: string;\n /** Microsoft's directory (tenant) ID. */\n tenant?: string;\n}\n\nfunction segment(value: string): Uint8Array {\n if (!SEGMENT.test(value)) refuse('Malformed ID token');\n let bytes: Uint8Array;\n try { bytes = fromBase64Url(value); } catch { return refuse('Malformed ID token'); }\n if (toBase64Url(bytes) !== value) refuse('Malformed ID token');\n return bytes;\n}\n\nfunction json(value: string): Record<string, unknown> {\n let parsed: unknown;\n try { parsed = JSON.parse(utf8Decode(segment(value))); } catch (e) { if (e instanceof OidcVerificationError) throw e; return refuse('Malformed ID token'); }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) refuse('Malformed ID token');\n return parsed as Record<string, unknown>;\n}\n\nconst text = (value: unknown, max: number): string | undefined =>\n typeof value === 'string' && value.length > 0 && value.length <= max ? value : undefined;\n\n/** Splits and decodes without verifying anything. Only for display of a token already verified. */\nexport function decodeIdToken(token: string): { header: Record<string, unknown>; claims: Record<string, unknown>; signature: Uint8Array; signed: Uint8Array } {\n if (typeof token !== 'string' || token.length > MAX_ID_TOKEN_LENGTH) refuse('ID token missing or too large');\n const parts = token.split('.');\n if (parts.length !== 3) refuse('Not a signed ID token');\n return { header: json(parts[0]), claims: json(parts[1]), signature: segment(parts[2]), signed: utf8Encode(`${parts[0]}.${parts[1]}`) };\n}\n\nasync function verifySignature(alg: OidcAlg, jwk: Jwk, signed: Uint8Array, signature: Uint8Array): Promise<boolean> {\n if (jwk.use !== undefined && jwk.use !== 'sig') refuse('Provider key is not a signing key');\n if (jwk.alg !== undefined && jwk.alg !== alg) refuse('Provider key is for another algorithm');\n if (jwk.key_ops !== undefined && !(Array.isArray(jwk.key_ops) && jwk.key_ops.includes('verify'))) refuse('Provider key is not a signing key');\n let key: CryptoKey;\n if (alg === 'RS256') {\n if (jwk.kty !== 'RSA' || typeof jwk.n !== 'string' || typeof jwk.e !== 'string') return refuse('Provider key does not match the token algorithm');\n const modulus = segment(jwk.n);\n const bits = (modulus.length - (modulus[0] === 0 ? 1 : 0)) * 8;\n if (bits < 2048 || bits > 8192) refuse('Provider key size refused');\n key = await crypto.subtle.importKey('jwk', { kty: 'RSA', n: jwk.n, e: jwk.e, ext: true }, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' }, false, ['verify']);\n return crypto.subtle.verify('RSASSA-PKCS1-v1_5', key, signature as BufferSource, signed as BufferSource);\n }\n if (jwk.kty !== 'EC' || jwk.crv !== 'P-256' || typeof jwk.x !== 'string' || typeof jwk.y !== 'string') return refuse('Provider key does not match the token algorithm');\n if (signature.length !== 64) refuse('Invalid ID token signature');\n key = await crypto.subtle.importKey('jwk', { kty: 'EC', crv: 'P-256', x: jwk.x, y: jwk.y, ext: true }, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['verify']);\n return crypto.subtle.verify({ name: 'ECDSA', hash: 'SHA-256' }, key, signature as BufferSource, signed as BufferSource);\n}\n\n/** Whether `iss` is this provider's, as it words it (Microsoft: with the token's own tenant). */\nexport function issuerMatches(provider: OidcProvider, claims: Record<string, unknown>): boolean {\n const iss = text(claims.iss, 512);\n return !!iss && issuerFor(provider, claims).includes(iss);\n}\n\nexport interface VerifyOptions {\n provider: OidcProvider;\n /** The nonce the sign-in was asked for: an identity proof's `statement.id`. */\n nonce: string;\n /** How the provider's keys are fetched: `ctx.fetch` in a proof's `verify`. */\n fetch: JwksFetch;\n /** Unix seconds. */\n now?: number;\n jwks?: JwksCache;\n /**\n * `[from, to]` in Unix seconds: the token must have been issued in this window. For a proof, the\n * sign-in that followed the statement; the token's own `exp` (an hour) then does not matter, the\n * statement's validity does. Without it, the token must still be unexpired.\n */\n issuedWithin?: [number, number];\n}\n\nexport async function verifyIdToken(token: string, options: VerifyOptions): Promise<OidcIdentity> {\n const { provider, nonce } = options;\n const now = options.now ?? Math.floor(Date.now() / 1000);\n const { header, claims, signature, signed } = decodeIdToken(token);\n\n // The algorithm is the provider's, never the token's choice: `none`, HS256\n // (a public key used as an HMAC secret) and anything unpinned stop here.\n const alg = header.alg;\n if (typeof alg !== 'string' || !provider.algs.includes(alg as OidcAlg)) refuse(`Algorithm ${typeof alg === 'string' ? alg : 'missing'} is not accepted for ${provider.name}`);\n if (header.typ !== undefined && (typeof header.typ !== 'string' || header.typ.toUpperCase() !== 'JWT')) refuse('Not an ID token');\n for (const name of REFUSED_HEADERS) if (name in header) refuse('ID token header refused');\n const kid = text(header.kid, 256) ?? refuse('ID token names no provider key');\n\n if (!issuerMatches(provider, claims)) refuse(`Token was not issued by ${provider.name}`);\n const iss = claims.iss as string;\n\n const expected = audiences(provider);\n if (!expected.length) refuse(`${provider.name} is not configured in this Ghostly build`);\n const aud = typeof claims.aud === 'string' ? [claims.aud] : Array.isArray(claims.aud) && claims.aud.every(a => typeof a === 'string') ? claims.aud as string[] : [];\n const audience = aud.find(a => expected.includes(a)) ?? refuse('Token was issued to another app');\n if (aud.length > 1 && claims.azp !== audience) refuse('Token was issued to another app');\n if (claims.azp !== undefined && claims.azp !== audience) refuse('Token was issued to another app');\n\n const iat = claims.iat, exp = claims.exp, nbf = claims.nbf;\n if (typeof iat !== 'number' || typeof exp !== 'number' || !Number.isFinite(iat) || !Number.isFinite(exp)) return refuse('Token has no validity window');\n if (exp <= iat || exp - iat > MAX_LIFETIME) refuse('Token validity window refused');\n if (iat > now + CLOCK_TOLERANCE) refuse('Token is not valid yet');\n if (options.issuedWithin) {\n const [from, to] = options.issuedWithin;\n if (iat < from || iat > to) refuse('The sign-in does not belong to this proof: it happened at another time');\n } else {\n if (typeof nbf === 'number' && nbf > now + CLOCK_TOLERANCE) refuse('Token is not valid yet');\n if (exp + CLOCK_TOLERANCE < now) refuse('Token expired');\n }\n\n if (typeof claims.nonce !== 'string' || claims.nonce !== nonce) refuse('Token answers another proof request');\n const subject = text(claims.sub, 255) ?? refuse('Token names no account');\n\n const jwk = await (options.jwks ?? sharedJwksCache).key(provider.jwksUri, kid, options.fetch) ?? refuse(`${provider.name} does not publish the key that signed this token`);\n if (provider.id === 'microsoft' && jwk.issuer !== undefined && jwk.issuer !== provider.issuer && jwk.issuer !== iss) refuse('Provider key belongs to another issuer');\n let valid = false;\n try { valid = await verifySignature(alg as OidcAlg, jwk, signed, signature); }\n catch (e) { if (e instanceof OidcVerificationError) throw e; refuse('Invalid ID token signature'); }\n if (!valid) refuse('Invalid ID token signature');\n\n const verified = claims.email_verified === true || claims.email_verified === 'true' ? true : claims.email_verified === false || claims.email_verified === 'false' ? false : undefined;\n return {\n provider: provider.id, issuer: iss, subject, audience, issuedAt: Math.floor(iat), expiresAt: Math.floor(exp),\n email: text(claims.email, 320), emailVerified: text(claims.email, 320) ? verified : undefined,\n name: text(claims.name, 200), username: text(claims.preferred_username, 200) ?? text(claims.nickname, 200),\n tenant: provider.id === 'microsoft' ? text(claims.tid, 64) : undefined,\n };\n}\n","import { randomBytes, toBase64Url, utf8Encode } from '@ghostly/core';\nimport { clientIdFor, type OidcPlatform, type OidcProvider, type OidcReveal } from './providers';\nimport { decodeIdToken, issuerMatches } from './verify';\n\n/**\n * The sign-in half: send the person to the provider with the identity proof's\n * statement id as `nonce`, and take back only the ID token. The token's\n * signature is checked by the proof's `verify`, before saving and on every\n * contact's device; here only that it answers this request. Access and refresh tokens are never requested\n * where the provider allows it, and dropped (and revoked) where it does not.\n */\n\n/** How a platform shows the provider's page and hands back where it redirected. */\nexport interface OidcWindow {\n /** The redirect URI registered for this platform. */\n redirectUri: string;\n /** Extra state prefix the platform needs to route the answer back (desktop: its loopback port). */\n statePrefix?: string;\n /** Opens `url` and resolves with the full URL the provider redirected to. */\n authorize(url: string, signal: AbortSignal): Promise<string>;\n /** Releases the window or listener, answered or not. */\n close(): void;\n}\n\nexport interface OidcRequest {\n url: string;\n state: string;\n nonce: string;\n redirectUri: string;\n clientId: string;\n /** PKCE verifier, only for `code`. Held in memory for the one exchange. */\n verifier?: string;\n}\n\n/** Optional platform prefix (desktop `d.<port>.`, Android deep link `a.`), then 32 random bytes. */\nconst STATE = /^(?:d\\.\\d{1,5}\\.|a\\.)?[A-Za-z0-9_-]{43}$/;\n\nexport function scopeFor(provider: OidcProvider, reveal: OidcReveal[]): { scope: string; claims?: string } {\n const scopes = provider.id === 'apple' ? [] : ['openid'];\n const claims: string[] = [];\n for (const r of reveal) {\n const option = provider.reveal[r];\n if (!option) continue;\n if (option.scope) scopes.push(option.scope);\n if (option.claims) claims.push(...option.claims);\n }\n return { scope: scopes.join(' '), claims: claims.length ? JSON.stringify({ id_token: Object.fromEntries(claims.map(c => [c, null])) }) : undefined };\n}\n\nexport async function authorizationRequest(options: { provider: OidcProvider; platform: OidcPlatform; nonce: string; reveal: OidcReveal[]; redirectUri: string; statePrefix?: string }): Promise<OidcRequest> {\n const { provider } = options;\n const clientId = clientIdFor(provider, options.platform);\n if (!clientId) throw new Error(`${provider.name} sign-in is not set up in this Ghostly build yet.`);\n const { nonce } = options;\n if (!/^[A-Za-z0-9_-]{16,128}$/.test(nonce)) throw new Error('Invalid sign-in nonce');\n const state = `${options.statePrefix ?? ''}${toBase64Url(randomBytes(32))}`;\n const url = new URL(provider.authorizationEndpoint);\n const { scope, claims } = scopeFor(provider, options.reveal);\n const params: Record<string, string> = {\n client_id: clientId, redirect_uri: options.redirectUri, response_type: provider.responseType,\n nonce, state, ...(scope ? { scope } : {}), ...(claims ? { claims } : {}), ...provider.extraParams,\n };\n let verifier: string | undefined;\n if (provider.responseType === 'code') {\n verifier = toBase64Url(randomBytes(32));\n params.code_challenge = toBase64Url(new Uint8Array(await crypto.subtle.digest('SHA-256', utf8Encode(verifier) as BufferSource)));\n params.code_challenge_method = 'S256';\n }\n for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);\n return { url: url.toString(), state, nonce, redirectUri: options.redirectUri, clientId, verifier };\n}\n\n/** Parameters from the redirect: the fragment for implicit responses, the query for `code`. */\nexport function callbackParams(redirected: string, redirectUri: string): URLSearchParams {\n const url = new URL(redirected), expected = new URL(redirectUri);\n if (url.origin !== expected.origin || url.pathname !== expected.pathname) throw new Error('Sign-in returned to an unexpected address.');\n const params = new URLSearchParams(url.hash.slice(1));\n for (const [k, v] of new URLSearchParams(url.search)) if (!params.has(k)) params.set(k, v);\n return params;\n}\n\nconst PROVIDER_ERRORS: Record<string, string> = {\n access_denied: 'Sign-in was cancelled.',\n login_required: 'Sign in to your account and try again.',\n consent_required: 'Consent was not given.',\n interaction_required: 'The provider needs you to finish signing in.',\n unauthorized_client: 'This provider has not accepted Ghostly’s sign-in yet.',\n invalid_request: 'The provider refused the sign-in request.',\n};\n\nasync function exchangeCode(provider: OidcProvider, request: OidcRequest, code: string, doFetch: typeof fetch): Promise<string> {\n if (!provider.tokenEndpoint || !request.verifier) throw new Error('This provider cannot finish sign-in here.');\n const response = await doFetch(provider.tokenEndpoint, {\n method: 'POST', redirect: 'error', credentials: 'omit', referrerPolicy: 'no-referrer', cache: 'no-store',\n headers: { 'content-type': 'application/x-www-form-urlencoded', accept: 'application/json' },\n body: new URLSearchParams({ grant_type: 'authorization_code', code, redirect_uri: request.redirectUri, client_id: request.clientId, code_verifier: request.verifier }),\n });\n const body = await response.json().catch(() => ({})) as { id_token?: unknown; access_token?: unknown; refresh_token?: unknown };\n // Only the ID token leaves this function. The others were never wanted:\n // revoke them without waiting, and let them go out of scope.\n for (const token of [body.refresh_token, body.access_token]) {\n if (typeof token === 'string' && provider.revocationEndpoint)\n void doFetch(provider.revocationEndpoint, { method: 'POST', redirect: 'error', credentials: 'omit', referrerPolicy: 'no-referrer',\n headers: { 'content-type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ token, client_id: request.clientId }) }).catch(() => {});\n }\n if (!response.ok || typeof body.id_token !== 'string') throw new Error(`${provider.name} did not return an ID token.`);\n return body.id_token;\n}\n\n/** Turns the redirect into the ID token: state, provider error, code exchange if needed, and that it answers this request. */\nexport async function completeSignIn(provider: OidcProvider, request: OidcRequest, redirected: string, options: { fetch?: typeof fetch } = {}): Promise<string> {\n const params = callbackParams(redirected, request.redirectUri);\n const state = params.get('state');\n if (!state || !STATE.test(state) || state !== request.state) throw new Error('Sign-in answer does not belong to this request.');\n const error = params.get('error');\n if (error) throw new Error(PROVIDER_ERRORS[error] ?? 'The provider refused the sign-in.');\n let idToken: string | null;\n if (provider.responseType === 'code') {\n const code = params.get('code');\n if (!code || code.length > 2048) throw new Error(`${provider.name} did not return a sign-in code.`);\n idToken = await exchangeCode(provider, request, code, options.fetch ?? fetch);\n } else idToken = params.get('id_token');\n // Apple's code needs a client secret to redeem, which Ghostly does not have: it is dropped with the rest.\n if (!idToken) throw new Error(`${provider.name} did not return an ID token.`);\n const { claims } = decodeIdToken(idToken);\n if (claims.nonce !== request.nonce || !issuerMatches(provider, claims)) throw new Error(`${provider.name} answered another sign-in request.`);\n return idToken;\n}\n\n/** Everything the prover does: sign in on this platform and return the ID token for this nonce. */\nexport async function signInForProof(options: {\n provider: OidcProvider; platform: OidcPlatform; nonce: string; reveal: OidcReveal[];\n window: OidcWindow; signal: AbortSignal; fetch?: typeof fetch;\n}): Promise<string> {\n const { window: w } = options;\n try {\n options.signal.throwIfAborted();\n const request = await authorizationRequest({ ...options, redirectUri: w.redirectUri, statePrefix: w.statePrefix });\n const redirected = await w.authorize(request.url, options.signal);\n options.signal.throwIfAborted();\n return await completeSignIn(options.provider, request, redirected, options);\n } finally { w.close(); }\n}\n","import { IDENTITY_CLOCK_SKEW, type IdentityStatement } from \"@ghostly/core\";\nimport type { IdentityProofProvider, IdentityPlatform, RedirectSigner } from \"../contract\";\nimport { getBrowserHost, type OidcHost } from \"../../host\";\nimport { audiences, isConfigured, oidcProviders, providerForIssuer, type OidcProvider, type OidcReveal } from \"../oidc/providers\";\nimport { signInForProof } from \"../oidc/flow\";\nimport { MAX_ID_TOKEN_LENGTH, verifyIdToken } from \"../oidc/verify\";\nimport { JwksCache, sharedJwksCache } from \"../oidc/jwks\";\n\n/**\n * Identity proofs through OpenID Connect: the person signs in with a provider (Google, Microsoft,\n * Apple, GitLab, Twitch) whose ID token carries the statement id as `nonce`. The token is the evidence;\n * a contact's app checks the provider's signature against its published keys, the issuer, Ghostly's\n * client ID, the nonce and when the sign-in happened. No Ghostly server is involved, and the proof is\n * only as good as the provider's word: it is attested, not a key the person holds.\n *\n * The statement's subject is the issuer the person picked; the account comes out of `verify`.\n * Configuration and flows per provider: ../oidc/, docs/OIDC-PROVIDERS.md, docs/wisps/3xx-oidc-proofs.md.\n */\nexport interface OidcEvidence { token: string }\n\n/** How long after the statement the sign-in may have happened: time to pick an account and approve. */\nconst SIGN_IN_WINDOW = 15 * 60;\nconst TOKEN = /^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$/;\nconst SUB = /^[\\x21-\\x7e]{1,255}$/;\n\nfunction currentHost(): OidcHost | undefined {\n try { return getBrowserHost().oidc; } catch { return undefined; }\n}\n\nconst hostOf = (issuer: string) => { try { return new URL(issuer).host; } catch { return issuer; } };\n\nexport interface OidcIdentityOptions {\n /** The OIDC providers this build knows (tests pass configured ones). */\n providers?: () => OidcProvider[];\n /** The platform's sign-in window. */\n host?: () => OidcHost | undefined;\n /** Provider keys, cached across proofs. */\n jwks?: JwksCache;\n /** The UI-side fetch for GitLab's code exchange. */\n fetch?: typeof fetch;\n}\n\nexport function createOidcIdentityProvider(options: OidcIdentityOptions = {}): IdentityProofProvider<OidcEvidence> {\n const providers = options.providers ?? (() => oidcProviders());\n const host = options.host ?? currentHost;\n const configured = () => providers().filter(p => audiences(p).length > 0);\n\n const signer = (id: string, label: string, description: string, reveal: OidcReveal[]): RedirectSigner<OidcEvidence> => ({\n id, kind: \"redirect\", label, description,\n available: () => { const h = host(); return !!h && providers().some(p => isConfigured(p, h.platform)); },\n start(statement: IdentityStatement, ctx) {\n // Before any await: the popup and the extension's permission prompt need the click's user activation.\n const h = host();\n const opening = h?.open();\n return (async () => {\n if (!h || !opening) throw new Error(\"Signing in with a provider is not available here.\");\n const provider = providerForIssuer(statement.binding.subject, providers());\n if (!provider || !isConfigured(provider, h.platform)) { void opening.then(w => w.close(), () => {}); throw new Error(\"This provider is not set up in this Ghostly build.\"); }\n ctx.onProgress(`Sign in with ${provider.name} in the window that opened.`);\n const token = await signInForProof({\n provider, platform: h.platform, nonce: statement.id, window: await opening, signal: ctx.signal, fetch: options.fetch,\n // What the provider cannot put in its token is not asked for.\n reveal: reveal.filter(r => provider.reveal[r]),\n });\n return { token };\n })();\n },\n });\n\n return {\n id: \"oidc\",\n label: \"Account at a provider\",\n category: \"provider-attested\",\n summary: \"Sign in with Google, Microsoft, Apple…\",\n description: \"Sign in with Google, Microsoft, Apple, GitLab or Twitch: the provider vouches to Ghostly, with a signed token, that the account is yours.\",\n limits: \"Attested by the provider, not by a key you hold: your contact trusts that company.\",\n platforms: [\"web\", \"extension\", \"desktop\"] satisfies IdentityPlatform[],\n subject: {\n label: \"Provider\",\n help: \"Your contact sees your account’s identifier at that provider, plus what you choose to add. GitLab also adds the groups you belong to directly; Apple never gives Ghostly your email.\",\n get options() { return configured().map(p => ({ value: p.issuer, label: p.name })); },\n normalize(input: string) {\n const provider = providerForIssuer(input.trim(), configured());\n if (!provider) throw new Error(\"Choose a provider\");\n return provider.issuer;\n },\n short: subject => providerForIssuer(subject, providers())?.name ?? hostOf(subject),\n },\n // Providers rotate their signing keys within weeks (Google about every two); a contact can only\n // check a token while its key is still published, so a proof is short-lived.\n validity: { defaultDays: 7, maxDays: 14 },\n privacy: \"Your contact’s app downloads the provider’s public keys (for example from www.googleapis.com), so the provider sees that some app checked a sign-in, not whose. Your contact sees the account identifier and what you chose to share.\",\n signers: [\n signer(\"oidc-account\", \"Sign in: share the account only\", \"The token carries the provider’s identifier for your account, nothing else.\", []),\n signer(\"oidc-email\", \"Sign in: share the account and email\", \"Adds your email address, where the provider offers it.\", [\"email\"]),\n signer(\"oidc-profile\", \"Sign in: share the account, email and name\", \"Adds your email address and name or username, where the provider offers them.\", [\"email\", \"profile\"]),\n ],\n parseEvidence(raw) {\n const e = raw as OidcEvidence;\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw) || Object.keys(raw).join(\",\") !== \"token\" ||\n typeof e.token !== \"string\" || e.token.length > MAX_ID_TOKEN_LENGTH || !TOKEN.test(e.token)) throw new Error(\"Invalid sign-in token\");\n return { token: e.token };\n },\n async verify(statement, { token }, ctx) {\n const provider = providerForIssuer(statement.binding.subject, providers());\n if (!provider) throw new Error(\"This app does not know this provider\");\n const id = await verifyIdToken(token, {\n provider, nonce: statement.id, now: ctx.now, jwks: options.jwks ?? sharedJwksCache,\n fetch: async (url, { maxBytes }) => { const r = await ctx.fetch(url, { maxBytes, signal: ctx.signal }); return { status: r.status, text: r.text }; },\n issuedWithin: [statement.binding.issuedAt - IDENTITY_CLOCK_SKEW, statement.binding.issuedAt + SIGN_IN_WINDOW],\n });\n if (!SUB.test(id.subject)) throw new Error(\"The provider’s account identifier cannot be shown\");\n const attester = hostOf(provider.issuer);\n const person = id.name ?? id.username;\n const name = id.email ? (person && `${person} · ${id.email}`.length <= 64 ? `${person} · ${id.email}` : id.email) : person;\n const url = id.username && /^[A-Za-z0-9_.-]{1,64}$/.test(id.username)\n ? provider.id === \"gitlab\" ? `https://gitlab.com/${id.username}` : provider.id === \"twitch\" ? `https://www.twitch.tv/${id.username}` : undefined\n : undefined;\n return {\n // Microsoft's issuer names the account's tenant; the others are fixed.\n subject: `${provider.id === \"microsoft\" ? id.issuer : provider.issuer}#${id.subject}`,\n attester,\n source: `ID token signed by ${attester}`,\n ...(name || url ? { display: {\n ...(name ? { name } : {}), ...(url ? { url } : {}), fetchedAt: id.issuedAt,\n source: `Signed by ${provider.name}${id.email ? id.emailVerified ? `; email verified by ${provider.name}` : `; email not verified by ${provider.name}` : \"\"}`,\n } } : {}),\n };\n },\n };\n}\n\nexport const oidc = createOidcIdentityProvider();\n","import {\n DidError, DID_DOCUMENT_MAX_BYTES, resolveDidDht, DID_FILE_MAX_BYTES, didWebDocumentUrl, didWebFileUrl, didWebHost, parseDid, parseDidDocument, staticDidDocument,\n type DidDocumentView, type DidMethod,\n} from \"@ghostly/core\";\nimport type { IdentityFetch } from \"./contract\";\nimport { assertPublicHost, chosenResolver, DomainCheckError, type DohResolverId } from \"./domain\";\n\n/**\n * Network side of DID proofs: resolving a DID to the keys its document lists, under the same limits as the\n * other providers. did:key and did:jwk need no network. did:web is one HTTPS GET of its did.json (bounded,\n * no redirect, after checking that the domain's addresses are public, like the domain proof). did:dht is a\n * signed Pkarr record read from a relay (`resolveDidDht`, WISP 310-did-dht).\n *\n * What a check reveals, and to whom:\n * - did:web: the DNS-over-HTTPS resolver learns the domain; the domain's web server, run by the person\n * being checked, sees this device's IP address and when.\n * - did:dht: the Pkarr relay learns which DID was looked up, from which IP address.\n */\n\nexport interface DidResolution {\n method: DidMethod;\n document: DidDocumentView;\n /** did:web: the host, and where its did.json was read. */\n host?: string;\n documentUrl?: string;\n /** did:dht: the relay that answered. */\n relay?: string;\n}\n\nexport interface DidResolveOptions {\n fetch: IdentityFetch;\n signal?: AbortSignal;\n /** Milliseconds. */\n now?: () => number;\n resolver?: DohResolverId;\n /** did:dht resolution; tests pass their own. */\n dht?: typeof resolveDidDht;\n}\n\n/** Resolutions are shared for this long: back-to-back checks and the add dialog's preview, never a stale key for long. */\nconst CACHE_MS = 30_000;\nconst cache = new Map<string, { until: number; result: Promise<DidResolution> }>();\n/** The last document seen per DID, without expiry, for the signer's instructions (which cannot wait). */\nconst seen = new Map<string, DidResolution>();\n\n/** Tests only. */\nexport function clearDidCache(): void { cache.clear(); seen.clear(); }\n\n/** What this app last resolved for `did`, if anything: the add dialog's instructions name its keys. */\nexport const lastDidResolution = (did: string): DidResolution | undefined => seen.get(did);\n\nasync function fetchDidWeb(did: string, url: string, maxBytes: number, options: DidResolveOptions): Promise<string | undefined> {\n const host = didWebHost(did);\n try {\n await assertPublicHost(host, { fetch: options.fetch, signal: options.signal, resolver: options.resolver ?? chosenResolver().id, now: options.now });\n } catch (e) { throw e instanceof DomainCheckError ? new DidError(e.message) : e; }\n let response: Awaited<ReturnType<IdentityFetch>>;\n try {\n response = await options.fetch(url, { headers: { accept: \"application/did+json, application/json\" }, maxBytes, signal: options.signal, redirect: \"error\" });\n } catch (e) {\n if (options.signal?.aborted) throw e;\n throw new DidError(/too large/i.test(String(e)) ? `${url} is too large`\n : `${host} could not be reached. It must serve ${url} over HTTPS, without a redirect, with the header Access-Control-Allow-Origin: *`);\n }\n if (response.status === 404 || response.status === 410) return undefined;\n if (response.status >= 300 && response.status < 400) throw new DidError(`${host} answered with a redirect; it must serve exactly ${url}`);\n if (response.status !== 200) throw new DidError(`${host} answered ${response.status} for ${url}`);\n try { return new TextDecoder(\"utf-8\", { fatal: true }).decode(response.bytes); } catch { throw new DidError(`${url} is not text`); }\n}\n\nasync function resolve(did: string, method: DidMethod, options: DidResolveOptions): Promise<DidResolution> {\n if (method === \"key\" || method === \"jwk\") return { method, document: staticDidDocument(did) };\n if (method === \"web\") {\n const documentUrl = didWebDocumentUrl(did);\n const text = await fetchDidWeb(did, documentUrl, DID_DOCUMENT_MAX_BYTES, options);\n if (text === undefined) throw new DidError(`No DID document at ${documentUrl}`);\n let raw: unknown;\n try { raw = JSON.parse(text); } catch { throw new DidError(`${documentUrl} is not JSON`); }\n return { method, document: parseDidDocument(did, raw), host: didWebHost(did), documentUrl };\n }\n const found = await (options.dht ?? resolveDidDht)(did, { fetch: options.fetch, signal: options.signal });\n if (found.metadata.deactivated) throw new DidError(\"This DID is deactivated: its owner retired it\");\n return { method, document: parseDidDocument(did, found.document), relay: found.relay };\n}\n\n/** A DID's document as far as Ghostly uses it. Concurrent and back-to-back resolutions share one lookup. */\nexport async function resolveDid(input: string, options: DidResolveOptions): Promise<DidResolution> {\n const { did, method } = parseDid(input);\n const now = options.now ?? Date.now;\n const hit = cache.get(did);\n if (hit && hit.until > now()) return hit.result;\n const entry = { until: now() + CACHE_MS, result: resolve(did, method, options) };\n cache.set(did, entry);\n if (cache.size > 64) cache.delete(cache.keys().next().value!);\n try {\n const result = await entry.result;\n seen.delete(did);\n seen.set(did, result);\n if (seen.size > 16) seen.delete(seen.keys().next().value!);\n return result;\n } catch (e) {\n if (cache.get(did) === entry) cache.delete(did);\n throw e;\n }\n}\n\n/** The statement file a did:web publishes for proof `proofId`; undefined when the server says it is not there. */\nexport async function fetchDidWebFile(did: string, proofId: string, options: DidResolveOptions): Promise<string | undefined> {\n return fetchDidWeb(did, didWebFileUrl(did, proofId), DID_FILE_MAX_BYTES, options);\n}\n","import { boundVerifiedIdentity, IDENTITY_MAX_EVIDENCE, IDENTITY_MAX_VALIDITY, type IdentityStatement, type VerifiedIdentity } from \"@ghostly/core\";\nimport type { IdentityFetch, IdentityProofProvider, IdentitySigner, VerifyContext } from \"./contract\";\n\nconst DAY = 86_400;\n\n/**\n * The checks every proof goes through, whatever its provider: known provider, validity within the\n * provider's limit, bounded evidence, strict parsing, the provider's own check, and the category rule\n * (a self-custodied proof proves exactly the subject it names; an attested one says who attests it).\n * The engine verifies a contact's proof, and the person's own proof before saving it, only through this.\n */\nexport async function verifyIdentity(providers: readonly IdentityProofProvider[], statement: IdentityStatement, evidence: unknown, ctx: VerifyContext): Promise<VerifiedIdentity> {\n const { binding } = statement;\n const provider = providers.find(p => p.id === binding.provider);\n if (!provider) throw new Error(\"This app cannot verify this kind of identity\");\n if (binding.expiresAt - binding.issuedAt > Math.min(provider.validity.maxDays * DAY, IDENTITY_MAX_VALIDITY)) throw new Error(\"The proof lasts longer than this kind of identity allows\");\n if (JSON.stringify(evidence ?? null).length > IDENTITY_MAX_EVIDENCE) throw new Error(\"Evidence too large\");\n const parsed = provider.parseEvidence(evidence);\n const verified = boundVerifiedIdentity(await provider.verify(statement, parsed, ctx));\n if (provider.category === \"self-custodied\" && verified.subject !== binding.subject) throw new Error(\"The evidence is for another identity\");\n if (provider.category === \"provider-attested\" && !verified.attester) throw new Error(\"The evidence does not say who attests it\");\n return verified;\n}\n\n/** Problems with a provider's descriptor, empty when it is well-formed. The registry and the contract suite use it. */\nexport function descriptorProblems(p: IdentityProofProvider): string[] {\n const problems: string[] = [];\n if (!/^[a-z][a-z0-9-]{0,31}$/.test(p.id)) problems.push(\"id must match ^[a-z][a-z0-9-]{0,31}$\");\n if (!p.label?.trim()) problems.push(\"label is empty\");\n if (!p.description?.trim()) problems.push(\"description is empty\");\n if (!p.privacy?.trim()) problems.push(\"privacy is empty: say what verify contacts\");\n if (p.category !== \"self-custodied\" && p.category !== \"provider-attested\") problems.push(\"category is unknown\");\n if (!p.platforms.length) problems.push(\"no platforms\");\n const { defaultDays, maxDays } = p.validity;\n if (!(Number.isInteger(defaultDays) && Number.isInteger(maxDays) && defaultDays >= 1 && defaultDays <= maxDays && maxDays * DAY <= IDENTITY_MAX_VALIDITY)) problems.push(\"validity must be whole days, 1 ≤ default ≤ max ≤ 400\");\n if (p.recheck && !(Number.isInteger(p.recheck.afterSeconds) && p.recheck.afterSeconds >= 60)) problems.push(\"recheck.afterSeconds must be at least 60\");\n if (!p.signers.length) problems.push(\"no signers\");\n const ids = p.signers.map(s => s.id);\n if (new Set(ids).size !== ids.length) problems.push(\"signer ids are not unique\");\n for (const s of p.signers as readonly IdentitySigner<unknown>[]) {\n if (![\"in-app\", \"external-tool\", \"redirect\", \"publish\"].includes(s.kind)) problems.push(`signer ${s.id} has an unknown kind`);\n if (s.kind === \"in-app\" && typeof s.run !== \"function\") problems.push(`signer ${s.id} has no run()`);\n if (s.kind === \"external-tool\" && (typeof s.instructions !== \"function\" || typeof s.parse !== \"function\")) problems.push(`signer ${s.id} needs instructions() and parse()`);\n if (s.kind === \"redirect\" && typeof s.start !== \"function\") problems.push(`signer ${s.id} has no start()`);\n if (s.kind === \"publish\" && (typeof s.instructions !== \"function\" || typeof s.evidence !== \"function\")) problems.push(`signer ${s.id} needs instructions() and evidence()`);\n }\n return problems;\n}\n\n/** Signers of a provider that run on this platform and are available in this window. */\nexport function availableSigners<E>(p: IdentityProofProvider<E>, platform: IdentityProofProvider[\"platforms\"][number]): IdentitySigner<E>[] {\n return p.signers.filter(s => (s.platforms ?? p.platforms).includes(platform) && (s.available?.() ?? true));\n}\n\n/**\n * The engine's `ctx.fetch`: HTTPS GET only, no credentials or referrer, a time-out, a size cap,\n * redirects refused unless asked for.\n * `online` false refuses everything (the person turned the network off).\n */\nexport function boundedIdentityFetch(options: { online: () => boolean; timeoutMs?: number; fetcher?: typeof fetch } = { online: () => true }): IdentityFetch {\n return async (url, { headers, maxBytes = 64 * 1024, signal, redirect = \"error\" } = {}) => {\n if (!options.online()) throw new Error(\"Offline: this identity cannot be checked now\");\n const target = new URL(url);\n if (target.protocol !== \"https:\" || target.username || target.password) throw new Error(\"Identity checks use HTTPS only\");\n const timeout = AbortSignal.timeout(options.timeoutMs ?? 10_000);\n const response = await (options.fetcher ?? fetch)(target.href, {\n headers, signal: signal ? AbortSignal.any([signal, timeout]) : timeout,\n credentials: \"omit\", referrerPolicy: \"no-referrer\", cache: \"no-store\", redirect,\n });\n if (Number(response.headers.get(\"content-length\")) > maxBytes) throw new Error(\"Identity check response too large\");\n const reader = response.body?.getReader();\n const parts: Uint8Array[] = [];\n let length = 0;\n if (reader) {\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n length += value.length;\n if (length > maxBytes) throw new Error(\"Identity check response too large\");\n parts.push(value);\n }\n } finally { await reader.cancel().catch(() => {}); }\n }\n const bytes = new Uint8Array(length);\n let offset = 0;\n for (const part of parts) { bytes.set(part, offset); offset += part.length; }\n return { status: response.status, contentType: response.headers.get(\"content-type\") ?? \"\", text: new TextDecoder().decode(bytes), bytes };\n };\n}\n","import {\n checkDidJws, checkDidSignature, DidError, DID_JWS_MAX_LENGTH, didGhostlyServiceEntry, didJwsAlg, didSignedMessages, didWebFile, didWebFileNames,\n didWebFileUrl, normalizeDid, parseDid, parseDidSignaturePaste, signingKeys, staticDidDocument, strictBase64Url, toBase64Url,\n type DidCurve, type DidKey, type IdentityStatement,\n} from \"@ghostly/core\";\nimport type { ExternalToolSigner, IdentityProofProvider, PublishSigner, SignerInstructions, SubjectPreview } from \"../contract\";\nimport { fetchDidWebFile, lastDidResolution, resolveDid, type DidResolution } from \"../did\";\nimport { boundedIdentityFetch } from \"../verify\";\n\n/**\n * A decentralized identifier (W3C DID Core), draft WISP 311-did. The subject is the DID; the statement is\n * vouched for by a key its document lists under `authentication` or `assertionMethod` (a JWS or a raw\n * signature pasted back, like SSH and PGP), or, for did:web, by publishing it where the DID document lives\n * (a file beside did.json, or a service entry in it), like the domain proof.\n *\n * Methods: did:key and did:jwk (the key is the identifier: checked offline), did:dht (a signed Pkarr\n * record), did:web (HTTPS). Every check resolves the DID again, so a key removed from a did:web or did:dht\n * document stops counting at the contact's next check.\n */\n\nexport type DidEvidence =\n | { method: \"jws\"; vm: string; jws: string }\n | { method: \"sig\"; vm: string; sig: string }\n | { method: \"file\" }\n | { method: \"service\" };\n\nconst VM = /^did:[a-z0-9]+:[\\x21-\\x7e]+#[\\x21-\\x7e]+$/;\nconst SIG = /^[A-Za-z0-9_-]{86}$/;\nconst exactKeys = (raw: unknown, keys: string) => !!raw && typeof raw === \"object\" && !Array.isArray(raw) && Object.keys(raw).sort().join(\",\") === keys;\nconst validVm = (vm: unknown): vm is string => typeof vm === \"string\" && vm.length <= 640 && VM.test(vm);\n\n/** The profile's own Ghostly did:dht, refused as an external identity. The UI and the engine each set where to read it. */\nlet ownDid: () => string | undefined = () => undefined;\nexport function setOwnDidSource(source: () => string | undefined): void { ownDid = source; }\n\n/** The add dialog's checks run in the UI, with the same bounded fetch the engine uses. */\nconst uiFetch = boundedIdentityFetch({ online: () => globalThis.navigator?.onLine !== false });\n\n/** `#key-1`, shortened when a multibase fragment is long. */\nconst fragment = (id: string) => {\n const f = id.slice(id.indexOf(\"#\"));\n return f.length > 20 ? `${f.slice(0, 11)}…${f.slice(-4)}` : f;\n};\n\nfunction shortDid(did: string): string {\n if (did.length <= 30) return did;\n const { method, id } = parseDidSafe(did);\n if (method === \"web\") return `did:web:${id.split(\":\")[0]}${id.includes(\":\") ? \":…\" : \"\"}`;\n return `did:${method}:${id.slice(0, 8)}…${id.slice(-4)}`;\n}\nconst parseDidSafe = (did: string) => { try { return parseDid(did); } catch { return { did, method: did.split(\":\")[1] ?? \"\", id: did.split(\":\").slice(2).join(\":\") }; } };\n\n/** Keys the instructions can name before anything is fetched: the identifier's own, or what the preview resolved. */\nfunction knownKeys(did: string): DidKey[] {\n const { method } = parseDid(did);\n if (method === \"key\" || method === \"jwk\") return signingKeys(staticDidDocument(did));\n const seen = lastDidResolution(did);\n return seen ? signingKeys(seen.document) : [];\n}\n\nconst NOBLE: Record<DidCurve, { name: string; module: string }> = {\n Ed25519: { name: \"ed25519\", module: \"ed25519.js\" },\n secp256k1: { name: \"secp256k1\", module: \"secp256k1.js\" },\n \"P-256\": { name: \"p256\", module: \"nist.js\" },\n};\n\nfunction joseScript(statement: string, kid?: string): string {\n return [\n \"// npm install jose; then: node sign.mjs your-private-key.jwk.json\",\n \"import { readFileSync } from \\\"node:fs\\\";\",\n \"import { CompactSign, importJWK } from \\\"jose\\\";\",\n `const statement = ${JSON.stringify(statement)};`,\n \"const jwk = JSON.parse(readFileSync(process.argv[2], \\\"utf8\\\"));\",\n \"const alg = { Ed25519: \\\"EdDSA\\\", \\\"P-256\\\": \\\"ES256\\\" }[jwk.crv];\",\n \"const jws = await new CompactSign(new TextEncoder().encode(statement))\",\n ` .setProtectedHeader({ alg${kid ? `, kid: ${JSON.stringify(kid)}` : \"\"} })`,\n \" .sign(await importJWK(jwk, alg));\",\n \"console.log(jws);\",\n ].join(\"\\n\");\n}\n\nfunction nobleScript(statement: string, curve: DidCurve): string {\n const lib = NOBLE[curve];\n return [\n \"// npm install @noble/curves; then: node sign.mjs your-private-key.hex\",\n \"import { readFileSync } from \\\"node:fs\\\";\",\n `import { ${lib.name} } from \"@noble/curves/${lib.module}\";`,\n `const statement = new TextEncoder().encode(${JSON.stringify(statement)});`,\n \"const secret = Uint8Array.from(Buffer.from(readFileSync(process.argv[2], \\\"utf8\\\").trim(), \\\"hex\\\"));\",\n `console.log(Buffer.from(${lib.name}.sign(statement, secret)).toString(\"hex\"));`,\n ].join(\"\\n\");\n}\n\nfunction opensslCommand(statement: string, curve: DidCurve): string {\n const save = `printf '%s' '${statement}' > statement.txt`;\n return curve === \"Ed25519\"\n ? `${save} && openssl pkeyutl -sign -rawin -inkey key.pem -in statement.txt | xxd -p | tr -d '\\\\n'`\n : `${save} && openssl dgst -sha256 -sign key.pem statement.txt | xxd -p | tr -d '\\\\n'`;\n}\n\nconst signer: ExternalToolSigner<DidEvidence> = {\n id: \"sign\", kind: \"external-tool\", label: \"Sign with a key of the DID\",\n description: \"Sign the statement with a key your DID document lists for authentication or assertionMethod, and paste a JWS or the raw signature. The private key never enters Ghostly.\",\n instructions(statement): SignerInstructions {\n const did = statement.binding.subject;\n if (statement.text.includes(\"'\")) throw new DidError(\"Statement is not signable as plain text\");\n const keys = knownKeys(did);\n const key = keys.length === 1 ? keys[0] : undefined;\n const curve = key?.curve ?? keys[0]?.curve ?? \"Ed25519\";\n const which = keys.length ? keys.map(k => `${fragment(k.id)} (${k.curve})`).join(\", \") : \"a key listed under authentication or assertionMethod\";\n return {\n steps: [\n { text: `Sign this statement with the private key of ${which}. It is one line, with no newline at the end:`, copy: statement.text },\n ...(curve === \"secp256k1\" ? [] : [{ text: `With jose (Node.js 20 or later), a JWS: save this as sign.mjs and run it with your ${curve} private key as a JWK file.`, copy: joseScript(statement.text, key?.id) }]),\n { text: `With @noble/curves (Node.js), a raw signature: save this as sign.mjs and run it with your ${curve} private key in hex.`, copy: nobleScript(statement.text, curve) },\n { text: `Or with OpenSSL 3 and your ${curve} key in PEM:`, copy: opensslCommand(statement.text, curve) },\n { text: `Paste what it prints: a compact JWS (${didJwsAlg(curve)}, attached or detached), or the signature in hex or base64. If your DID lists several keys, you may write the key id first: #key-1 <signature>.` },\n ],\n paste: { label: \"JWS or signature\", placeholder: \"eyJhbGciOi… or the signature in hex\", multiline: true },\n };\n },\n async parse(pasted, statement) {\n const did = statement.binding.subject;\n const paste = parseDidSignaturePaste(pasted);\n const { document } = await resolveDid(did, { fetch: uiFetch });\n const messages = didSignedMessages(statement.text);\n if (paste.kind === \"jws\") return { method: \"jws\", vm: checkDidJws(paste.jws, messages, document).key.id, jws: paste.jws };\n const check = checkDidSignature(paste.signature, messages, document, paste.vm);\n return { method: \"sig\", vm: check.key.id, sig: toBase64Url(check.signature) };\n },\n};\n\nconst webOnly = (statement: IdentityStatement) => {\n const did = statement.binding.subject;\n if (parseDid(did).method !== \"web\") throw new DidError(\"Publishing works for did:web only: sign the statement instead\");\n return did;\n};\n\nconst fileSigner: PublishSigner<DidEvidence> = {\n id: \"file\", kind: \"publish\", label: \"Publish a file beside did.json\",\n description: \"did:web only: upload a small file next to your did.json. Contacts' apps download both from your server.\",\n instructions(statement) {\n const did = webOnly(statement);\n return { steps: [\n { text: \"Create this file on your web server, beside your did.json:\", copy: didWebFileUrl(did, statement.id) },\n { text: \"With exactly this content:\", copy: didWebFile(did, statement.text) },\n { text: \"Serve it over HTTPS at exactly that address, without any redirect, and with this header so browsers may read it (your did.json needs it too):\", copy: \"Access-Control-Allow-Origin: *\" },\n { text: \"Then press Check. The file stays public while the proof is in use; deleting it withdraws this proof from every contact at their next check.\" },\n ] };\n },\n evidence: () => ({ method: \"file\" }),\n};\n\nconst serviceSigner: PublishSigner<DidEvidence> = {\n id: \"service\", kind: \"publish\", label: \"Add a service to did.json\",\n description: \"did:web only: add one entry to the service list of your did.json. Contacts' apps read it there.\",\n instructions(statement) {\n const did = webOnly(statement);\n const seen = lastDidResolution(did);\n return { steps: [\n { text: `Add this entry to the \"service\" list of your did.json${seen?.documentUrl ? ` (${seen.documentUrl})` : \"\"}:`, copy: JSON.stringify(didGhostlyServiceEntry(did, statement.binding.key, statement.id), null, 2) },\n { text: \"Then press Check. Removing the entry withdraws this proof from every contact at their next check.\" },\n ] };\n },\n evidence: () => ({ method: \"service\" }),\n};\n\nfunction previewOf(r: DidResolution): SubjectPreview {\n const keys = signingKeys(r.document);\n const unusable = r.document.keys.length - keys.length + r.document.skipped;\n if (!keys.length && r.method !== \"web\") throw new DidError(\"This DID lists no Ed25519, secp256k1 or P-256 key for authentication or assertionMethod, so it cannot sign\");\n const facts: { label: string; value: string }[] = [{ label: \"Method\", value: `did:${r.method}${r.method === \"key\" || r.method === \"jwk\" ? \", the key is the identifier\" : \"\"}` }];\n if (r.host) facts.push({ label: \"Domain\", value: r.host });\n if (r.documentUrl) facts.push({ label: \"Document\", value: r.documentUrl });\n if (r.relay) facts.push({ label: \"Relay\", value: r.relay.replace(/^https:\\/\\//, \"\") });\n facts.push({ label: keys.length === 1 ? \"Key\" : \"Keys\", value: keys.length ? keys.map(k => `${fragment(k.id)} (${k.curve})`).join(\", \") : \"none that can sign: publish instead\" });\n if (unusable > 0) facts.push({ label: \"Not usable\", value: `${unusable} ${unusable === 1 ? \"key\" : \"keys\"}: another curve, or not for authentication or assertionMethod` });\n return { facts, signers: r.method === \"web\" ? [...(keys.length ? [\"sign\"] : []), \"file\", \"service\"] : [\"sign\"] };\n}\n\nexport const did: IdentityProofProvider<DidEvidence> = {\n id: \"did\",\n label: \"DID\",\n category: \"self-custodied\",\n summary: \"did:key, did:jwk, did:dht or did:web\",\n description: \"Proves you control a decentralized identifier (W3C DID): sign the statement with a key its DID document lists, or, for did:web, publish it beside your did.json.\",\n limits: \"Does not prove who controls the DID, only that whoever added the proof held one of its keys (or its website).\",\n platforms: [\"web\", \"extension\", \"desktop\"],\n advanced: true,\n experimental: true,\n subject: {\n label: \"DID\", placeholder: \"did:key:z6Mk… or did:web:example.com\",\n help: \"did:key, did:jwk, did:dht or did:web. Contacts see the whole DID, and the key that signed.\",\n normalize(input) {\n const canonical = normalizeDid(input);\n if (canonical === ownDid()) throw new DidError(\"That is this profile's own Ghostly DID: your contacts already see your Ghostly identity. Add another DID.\");\n return canonical;\n },\n short: shortDid,\n preview: async (subject, { signal }) => previewOf(await resolveDid(subject, { fetch: uiFetch, signal })),\n },\n // A proven DID is already a URI: the profile's own did:dht may list it under alsoKnownAs.\n publicUri: subject => subject,\n validity: { defaultDays: 90, maxDays: 365 },\n // A did:web or did:dht document can drop a key, or the file be deleted: contacts can check again.\n recheck: { afterSeconds: 86_400 },\n privacy: \"did:key and did:jwk: nothing, checked on this device. did:web: the contact's app asks its DNS-over-HTTPS resolver for your domain and downloads your did.json, \" +\n \"so your web server sees the contact's IP address. did:dht: a Pkarr relay learns which DID was looked up.\",\n signers: [signer, fileSigner, serviceSigner],\n parseEvidence(raw) {\n const e = raw as Record<string, unknown>;\n if (exactKeys(raw, \"jws,method,vm\") && e.method === \"jws\" && typeof e.jws === \"string\" && e.jws.length <= DID_JWS_MAX_LENGTH && validVm(e.vm)) return { method: \"jws\", vm: e.vm, jws: e.jws };\n if (exactKeys(raw, \"method,sig,vm\") && e.method === \"sig\" && typeof e.sig === \"string\" && SIG.test(e.sig) && validVm(e.vm)) return { method: \"sig\", vm: e.vm, sig: e.sig };\n if (exactKeys(raw, \"method\") && (e.method === \"file\" || e.method === \"service\")) return { method: e.method };\n throw new Error(\"That is not DID proof evidence\");\n },\n async verify(statement, evidence, ctx) {\n const subject = statement.binding.subject;\n const { method } = parseDid(subject);\n if (normalizeDid(subject) !== subject) throw new DidError(\"The DID is not in canonical form\");\n if ((evidence.method === \"file\" || evidence.method === \"service\") && method !== \"web\") throw new DidError(\"Only a did:web can publish its proof\");\n const options = { fetch: ctx.fetch, signal: ctx.signal, now: () => ctx.now * 1000 };\n const { document, host } = await resolveDid(subject, options);\n const messages = didSignedMessages(statement.text);\n if (evidence.method === \"jws\" || evidence.method === \"sig\") {\n const check = evidence.method === \"jws\"\n ? checkDidJws(evidence.jws, messages, document)\n : checkDidSignature(strictBase64Url(evidence.sig), messages, document, evidence.vm);\n if (check.key.id !== evidence.vm) throw new DidError(\"Signed by another key than the proof names\");\n return { subject, source: `did:${method} · ${evidence.method === \"jws\" ? `${check.alg} JWS` : `${check.alg} signature`} by ${fragment(check.key.id)}` };\n }\n if (evidence.method === \"service\") {\n if (!document.services.some(s => s.proof === statement.id && s.key === statement.binding.key)) throw new DidError(\"The did.json has no Ghostly service for this proof: it was removed, or not added yet\");\n return { subject, source: `did:web · service in the did.json of ${host}` };\n }\n const text = await fetchDidWebFile(subject, statement.id, options);\n if (text === undefined) throw new DidError(`${didWebFileUrl(subject, statement.id)} was not found: it was deleted, or not uploaded yet`);\n if (!didWebFileNames(text, subject, statement.text)) throw new DidError(`${didWebFileUrl(subject, statement.id)} is not the statement of this proof`);\n return { subject, source: `did:web · statement file beside the did.json of ${host}` };\n },\n};\n","import { atprotoDidMethod, isAtprotoDid, parseAtprotoDidDocument, txtValue, type AtprotoDidDocument } from \"@ghostly/core\";\nimport type { IdentityFetch } from \"../contract\";\nimport { assertPublicHost, dohQuery, type DohResolverId } from \"../domain\";\n\n/**\n * AT Protocol identity resolution, the network half: a DID to its document (the PLC directory for\n * did:plc, the host's `/.well-known/did.json` for did:web), and a handle to a DID (DNS TXT\n * `_atproto.<handle>` through the person's DNS-over-HTTPS resolver, then\n * `https://<handle>/.well-known/atproto-did`). Every request goes through the contract's bounded\n * fetch: HTTPS only, no redirects, a size cap, a time-out. URLs are built here from the DID or the\n * handle, never taken from a proof.\n */\n\nexport const PLC_DIRECTORY = \"https://plc.directory\";\nconst TEST_HOST = /^https:\\/\\/[a-z0-9-]+(\\.[a-z0-9-]+)*\\.test$/;\n/** DID documents are small; a large answer is not one. */\nexport const DID_DOCUMENT_MAX_BYTES = 32 * 1024;\nconst HANDLE_DID_MAX_BYTES = 2048;\n\n/**\n * The PLC directory. A build made for the e2e suite with VITE_ATPROTO_TEST_PLC may point it at a\n * `.test` host (a name reserved for testing); a release build always uses plc.directory.\n */\nexport function plcDirectory(): string {\n try {\n const value = import.meta.env?.VITE_ATPROTO_TEST_PLC as string | undefined;\n return value && TEST_HOST.test(value) ? value : PLC_DIRECTORY;\n } catch { return PLC_DIRECTORY; }\n}\n\nexport interface AtprotoResolveOptions {\n fetch: IdentityFetch;\n signal?: AbortSignal;\n /** The DoH resolver for handles (the person's choice, as for domains). */\n resolver?: DohResolverId;\n /** The PLC directory; plcDirectory() by default. */\n plc?: string;\n}\n\n/** Where a DID's document lives. */\nexport function didDocumentUrl(did: string, plc = plcDirectory()): string {\n const method = atprotoDidMethod(did);\n if (method === \"plc\") return `${plc}/${did}`;\n if (method === \"web\") return `https://${did.slice(\"did:web:\".length)}/.well-known/did.json`;\n throw new Error(\"Not an AT Protocol DID\");\n}\n\n/**\n * Refuses a host the account chose (a did:web host, the PDS its document names) that points to a private\n * network address: a contact's app never knocks on its own local network for someone else's proof.\n */\nexport async function assertPublicServer(host: string, options: AtprotoResolveOptions): Promise<void> {\n try { await assertPublicHost(host, { fetch: options.fetch, signal: options.signal, resolver: options.resolver }); }\n catch (e) { throw new Error(`The account's server ${host} was not contacted: ${e instanceof Error ? e.message : String(e)}`); } // eslint-disable-line preserve-caught-error -- The domain check's message says why; ES2020 has no Error.cause.\n}\n\n/** A DID's document, checked (parseAtprotoDidDocument). Throws a message people can read. */\nexport async function resolveAtprotoDid(did: string, options: AtprotoResolveOptions): Promise<AtprotoDidDocument> {\n const url = didDocumentUrl(did, options.plc);\n if (atprotoDidMethod(did) === \"web\") await assertPublicServer(did.slice(\"did:web:\".length), options);\n let response: Awaited<ReturnType<IdentityFetch>>;\n try {\n response = await options.fetch(url, { headers: { accept: \"application/did+ld+json, application/json\" }, maxBytes: DID_DOCUMENT_MAX_BYTES, signal: options.signal, redirect: \"error\" });\n } catch (e) {\n // eslint-disable-next-line preserve-caught-error -- A message people can act on; the library's error stays out of the UI (ES2020 has no Error.cause).\n throw new Error(/too large/i.test(String(e)) ? \"The account's DID document is too large\" : \"The account's DID could not be resolved\");\n }\n if (response.status === 404 || response.status === 410) throw new Error(\"The account's DID is not registered, or was deactivated\");\n if (response.status !== 200) throw new Error(\"The account's DID could not be resolved\");\n let doc: unknown;\n try { doc = JSON.parse(response.text); } catch { throw new Error(\"The account's DID document is malformed\"); }\n return parseAtprotoDidDocument(doc, did);\n}\n\n/**\n * The DID a handle points to, or null when it points nowhere (or to two different DIDs). DNS first,\n * then HTTPS, as the handle spec says. The HTTPS lookup first checks the handle's addresses are public,\n * so a handle cannot make a contact's app knock on its local network.\n */\nexport async function resolveAtprotoHandle(handle: string, options: AtprotoResolveOptions): Promise<string | null> {\n const lookup = { fetch: options.fetch, signal: options.signal, resolver: options.resolver };\n try {\n const answer = await dohQuery(`_atproto.${handle}`, \"TXT\", lookup);\n const dids = new Set<string>();\n for (const a of answer.answers) {\n if (a.type !== 16) continue;\n let text: string;\n try { text = txtValue(a.data); } catch { continue; }\n if (text.startsWith(\"did=\")) dids.add(text.slice(4));\n }\n if (dids.size > 1) return null;\n const [did] = dids;\n if (did) return isAtprotoDid(did) ? did : null;\n } catch { /* The resolver failed: try HTTPS. */ }\n try {\n await assertPublicHost(handle, lookup);\n const response = await options.fetch(`https://${handle}/.well-known/atproto-did`, { headers: { accept: \"text/plain\" }, maxBytes: HANDLE_DID_MAX_BYTES, signal: options.signal, redirect: \"error\" });\n if (response.status !== 200) return null;\n const did = response.text.trim();\n return isAtprotoDid(did) ? did : null;\n } catch { return null; }\n}\n\n/**\n * The handle to show for a DID: the one its document claims, and only if that handle resolves back to\n * the same DID. Otherwise nothing: the card shows the DID. Never throws.\n */\nexport async function verifiedAtprotoHandle(doc: AtprotoDidDocument, options: AtprotoResolveOptions): Promise<string | undefined> {\n if (!doc.handle) return undefined;\n try { return (await resolveAtprotoHandle(doc.handle, options)) === doc.did ? doc.handle : undefined; } catch { return undefined; }\n}\n","import {\n ATPROTO_DECLARED_SCOPE, ATPROTO_FULL_SCOPE, ATPROTO_PROOF_COLLECTION, ATPROTO_PROOF_SCOPE, atprotoProofRecord, atprotoProofRkey,\n type AtprotoDidDocument, type IdentityStatement,\n} from \"@ghostly/core\";\nimport type { IdentityFetch, IdentityPlatform } from \"../contract\";\nimport { resolveAtprotoDid, resolveAtprotoHandle, type AtprotoResolveOptions } from \"./resolve\";\n\n/**\n * The prover's half of AT Protocol proofs: AT Protocol OAuth (PAR + PKCE + DPoP, through the official\n * `@atproto/oauth-client`) against the person's own server, then one record written or deleted. Every\n * flow gets a fresh client whose stores live in memory only: the DPoP key (a non-extractable WebCrypto\n * key), the PKCE verifier and the tokens exist for this one flow, are never logged or persisted, and the\n * session is revoked (signOut) as soon as the record is written. Nothing is kept for later: removing\n * the proof asks the person again.\n *\n * The client is identified by the URL of its metadata document (no registration with anyone):\n * https://ghostly.tools/oauth/client-metadata.json, which is ATPROTO_CLIENT_METADATA verbatim\n * (apps/website/public/oauth/client-metadata.json; a unit test keeps the two equal). A web app served from\n * a loopback address (development, the e2e suite) uses AT Protocol's `http://localhost` development\n * client instead, which needs no document.\n */\n\nexport const ATPROTO_CLIENT_ID = \"https://ghostly.tools/oauth/client-metadata.json\";\n/** The extension's id in the Chrome Web Store: its chromiumapp.org redirect is registered. Unpacked builds have another id. */\nexport const EXTENSION_ID = \"nbedaagicniejlmfcncndfjcejaidbcf\";\nexport const ATPROTO_REDIRECTS = {\n /** The web app's static callback page (shared with OpenID Connect): the answer stays in the fragment. */\n web: \"https://app.ghostly.tools/oidc-callback.html\",\n /** chrome.identity.launchWebAuthFlow catches it without loading it. */\n extension: `https://${EXTENSION_ID}.chromiumapp.org/atproto`,\n /** The desktop app's one-shot listener on 127.0.0.1; the port is not part of the match. */\n desktop: \"http://127.0.0.1/oidc-callback\",\n} as const;\n\n/**\n * `native` because the desktop app's loopback redirect is only allowed for native clients; HTTPS\n * redirects (web, extension) are allowed for them too. No `refresh_token` grant: Ghostly never keeps\n * a session, so it never gets a refresh token to lose.\n */\nexport const ATPROTO_CLIENT_METADATA = {\n client_id: ATPROTO_CLIENT_ID,\n client_name: \"Ghostly\",\n client_uri: \"https://ghostly.tools\",\n logo_uri: \"https://ghostly.tools/icon-192.png\",\n application_type: \"native\",\n grant_types: [\"authorization_code\"],\n response_types: [\"code\"],\n scope: ATPROTO_DECLARED_SCOPE,\n token_endpoint_auth_method: \"none\",\n dpop_bound_access_tokens: true,\n redirect_uris: [ATPROTO_REDIRECTS.web, ATPROTO_REDIRECTS.extension, ATPROTO_REDIRECTS.desktop],\n} as const;\n\n/** AT Protocol's development client: no document, loopback redirects only, any port. */\nexport function loopbackClientMetadata(redirectUri: string) {\n const registered = new URL(redirectUri);\n registered.port = \"\";\n const id = `http://localhost?${new URLSearchParams({ redirect_uri: registered.href, scope: ATPROTO_DECLARED_SCOPE })}`;\n return {\n client_id: id, application_type: \"native\", grant_types: [\"authorization_code\"], response_types: [\"code\"], scope: ATPROTO_DECLARED_SCOPE,\n token_endpoint_auth_method: \"none\", dpop_bound_access_tokens: true, redirect_uris: [registered.href],\n } as const;\n}\n\nconst isLoopback = (url: string) => { try { const u = new URL(url); return u.protocol === \"http:\" && (u.hostname === \"127.0.0.1\" || u.hostname === \"[::1]\"); } catch { return false; } };\nconst withoutPort = (url: string) => { const u = new URL(url); u.port = \"\"; return u.href; };\n\n/**\n * The client for a redirect: the published one, or the development client on a loopback web app. A\n * loopback redirect is registered without its port (servers match any port), but the library checks\n * the exact address locally, so the one in use is added to the copy it gets; the server only ever sees\n * the client id and reads the registered addresses itself.\n */\nexport function clientMetadataFor(platform: IdentityPlatform, redirectUri: string) {\n const base = platform === \"web\" && isLoopback(redirectUri) ? loopbackClientMetadata(redirectUri) : ATPROTO_CLIENT_METADATA;\n const registered = base.redirect_uris as readonly string[];\n const matches = registered.includes(redirectUri) || (isLoopback(redirectUri) && registered.includes(withoutPort(redirectUri)));\n if (!matches) throw new Error(\"This Ghostly build cannot sign in to an AT Protocol server here (its return address is not registered).\");\n return { ...base, redirect_uris: [...new Set([redirectUri, ...registered])] };\n}\n\n/** How a platform shows the server's page and hands back where it redirected. */\nexport interface AtprotoWindow {\n /** The redirect URI for this platform, as used in the request (the desktop's carries its port). */\n redirectUri: string;\n /** Opens `url`; resolves with the full address the server redirected to, carrying `state`. */\n authorize(url: string, state: string, signal: AbortSignal): Promise<string>;\n /** Releases the window or listener, answered or not. */\n close(): void;\n}\n\nexport interface AtprotoHost {\n platform: IdentityPlatform;\n /** Call it straight from the click: the web app opens its popup before anything is awaited. */\n open(): Promise<AtprotoWindow>;\n}\n\n/** Which permission to ask for: only Ghostly's records, or the whole repository (older servers). */\nexport type AtprotoAccess = \"proof-records\" | \"full\";\nexport const scopeFor = (access: AtprotoAccess) => (access === \"full\" ? ATPROTO_FULL_SCOPE : ATPROTO_PROOF_SCOPE);\n\n/** The server refused the narrow permission: it predates granular scopes. */\nexport class AtprotoScopeError extends Error {}\n\n/** The account a handle (or a DID typed as is) names, with its document. */\nexport async function lookupAtprotoAccount(input: string, options: AtprotoResolveOptions & { normalize(input: string): string | null }): Promise<AtprotoDidDocument> {\n const value = input.trim();\n if (value.startsWith(\"did:\")) return resolveAtprotoDid(value, options);\n const handle = options.normalize(value);\n if (!handle) throw new Error(\"That is not a handle (like alice.bsky.social)\");\n const did = await resolveAtprotoHandle(handle, options);\n if (!did) throw new Error(`${handle} does not point to an AT Protocol account`);\n const doc = await resolveAtprotoDid(did, options);\n if (doc.handle !== handle) throw new Error(`${handle} points to an account that names another handle`);\n return doc;\n}\n\nexport interface AtprotoFlowOptions {\n platform: IdentityPlatform;\n window: AtprotoWindow;\n account: AtprotoDidDocument;\n access: AtprotoAccess;\n signal: AbortSignal;\n /** The resolution the library uses: the same bounded lookups as the verifier. */\n resolve: AtprotoResolveOptions;\n onProgress(message: string): void;\n /** The HTTP client for the OAuth requests and the record (the page's fetch). */\n fetch?: typeof fetch;\n}\n\nconst MESSAGES: Record<string, string> = {\n access_denied: \"You declined on your server. Nothing was published.\",\n login_required: \"Log in on your server and try again.\",\n consent_required: \"Your server needs your approval to continue.\",\n};\n\n/** The redirect's parameters: the fragment (Ghostly asks for fragment responses), then the query. */\nexport function callbackParams(redirected: string, redirectUri: string): URLSearchParams {\n const url = new URL(redirected), expected = new URL(redirectUri);\n if (url.origin !== expected.origin || url.pathname !== expected.pathname) throw new Error(\"Sign-in returned to an unexpected address.\");\n const params = new URLSearchParams(url.hash.slice(1));\n for (const [k, v] of new URLSearchParams(url.search)) if (!params.has(k)) params.set(k, v);\n return params;\n}\n\nconst pdsHost = (pds: string) => new URL(pds).host;\n\n/** Runs OAuth for `account`, hands `work` a session scoped to this flow, and always revokes it afterwards. */\nasync function withSession<T>(options: AtprotoFlowOptions, work: (session: { did: string; fetchHandler(path: string, init?: RequestInit): Promise<Response> }) => Promise<T>): Promise<T> {\n const { window: w, account, signal } = options;\n signal.throwIfAborted();\n const [{ OAuthClient }, { WebcryptoKey }] = await Promise.all([import(\"@atproto/oauth-client\"), import(\"@atproto/jwk-webcrypto\")]);\n const states = new Map<string, unknown>(), sessions = new Map<string, unknown>();\n let state: string | undefined;\n const memory = <V>(map: Map<string, V>, onSet?: (key: string) => void) => ({\n async get(key: string) { return map.get(key); },\n async set(key: string, value: V) { onSet?.(key); map.set(key, value); },\n async del(key: string) { map.delete(key); },\n });\n const client = new OAuthClient({\n responseMode: \"fragment\",\n clientMetadata: clientMetadataFor(options.platform, w.redirectUri) as never,\n stateStore: memory(states, key => { state = key; }) as never,\n sessionStore: memory(sessions) as never,\n runtimeImplementation: {\n createKey: algs => WebcryptoKey.generate(algs),\n getRandomValues: length => crypto.getRandomValues(new Uint8Array(length)),\n digest: async (data, { name }) => new Uint8Array(await crypto.subtle.digest(`SHA-${name.slice(3)}`, data)),\n },\n // The same lookups the verifier makes, bounded, through the person's DoH resolver: no third-party handle resolver.\n identityResolver: {\n async resolve(input: string) {\n const did = input.startsWith(\"did:\") ? input : await resolveAtprotoHandle(input, options.resolve);\n if (!did) throw new Error(\"The account could not be resolved\");\n const doc = await fetchDidJson(did, options.resolve);\n return { did, didDoc: doc.json, handle: doc.parsed.handle ?? \"handle.invalid\" } as never;\n },\n },\n fetch: options.fetch ?? globalThis.fetch.bind(globalThis),\n allowHttp: false,\n });\n let url: URL;\n try {\n url = await client.authorize(account.did, { scope: scopeFor(options.access), redirect_uri: w.redirectUri as never, signal });\n } catch (e) {\n const text = String((e as { error?: string })?.error ?? (e as Error)?.message ?? e);\n if (/invalid_scope/i.test(text) || /scope/i.test(String((e as { errorDescription?: string })?.errorDescription ?? \"\"))) throw new AtprotoScopeError(\n \"Your server does not offer a permission limited to Ghostly's records: it only offers full access to your account. Choose \\\"Your server, full access\\\" to continue anyway; Ghostly still only writes this one record.\");\n // eslint-disable-next-line preserve-caught-error -- A message people can act on; the library's error stays out of the UI (ES2020 has no Error.cause).\n throw new Error(`Your server (${pdsHost(account.pds)}) refused to start the sign-in.`);\n }\n if (!state) throw new Error(\"The sign-in could not start.\");\n options.onProgress(`Approve on ${pdsHost(account.pds)} in the window that opened…`);\n const redirected = await w.authorize(url.href, state, signal);\n signal.throwIfAborted();\n const params = callbackParams(redirected, w.redirectUri);\n const error = params.get(\"error\");\n if (error) throw new Error(MESSAGES[error] ?? \"Your server refused the sign-in.\");\n let session: Awaited<ReturnType<typeof client.callback>>[\"session\"];\n try { ({ session } = await client.callback(params, { redirect_uri: w.redirectUri as never })); }\n catch { throw new Error(\"The sign-in could not be completed. Nothing was published.\"); }\n try {\n if (session.did !== account.did) throw new Error(\"You logged in to another account than the one you typed. Nothing was published.\");\n return await work(session);\n } finally {\n // Revokes the tokens on the server; the DPoP key and verifier go out of scope with the client.\n await session.signOut().catch(() => {});\n sessions.clear(); states.clear();\n }\n}\n\n/** A DID's document, checked, and the JSON the library reads its services from. */\nasync function fetchDidJson(did: string, options: AtprotoResolveOptions): Promise<{ parsed: AtprotoDidDocument; json: unknown }> {\n let json: unknown;\n const recording: IdentityFetch = async (url, init) => {\n const response = await options.fetch(url, init);\n try { json = JSON.parse(response.text); } catch { /* resolveAtprotoDid says so */ }\n return response;\n };\n const parsed = await resolveAtprotoDid(did, { ...options, fetch: recording });\n return { parsed, json };\n}\n\nasync function xrpcError(response: Response): Promise<string> {\n const body = await response.json().catch(() => ({})) as { error?: string; message?: string };\n return `${body.error ?? response.status}${body.message ? `: ${body.message}` : \"\"}`;\n}\n\n/** Writes the proof record (rkey = the proof key) and returns once the server has it. */\nexport async function publishAtprotoProof(statement: IdentityStatement, options: AtprotoFlowOptions): Promise<void> {\n await withSession(options, async session => {\n options.onProgress(`Publishing the proof on ${pdsHost(options.account.pds)}…`);\n const response = await session.fetchHandler(\"/xrpc/com.atproto.repo.createRecord\", {\n method: \"POST\", headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ repo: session.did, collection: ATPROTO_PROOF_COLLECTION, rkey: atprotoProofRkey(statement.binding.key), record: atprotoProofRecord(statement, new Date()) }),\n });\n if (response.status === 403) throw new AtprotoScopeError(\"Your server did not let Ghostly write its record. Choose \\\"Your server, full access\\\" to try again.\");\n if (!response.ok) throw new Error(`Your server did not publish the record (${await xrpcError(response)}).`);\n });\n}\n\n/** Deletes the proof record. Deleting a record that is not there succeeds. */\nexport async function unpublishAtprotoProof(proofKey: string, options: AtprotoFlowOptions): Promise<void> {\n await withSession(options, async session => {\n options.onProgress(`Deleting the record on ${pdsHost(options.account.pds)}…`);\n const response = await session.fetchHandler(\"/xrpc/com.atproto.repo.deleteRecord\", {\n method: \"POST\", headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ repo: session.did, collection: ATPROTO_PROOF_COLLECTION, rkey: atprotoProofRkey(proofKey) }),\n });\n if (response.status === 403) throw new AtprotoScopeError(\"Your server did not let Ghostly delete its record.\");\n if (!response.ok) throw new Error(`Your server did not delete the record (${await xrpcError(response)}).`);\n });\n}\n","import {\n ATPROTO_PROOF_COLLECTION, AtprotoProofError, atprotoProofRkey, checkAtprotoProofRecord, isAtprotoDid, normalizeAtprotoHandle, shortAtprotoDid,\n verifyAtprotoRecordProof,\n} from \"@ghostly/core\";\nimport type { IdentityFetch, IdentityProofProvider, InAppSigner, SignerContext } from \"../contract\";\nimport { getBrowserHost } from \"../../host\";\nimport { boundedIdentityFetch } from \"../verify\";\nimport { chosenResolver } from \"../domain\";\nimport { lookupAtprotoAccount, publishAtprotoProof, unpublishAtprotoProof, type AtprotoAccess, type AtprotoHost, AtprotoScopeError } from \"../atproto/oauth\";\nimport { assertPublicServer, resolveAtprotoDid, verifiedAtprotoHandle, type AtprotoResolveOptions } from \"../atproto/resolve\";\n\n/**\n * Identity proofs for an AT Protocol account (Bluesky, or any PDS): the account publishes, in its own\n * repository, one record of the collection `tools.ghostly.proof` carrying the statement, keyed by the\n * proof key. Publishing takes an OAuth approval on the person's server that lets Ghostly create and\n * delete records of that collection only. A contact's app checks it without logging in: the DID\n * document (PLC directory or did:web) names the signing key and the server; the server's\n * `com.atproto.sync.getRecord` answer is a CAR whose commit must be signed by that key and whose tree\n * must lead to the record. The handle is shown only when it resolves back to the DID.\n *\n * The subject is the DID (it outlives handles). Evidence is empty: everything is fetched from the\n * account's own repository, so deleting the record (removing the proof here does) makes re-checks fail.\n * See docs/wisps/312-atproto.md and docs/lexicons/tools.ghostly.proof.json.\n */\nexport type AtprotoEvidence = Record<string, never>;\n\n/** The proof CAR: a commit, the tree path and one small record. Real repositories' paths are a few KiB. */\nexport const RECORD_PROOF_MAX_BYTES = 256 * 1024;\n\nfunction currentHost(): AtprotoHost | undefined {\n try { return getBrowserHost().atproto; } catch { return undefined; }\n}\n\nconst hostOf = (url: string) => new URL(url).host;\n\nexport interface AtprotoIdentityOptions {\n /** The platform's sign-in window. */\n host?: () => AtprotoHost | undefined;\n /** The UI side's network for looking the account up before signing in. */\n fetch?: IdentityFetch;\n /** The page's fetch for OAuth and the record (tests pass a fake server). */\n oauthFetch?: typeof fetch;\n /** The PLC directory (tests). */\n plc?: string;\n}\n\nexport function createAtprotoIdentityProvider(options: AtprotoIdentityOptions = {}): IdentityProofProvider<AtprotoEvidence> {\n const host = options.host ?? currentHost;\n const uiFetch = () => options.fetch ?? boundedIdentityFetch({ online: () => true });\n const resolveOptions = (fetch: IdentityFetch, signal?: AbortSignal): AtprotoResolveOptions =>\n ({ fetch, signal, resolver: chosenResolver().id, ...(options.plc ? { plc: options.plc } : {}) });\n\n /** Opens the window from the click, looks the account up, then runs OAuth for `work`; always closes the window. */\n function signIn<T>(input: string, access: AtprotoAccess, ctx: SignerContext, work: (flow: Parameters<typeof publishAtprotoProof>[1]) => Promise<T>): Promise<T> {\n // Before any await: the web app's popup needs the click's user activation.\n const h = host();\n const opening = h?.open();\n return (async () => {\n if (!h || !opening) throw new Error(\"Signing in to an AT Protocol server is not available here.\");\n const w = await opening;\n try {\n const resolve = resolveOptions(uiFetch(), ctx.signal);\n ctx.onProgress(`Looking up ${input.trim()}…`);\n const account = await lookupAtprotoAccount(input, { ...resolve, normalize: normalizeAtprotoHandle });\n ctx.signal.throwIfAborted();\n return await work({ platform: h.platform, window: w, account, access, signal: ctx.signal, resolve, onProgress: ctx.onProgress, fetch: options.oauthFetch });\n } finally { w.close(); }\n })();\n }\n\n const signer = (id: string, access: AtprotoAccess, label: string, description: string): InAppSigner<AtprotoEvidence> => ({\n id, kind: \"in-app\", label, description,\n action: access === \"full\" ? \"Continue on your server (full access)\" : \"Continue on your server\",\n available: () => !!host(),\n fields: [{\n name: \"handle\", label: \"Handle\", kind: \"text\", placeholder: \"alice.bsky.social\",\n help: \"Your Bluesky handle, your own domain if you use one, or your DID.\",\n }],\n run(ctx, work) {\n return signIn(ctx.values.handle ?? \"\", access, ctx, flow => work({\n subject: async () => flow.account.did,\n sign: async statement => { await publishAtprotoProof(statement, flow); return {}; },\n }));\n },\n });\n\n // Removing asks for a fresh approval; the server that refused the narrow permission once is asked for full access.\n const fullAccessServers = new Set<string>();\n\n return {\n id: \"atproto\",\n label: \"Bluesky / AT Protocol\",\n category: \"self-custodied\",\n summary: \"Publish a record on your Bluesky server\",\n description: \"Proves you control a Bluesky (AT Protocol) account: approve on your server, and Ghostly publishes one record in your account's repository. It is public, like the rest of your repository: anyone can see this account made a Ghostly proof, not who you share it with. Contacts check it without logging in.\",\n limits: \"Signed with your account's key, which your server usually holds. It proves control of the account, not who you are; the handle is shown only while it points back to the account.\",\n platforms: [\"web\", \"extension\", \"desktop\"],\n subject: {\n label: \"Account (DID)\",\n placeholder: \"did:plc:…\",\n normalize(input: string) {\n const did = input.trim();\n if (!isAtprotoDid(did)) throw new Error(\"Not an AT Protocol account (did:plc or did:web)\");\n return did;\n },\n short: shortAtprotoDid,\n },\n // The account's AT URI, for a public DID document's alsoKnownAs (#247), when the person lists it there.\n publicUri: did => `at://${did}`,\n validity: { defaultDays: 90, maxDays: 365 },\n // The record can be deleted any time: contacts look again after an hour.\n recheck: { afterSeconds: 3600 },\n privacy: \"Your contact's app asks the PLC directory (or your did:web host) for your account's document, your server for the record, and its DNS-over-HTTPS resolver or your handle's website for your handle, so they learn this account was checked.\",\n signers: [\n signer(\"atproto-oauth\", \"proof-records\", \"Your server (Bluesky or another PDS)\",\n \"Opens your server's login page. Ghostly asks only to create and delete its own proof records: never to post, read your messages or edit your profile.\"),\n signer(\"atproto-oauth-full\", \"full\", \"Your server, full access (older servers)\",\n \"Only if your server says it cannot limit Ghostly's permission: it then asks for access to your whole account. Ghostly still only writes this one record, and gives the access back right away.\"),\n ],\n parseEvidence(raw) {\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw) || Object.keys(raw).length) throw new Error(\"Invalid AT Protocol evidence\");\n return {};\n },\n async verify(statement, _evidence, ctx) {\n const did = statement.binding.subject;\n if (!isAtprotoDid(did)) throw new Error(\"Not an AT Protocol account\");\n const resolve = resolveOptions(ctx.fetch, ctx.signal);\n // The server comes from the DID document, never from the proof.\n const doc = await resolveAtprotoDid(did, resolve);\n await assertPublicServer(hostOf(doc.pds).replace(/:\\d+$/, \"\"), resolve);\n const rkey = atprotoProofRkey(statement.binding.key);\n const url = `${doc.pds}/xrpc/com.atproto.sync.getRecord?${new URLSearchParams({ did, collection: ATPROTO_PROOF_COLLECTION, rkey })}`;\n let response: Awaited<ReturnType<IdentityFetch>>;\n try { response = await ctx.fetch(url, { headers: { accept: \"application/vnd.ipld.car\" }, maxBytes: RECORD_PROOF_MAX_BYTES, signal: ctx.signal, redirect: \"error\" }); }\n // eslint-disable-next-line preserve-caught-error -- A message people can act on; the library's error stays out of the UI (ES2020 has no Error.cause).\n catch (e) { throw new Error(/too large/i.test(String(e)) ? \"The account's server sent a proof that is too large\" : `The account's server (${hostOf(doc.pds)}) could not be reached`); }\n if (response.status !== 200) {\n let code = \"\";\n try { code = String((JSON.parse(response.text) as { error?: unknown }).error ?? \"\"); } catch { /* not JSON */ }\n if (code === \"RecordNotFound\") throw new Error(\"The record is no longer in the account's repository\");\n if (/^Repo(NotFound|Deactivated|Takendown|Suspended)$/.test(code)) throw new Error(\"The account is no longer on its server\");\n throw new Error(`The account's server (${hostOf(doc.pds)}) did not send the record`);\n }\n let proof: ReturnType<typeof verifyAtprotoRecordProof>;\n try { proof = verifyAtprotoRecordProof(response.bytes, { did, key: doc.key, collection: ATPROTO_PROOF_COLLECTION, rkey }); }\n // eslint-disable-next-line preserve-caught-error -- A message people can act on; the library's error stays out of the UI (ES2020 has no Error.cause).\n catch (e) { throw new Error(e instanceof AtprotoProofError ? `The account's repository does not prove the record: ${e.message}` : \"The account's repository could not be read\"); }\n if (!proof.record) throw new Error(\"The record is no longer in the account's repository\");\n checkAtprotoProofRecord(proof.record.value, statement);\n const handle = await verifiedAtprotoHandle(doc, resolve);\n return {\n subject: did,\n source: `Record signed with the account's key, from ${hostOf(doc.pds)}`,\n display: {\n ...(handle ? { name: `@${handle}` } : {}),\n url: `https://bsky.app/profile/${did}`,\n source: handle ? \"Handle checked both ways: it points to this account\" : \"The account's handle does not point back to it, so it is not shown\",\n fetchedAt: ctx.now,\n },\n };\n },\n unpublish: {\n description: \"Also deletes the record on your server: your server asks you to approve.\",\n run(proof, ctx) {\n return signIn(proof.subject, \"proof-records\", ctx, async flow => {\n const access: AtprotoAccess = fullAccessServers.has(flow.account.pds) ? \"full\" : \"proof-records\";\n try { await unpublishAtprotoProof(proof.key, { ...flow, access }); }\n catch (e) {\n if (e instanceof AtprotoScopeError && access !== \"full\") {\n fullAccessServers.add(flow.account.pds);\n // eslint-disable-next-line preserve-caught-error -- A message people can act on; the library's error stays out of the UI (ES2020 has no Error.cause).\n throw new Error(\"Your server only offers full access to your account. Try again to approve that; Ghostly only deletes this one record.\");\n }\n throw e;\n }\n });\n },\n },\n };\n}\n\nexport const atproto = createAtprotoIdentityProvider();\n","import {\n homeserverWebEndpoint, IdentityCheckUnavailable, openRelayPayload, pubkyHomeserverOf, PUBKY_PROOF_MAX_BYTES, RELAY_PAYLOAD_MAX_BYTES,\n type HomeserverEndpoint,\n} from \"@ghostly/core\";\nimport type { AuthFlow, GrantAuthFlow, Session } from \"@synonymdev/pubky\";\nimport type { ApprovalRequest, IdentityFetch } from \"./contract\";\nimport { assertPublicHost, chosenResolver, type DohResolverId } from \"./domain\";\nimport { getBrowserHost, type PubkyCookieSession } from \"../host\";\n\n/**\n * Pubky, browser side: reading a key's records and files the way a contact's app does (through the proof\n * contract's bounded `ctx.fetch` only), and asking the person to approve one Pubky auth request in Pubky Ring or\n * Pubky Passport (https://github.com/pubky/pubky-passport/blob/main/docs/integration.md). See WISP 302.\n */\n\n// -- reading ---------------------------------------------------------------------------------------\n\n/** Pkarr relays the records are read from: Pubky's own, the ones the Pubky SDK uses. */\nexport const PUBKY_RELAYS: readonly string[] = [\"https://pkarr.pubky.org\", \"https://pkarr.pubky.app\"];\n\n/**\n * The newest DNS packet the relays hold for `key`, its signature checked against `key`. Every relay is asked; one\n * that is down, answers garbage or holds an older packet does not count. Throws when none has a valid one.\n */\nexport async function pubkyRecords(key: string, fetch: IdentityFetch, signal?: AbortSignal, relays: readonly string[] = PUBKY_RELAYS): Promise<Uint8Array> {\n const failed: unknown[] = [];\n const answers = await Promise.all(relays.map(async relay => {\n let r;\n try { r = await fetch(`${relay.replace(/\\/+$/, \"\")}/${key}`, { maxBytes: RELAY_PAYLOAD_MAX_BYTES, signal }); }\n catch (e) { failed.push(e); return undefined; }\n try { return r.status === 200 ? openRelayPayload(key, r.bytes) : undefined; } catch { return undefined; }\n }));\n const newest = answers.filter(a => !!a).sort((a, b) => (b.seq > a.seq ? 1 : b.seq < a.seq ? -1 : 0))[0];\n // No relay answered at all (offline, a time-out): say that, not that the key has no records.\n if (!newest && failed.length === relays.length && failed[0] instanceof Error) throw failed[0];\n if (!newest) throw new Error(\"This Pubky key has no records on the Pkarr relays\");\n return newest.dnsPacket;\n}\n\n/** Where `key`'s homeserver answers browsers: its `_pubky` record, then the homeserver's own HTTPS record. */\nexport async function pubkyHomeserver(key: string, fetch: IdentityFetch, signal?: AbortSignal, relays?: readonly string[]): Promise<{ homeserver: string; endpoint: HomeserverEndpoint }> {\n const homeserver = pubkyHomeserverOf(await pubkyRecords(key, fetch, signal, relays), key);\n if (!homeserver) throw new Error(\"This Pubky key names no homeserver\");\n const endpoint = homeserverWebEndpoint(await pubkyRecords(homeserver, fetch, signal, relays), homeserver);\n if (!endpoint) throw new Error(\"The homeserver of this Pubky key publishes no address a browser can reach\");\n return { homeserver, endpoint };\n}\n\n/**\n * A public file of `key` at `path`, from the homeserver its own records name: bounded, no redirects, the owner in the\n * `pubky-host` header the way the SDK addresses it. `undefined` when the homeserver says it is not there.\n *\n * The key's owner chooses that host, so it is contacted only on port 443 and only once its addresses are all public\n * (the name is resolved first through the chosen DNS-over-HTTPS resolver): a proof never makes a contact's app knock\n * on its own network. Failures on the way there are IdentityCheckUnavailable, which the contact is not told about.\n */\nexport async function readPubkyFile(key: string, path: `/pub/${string}`, fetch: IdentityFetch, signal?: AbortSignal, relays?: readonly string[], resolver: DohResolverId = chosenResolver().id): Promise<{ text: string | undefined; host: string }> {\n const { endpoint: { host, port } } = await pubkyHomeserver(key, fetch, signal, relays);\n if (port !== undefined) throw new IdentityCheckUnavailable(`The homeserver ${host} answers on port ${port}; only port 443 is contacted`);\n try { await assertPublicHost(host, { fetch, signal, resolver }); }\n catch (e) { throw new IdentityCheckUnavailable(`The homeserver ${host} was not contacted: ${e instanceof Error ? e.message : String(e)}`); }\n let r: Awaited<ReturnType<IdentityFetch>>;\n try { r = await fetch(`https://${host}${path}`, { headers: { \"pubky-host\": key }, maxBytes: PUBKY_PROOF_MAX_BYTES, signal }); }\n catch (e) {\n // A file over the cap is the owner's doing, and says nothing about this side's network.\n if (/too large/i.test(String(e))) throw e;\n throw new IdentityCheckUnavailable(`The homeserver ${host} could not be reached`);\n }\n if (r.status === 404) return { text: undefined, host };\n if (r.status !== 200) throw new IdentityCheckUnavailable(`The homeserver ${host} answered ${r.status}`);\n return { text: r.text, host };\n}\n\n// -- approving -------------------------------------------------------------------------------------\n\n/** The app identifier Pubky Ring and Passport list the grant under. */\nexport const PUBKY_CLIENT_ID = \"ghostly.tools\";\nexport const PASSPORT_ORIGIN = \"https://passport.pubky.app\";\n/** How long one request waits: Pubky's relay keeps a message about five minutes. */\nconst APPROVAL_TIMEOUT = 5 * 60_000;\nconst POLL_EVERY = 1_000;\n\n/**\n * Passport's authorize page for a request. The authorization URL carries the relay secret: it goes in the fragment\n * (never sent to a server), encoded exactly once, and is never logged.\n */\nexport const passportUrl = (authorizationUrl: string) => `${PASSPORT_ORIGIN}/authorize#d=${encodeURIComponent(authorizationUrl)}`;\n\n/** Messages from the SDK may quote the request: nothing that looks like one reaches the UI or a log. */\nconst redact = (text: string) => text.replace(/pubkyauth:\\/\\/\\S*/gi, \"(request)\").replace(/#d=\\S*/g, \"#d=(request)\");\nconst failure = (what: string, e: unknown) => new Error(`${what}${e instanceof Error && e.message ? `: ${redact(e.message)}` : \"\"}`);\n\n/** An approved Pubky session, only as long as `work` runs: the key, and writes inside the one folder granted. */\nexport interface PubkyApprovedSession {\n key: string;\n put(path: `/pub/${string}`, text: string): Promise<void>;\n delete(path: `/pub/${string}`): Promise<void>;\n}\n\nexport interface PubkyApprovalOptions {\n /** The one capability asked for, `/pub/…/:w`. A session granted anything else is refused. */\n capability: `/pub/${string}/:w`;\n signal: AbortSignal;\n onApproval(request: ApprovalRequest | null): void;\n onProgress(message: string): void;\n /** Where Passport opens. The desktop app hands it to the system browser; elsewhere a popup from the click. */\n openPassport?: (url: string) => Window | null | void;\n /** Tests: the SDK's relay, and a shorter wait. */\n relay?: string;\n timeoutMs?: number;\n}\n\n/**\n * Opens a Passport window from the click that calls it. It must run synchronously in the click handler, before\n * anything is awaited, or the browser blocks the popup; the desktop app opens the system browser instead.\n */\nexport function openPassportWindow(url: string): Window | null | void {\n let host: ReturnType<typeof getBrowserHost> | undefined;\n try { host = getBrowserHost(); } catch { host = undefined; }\n if (host?.openPubkyPassport) { void host.openPubkyPassport(url).catch(() => {}); return; }\n // Not `noopener`: the reference lets Ghostly close the window once either signer approved.\n return window.open(url, \"pubky-passport\", \"popup,width=520,height=760\");\n}\n\n/** What a request of either kind offers while it waits: one poll at a time, freed once no poll is in flight. */\ninterface PendingRequest { tryPollOnce(): Promise<Session | undefined>; free(): void }\n\n/** A 401 or 403 from the homeserver: the session it was given is not one it accepts. */\nconst refused = (e: unknown) => {\n const status = (e as { data?: { statusCode?: unknown } } | null)?.data?.statusCode;\n return status === 401 || status === 403;\n};\n\n/**\n * Polls every request until one of them is approved and resolves with that session and its request; a cancel, the\n * deadline or a failed poll ends the wait for all of them. Each request is freed once its last poll has settled\n * (never while one is in flight), and a session that lands after the end is signed out at once; `onSettled` runs\n * once all of that is done.\n */\nfunction firstApproval(requests: readonly PendingRequest[], signal: AbortSignal, timeoutMs: number, onSettled: () => void): Promise<{ session: Session; request: PendingRequest }> {\n return new Promise((resolve, reject) => {\n let over = false;\n let polling = requests.length;\n const wakers = new Set<() => void>();\n const end = (settle: () => void) => {\n if (over) return;\n over = true;\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", cancel);\n for (const wake of wakers) wake();\n settle();\n };\n const cancel = () => end(() => reject(signal.reason));\n const timer = setTimeout(() => end(() => reject(new Error(\"Nobody approved the request in time. Start again for a new one.\"))), timeoutMs);\n if (signal.aborted) cancel(); else signal.addEventListener(\"abort\", cancel, { once: true });\n const pause = () => new Promise<void>(done => {\n const wake = () => { clearTimeout(t); wakers.delete(wake); done(); };\n const t = setTimeout(wake, POLL_EVERY);\n wakers.add(wake);\n });\n\n // The end does not wait for a poll in flight (one that got an approval is still redeeming it at the homeserver):\n // its request is freed when it settles, and a session it brings is signed out.\n const poll = async (request: PendingRequest) => {\n try {\n while (!over) {\n let session: Session | undefined;\n try { session = await request.tryPollOnce(); }\n catch (e) { end(() => reject(failure(\"The Pubky request failed\", e))); return; }\n if (session && over) { try { await session.signout(); } catch { /* best effort */ } finally { session.free(); } return; }\n if (session) { const approved = session; end(() => resolve({ session: approved, request })); return; }\n if (!over) await pause();\n }\n } finally {\n request.free();\n if (--polling === 0) onSettled();\n }\n };\n for (const request of requests) void poll(request);\n });\n}\n\n/**\n * A request of a cookie session (Ring's approval) to its homeserver: its `/session` or a file of its storage\n * (`/storage/<key>/pub/…`, or `/pub/…` where the homeserver does not address storage by path), sent with the\n * browser's credentials. A grant session's requests carry an `Authorization` header instead, and the relay's\n * and the Pkarr relays' are elsewhere: those stay the page's.\n */\nconst cookieSessionRequest = (request: Request) =>\n request.credentials === \"include\" && !request.headers.has(\"authorization\") && /^\\/(?:session$|storage\\/|pub\\/)/.test(new URL(request.url).pathname);\n\n/** Statuses a Response carries no body with. */\nconst NO_BODY = new Set([204, 205, 304]);\n\n/**\n * Sends the SDK's cookie-session requests through `transport` until the returned function runs. The SDK fetches with\n * the global fetch, so this wraps it; everything else goes on to the page's fetch as it was. The cookie the\n * homeserver sets stays in the transport: the page, and so the SDK, never sees it.\n */\nfunction routeCookieSession(transport: PubkyCookieSession): () => void {\n const pageFetch = globalThis.fetch;\n let closed = false;\n const routed = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n // A Request made from another takes its body over: whatever is made here is what goes on.\n const request = input instanceof Request && init === undefined ? input : new Request(input, init);\n // Once closed, a wrapper something else has wrapped since stays in place: it only passes requests on.\n if (closed || !cookieSessionRequest(request)) return pageFetch(request);\n request.signal.throwIfAborted();\n const body = request.body ? new Uint8Array(await request.arrayBuffer()) : null;\n let answer;\n try { answer = await transport.fetch({ url: request.url, method: request.method, headers: [...request.headers], body }); }\n catch (e) { throw Object.assign(new TypeError(e instanceof Error ? e.message : String(e)), { cause: e }); }\n const response = new Response(NO_BODY.has(answer.status) ? null : answer.body as BodyInit, { status: answer.status, headers: answer.headers });\n // The SDK parses the answer's URL, which a Response made here does not have: it is the request's (no redirects).\n Object.defineProperty(response, \"url\", { value: request.url });\n return response;\n };\n globalThis.fetch = routed;\n return () => {\n if (closed) return;\n closed = true;\n if (globalThis.fetch === routed) globalThis.fetch = pageFetch;\n transport.close();\n };\n}\n\n/** The host's way around a page that drops the homeserver's cookie, if it has one. */\nfunction cookieSessionTransport(): PubkyCookieSession | undefined {\n try { return getBrowserHost().pubkyCookieSession?.(); } catch { return undefined; }\n}\n\n/**\n * Starts one request for `capability` in each of the two forms Pubky signers read (a grant request for Passport, a\n * cookie request for Ring's QR code: see below), shows both on one screen, and waits for whichever is approved\n * first, then runs `work` with that session. The session lives only in this renderer's memory while `work` runs,\n * and is signed out afterwards; nothing about it is stored. An approval that arrives after the end is signed out.\n */\nexport async function withPubkyApproval<T>(options: PubkyApprovalOptions, work: (session: PubkyApprovedSession) => Promise<T>): Promise<T> {\n const { signal } = options;\n signal.throwIfAborted();\n const { AuthFlow, GrantAuthFlow, AuthFlowKind } = await import(\"@synonymdev/pubky\");\n signal.throwIfAborted();\n const relay = options.relay ?? null;\n let grant: GrantAuthFlow, cookie: AuthFlow;\n try {\n // The flow's proof-of-possession key stays in this flow's memory (not the SDK's delegated IndexedDB key).\n grant = GrantAuthFlow.start(options.capability, AuthFlowKind.signin(), { clientId: PUBKY_CLIENT_ID, relay, xCallback: { xSource: \"Ghostly\" } });\n } catch (e) { throw failure(\"Could not start a Pubky request\", e); }\n try {\n // Same capability and relay, its own secret. The SDK deprecates this flow, but it is the one Ring's store build reads.\n cookie = AuthFlow.start(options.capability, AuthFlowKind.signin(), relay, { xSource: \"Ghostly\" });\n } catch (e) { grant.free(); throw failure(\"Could not start a Pubky request\", e); }\n\n const opened: { window?: Window | null } = {};\n options.onApproval({\n open: {\n label: \"Approve in your browser (Pubky Passport)\",\n description: \"Opens Pubky Passport. No Passport identity yet? It offers “Continue with Google” and makes one.\",\n run: () => {\n const window = (options.openPassport ?? openPassportWindow)(passportUrl(grant.authorizationUrl));\n opened.window = window ?? null;\n // null: the browser refused the popup (undefined: it opened elsewhere, the system browser).\n if (window === null) options.onProgress(\"Your browser blocked the Passport window: allow pop-ups for Ghostly and press the button again, or scan the code with Pubky Ring.\");\n },\n },\n // Ring gets the cookie request (`pubkyauth://signin?…`), not the grant one: the Ring in the app stores (1.19)\n // predates pubky/pubky-ring#360 and answers \"Unrecognized format\" to a `signin_grant` link. Move this to\n // grant.authorizationUrl, and drop the cookie flow, once Ring's store build parses `signin_grant`.\n qr: { value: cookie.authorizationUrl, label: \"Or scan with Pubky Ring\" },\n notes: [\n \"An identity Passport makes with Google is recovered with Google plus Passport: both are needed, neither alone can.\",\n \"The code is your request: scan it yourself, and do not share it.\",\n ],\n });\n\n // Where the page would drop the cookie session's cookie (the desktop app), its requests go through the host, from\n // the first poll (an approval is redeemed inside one) until the last poll has settled and the work is over.\n const transport = cookieSessionTransport();\n const unroute = transport ? routeCookieSession(transport) : () => {};\n let settled!: () => void;\n const polled = new Promise<void>(done => { settled = done; });\n try {\n return await approveAndWork(options, work, { grant, cookie, opened, settled, cookieKept: !!transport });\n } finally {\n void polled.then(unroute);\n }\n}\n\n/**\n * The rest of `withPubkyApproval`, once both requests are shown: the wait, the check, and the work. `cookieKept`: the\n * host keeps a cookie session's cookie, so a refusal is not the page's cookie policy.\n */\nasync function approveAndWork<T>(options: PubkyApprovalOptions, work: (session: PubkyApprovedSession) => Promise<T>, { grant, cookie, opened, settled, cookieKept }: {\n grant: GrantAuthFlow; cookie: AuthFlow; opened: { window?: Window | null }; settled: () => void; cookieKept: boolean;\n}): Promise<T> {\n const { signal } = options;\n let session: Session, viaCookie: boolean;\n try {\n const first = await firstApproval([grant, cookie], signal, options.timeoutMs ?? APPROVAL_TIMEOUT, settled);\n session = first.session;\n viaCookie = first.request === cookie;\n } finally {\n options.onApproval(null);\n try { if (opened.window && !opened.window.closed) opened.window.close(); } catch { /* another origin's window: best effort */ }\n }\n\n options.onProgress(\"Approved. Checking what Pubky granted…\");\n let key: string;\n try {\n const info = session.info;\n const publicKey = info.publicKey;\n try {\n key = publicKey.z32();\n const granted = info.capabilities;\n if (granted.length !== 1 || granted[0] !== options.capability)\n throw new Error(\"The signer granted different permissions than Ghostly asked for (one proof folder). Nothing was written.\");\n } finally { publicKey.free(); info.free(); }\n } catch (e) {\n await session.signout().catch(() => {});\n session.free();\n throw e;\n }\n\n // A cookie session lives in the homeserver's cookie, a third-party cookie in a page. WebKit (Safari) and Vanadium drop it, so the\n // homeserver then refuses the session's writes: say that, not \"401\". The desktop app keeps it outside its WebView.\n const fail = (what: string) => (e: unknown) => {\n if (viaCookie && !cookieKept && refused(e))\n throw new Error(\"Pubky Ring approved, but this browser blocks your homeserver's sign-in cookie (Vanadium and Safari block cookies from other sites), so nothing was written. Approve with Pubky Passport instead, or use Firefox or the Ghostly desktop app.\");\n throw failure(what, e);\n };\n const approved = session;\n try {\n return await work({\n key,\n put: (path, text) => approved.storage.putText(path, text).catch(fail(\"Could not write to your homeserver\")),\n delete: path => approved.storage.delete(path).catch(fail(\"Could not delete from your homeserver\")),\n });\n } finally {\n // The grant is only needed for this: end it, and forget the session.\n try { await approved.signout(); } catch { /* the grant expires on its own */ }\n approved.free();\n }\n}\n","import {\n isPubkyKey, isPubkyProofFolder, newPubkyProofFolder, normalizePubkyKey, pubkyProofCapability, pubkyProofFile, pubkyProofPath,\n} from \"@ghostly/core\";\nimport type { IdentityPlatform, IdentityProofProvider, InAppSigner } from \"../contract\";\nimport { readPubkyFile, withPubkyApproval, type PubkyApprovalOptions } from \"../pubky\";\n\n/**\n * A Pubky identity (WISP 302, docs/wisps/302-pubky.md), proven by publication: Pubky Ring and Pubky Passport sign\n * only standard Pubky auth requests, so Ghostly asks, in ONE request either of them can approve, for write access to\n * one fresh folder of the key's homeserver (`/pub/ghostly.app/proofs/<random>/`), and writes the identity statement\n * there. Contacts' apps read the file back from the homeserver the key's own signed Pkarr records name. The session\n * is used once, in memory, and signed out; removing the proof needs a new approval to delete the file.\n */\nexport interface PubkyEvidence { folder: string }\n\nconst short = (key: string) => `pubky${key.slice(0, 4)}…${key.slice(-4)}`;\n\n/** Strict: exactly `{ folder }`, 64 hex characters. Nothing else, so no evidence can name a host or another path. */\nfunction parseEvidence(raw: unknown): PubkyEvidence {\n const e = raw as PubkyEvidence;\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw) || Object.keys(raw).join(\",\") !== \"folder\" || !isPubkyProofFolder(e.folder))\n throw new Error(\"That is not Pubky proof evidence\");\n return { folder: e.folder };\n}\n\nexport interface PubkyIdentityOptions {\n /** Pkarr relays the verifier reads (tests). */\n relays?: readonly string[];\n /** The approval flow's knobs (tests: a local relay, a Passport stand-in). */\n approval?: Partial<Pick<PubkyApprovalOptions, \"relay\" | \"timeoutMs\" | \"openPassport\">>;\n}\n\nexport function createPubkyIdentityProvider(options: PubkyIdentityOptions = {}): IdentityProofProvider<PubkyEvidence> {\n const approve = <T>(capability: `/pub/${string}/:w`, ctx: Parameters<InAppSigner<PubkyEvidence>[\"run\"]>[0], work: Parameters<typeof withPubkyApproval<T>>[1]) => {\n if (!ctx.onApproval) throw new Error(\"This screen cannot show a Pubky request\");\n return withPubkyApproval({ ...options.approval, capability, signal: ctx.signal, onApproval: ctx.onApproval, onProgress: ctx.onProgress }, work);\n };\n\n const signer: InAppSigner<PubkyEvidence> = {\n id: \"pubky-auth\", kind: \"in-app\",\n label: \"Pubky Ring or Pubky Passport\",\n action: \"Continue\",\n description: \"One request: approve it in Pubky Passport in your browser, or scan it with Pubky Ring. Ghostly only asks to write one small file to a new folder of your homeserver.\",\n run(ctx, work) {\n const folder = newPubkyProofFolder();\n return approve(pubkyProofCapability(folder), ctx, async session => {\n let written: `/pub/${string}` | undefined;\n try {\n return await work({\n subject: async () => session.key,\n async sign(statement) {\n if (statement.binding.subject !== session.key) throw new Error(\"That is another Pubky identity\");\n ctx.onProgress(\"Writing the proof to your homeserver…\");\n const path = pubkyProofPath(folder, statement.id);\n await session.put(path, pubkyProofFile(statement));\n written = path;\n return { folder };\n },\n });\n } catch (e) {\n // Not saved (a failed check, a cancel): the file does not stay behind.\n if (written) await session.delete(written).catch(() => {});\n throw e;\n }\n });\n },\n };\n\n return {\n id: \"pubky\",\n label: \"Pubky\",\n category: \"self-custodied\",\n summary: \"Approve in Pubky Ring or Passport\",\n description: \"Proves you hold a Pubky key: approve one request in Pubky Ring or Pubky Passport, and Ghostly writes a small proof file to your homeserver. Contacts’ apps read it back through your key’s own records.\",\n limits: \"The file is on your homeserver: whoever runs it could write there too. It does not prove who holds the key.\",\n platforms: [\"web\", \"extension\", \"desktop\"] satisfies IdentityPlatform[],\n subject: {\n label: \"Pubky key\",\n placeholder: \"pubky…\",\n help: \"Comes from Pubky Ring or Passport when you approve.\",\n normalize: normalizePubkyKey,\n short,\n },\n validity: { defaultDays: 90, maxDays: 365 },\n // The file can be deleted, or the key can move to another homeserver, before the proof expires.\n recheck: { afterSeconds: 86_400 },\n privacy: \"Your contact’s app reads your key’s records from Pubky’s Pkarr relays (pkarr.pubky.org, pkarr.pubky.app), looks up your homeserver’s address through its DNS-over-HTTPS resolver (or the next one when that cannot be reached; it only contacts a public address, on port 443), and reads the proof file from your homeserver, which sees the contact’s IP address.\",\n signers: [signer],\n // Pubky's own URI form for a key, as its SDK writes resources (`pubky://<key>/…`).\n publicUri: key => `pubky://${key}`,\n parseEvidence,\n async verify(statement, { folder }, ctx) {\n const key = statement.binding.subject;\n if (!isPubkyKey(key)) throw new Error(\"That is not a Pubky key\");\n const { text, host } = await readPubkyFile(key, pubkyProofPath(folder, statement.id), ctx.fetch, ctx.signal, options.relays);\n if (text === undefined) throw new Error(\"The proof file is not on the homeserver\");\n if (text !== pubkyProofFile(statement)) throw new Error(\"The file on the homeserver is not this proof\");\n return { subject: key, source: `File on the homeserver ${host}, found through the key’s own Pkarr records` };\n },\n unpublish: {\n description: \"Also deletes the proof file on your homeserver: that takes one more approval in Pubky Ring or Passport, since Ghostly kept no access to it.\",\n async run({ id, subject, evidence }, ctx) {\n // Stored when the proof was made: parsed like any evidence, so a bad record never names another path.\n const { folder } = parseEvidence(evidence);\n const path = pubkyProofPath(folder, id);\n return approve(pubkyProofCapability(folder), ctx, async session => {\n if (session.key !== subject) throw new Error(\"That is another Pubky identity: approve with the one this proof is for\");\n ctx.onProgress(\"Deleting the proof file from your homeserver…\");\n await session.delete(path);\n });\n },\n },\n };\n}\n\nexport const pubky = createPubkyIdentityProvider();\n","import { ed25519 } from \"@noble/curves/ed25519.js\";\nimport { fromBase64Url, toBase64Url, utf8Decode, utf8Encode, type IdentityStatement } from \"@ghostly/core\";\nimport type { IdentityProofProvider } from \"./contract\";\n\n/**\n * Fake identity providers, for unit tests and e2e. No network, no real accounts. They are in the\n * registry only when `localStorage[\"ghostly-test-identities\"] === \"1\"` (or a test passes them itself).\n *\n * - fake-key self-custodied: an Ed25519 key held by a fake in-app signer, or signed \"outside\" and pasted.\n * - fake-account provider-attested: a fake issuer signs a token whose nonce is the statement id (like OIDC).\n * - fake-record self-custodied, published: a record the verifier fetches through `ctx.fetch` (like DNS).\n */\nexport const TEST_IDENTITIES_FLAG = \"ghostly-test-identities\";\nexport const testIdentitiesEnabled = () => { try { return globalThis.localStorage?.getItem(TEST_IDENTITIES_FLAG) === \"1\"; } catch { return false; } };\n\nconst HEX64 = /^[a-f0-9]{64}$/;\nconst hex = (b: Uint8Array) => Array.from(b, x => x.toString(16).padStart(2, \"0\")).join(\"\");\nconst unhex = (s: string) => Uint8Array.from(s.match(/../g)!, x => parseInt(x, 16));\nconst normalizeHexKey = (input: string) => { const v = input.trim().toLowerCase(); if (!HEX64.test(v)) throw new Error(\"Enter 64 hex characters\"); return v; };\nconst exactKeys = (raw: unknown, keys: string) => !!raw && typeof raw === \"object\" && !Array.isArray(raw) && Object.keys(raw).sort().join(\",\") === keys;\n\n/** The key behind the fake in-app signer. Tests may replace it. */\nexport const fakeKeyring = { seed: ed25519.utils.randomSecretKey() };\nexport const fakeKeySubject = (seed = fakeKeyring.seed) => hex(ed25519.getPublicKey(seed));\nexport const fakeKeySign = (statement: IdentityStatement, seed = fakeKeyring.seed) => ({ sig: toBase64Url(ed25519.sign(statement.bytes, seed)) });\n\nexport interface FakeKeyEvidence { sig: string }\nexport const fakeKey: IdentityProofProvider<FakeKeyEvidence> = {\n id: \"fake-key\", label: \"Test key\", category: \"self-custodied\",\n summary: \"Sign with the test key\", description: \"A test identity: an Ed25519 key held by this page. For tests only.\",\n platforms: [\"web\", \"extension\", \"desktop\"],\n subject: { label: \"Public key\", placeholder: \"64 hex characters\", normalize: normalizeHexKey, short: k => `${k.slice(0, 8)}…${k.slice(-4)}` },\n publicUri: k => `urn:ghostly-test:${k}`,\n validity: { defaultDays: 30, maxDays: 365 },\n privacy: \"Nothing: checked on this device.\",\n signers: [\n { id: \"fake-signer\", kind: \"in-app\", label: \"Test signer\", fields: [{ name: \"refuse\", label: \"Refuse\", kind: \"text\", optional: true }],\n run: async (ctx, work) => {\n ctx.signal.throwIfAborted();\n if (ctx.values.refuse) throw new Error(\"The test signer refused\");\n return work({ subject: async () => fakeKeySubject(), sign: async statement => fakeKeySign(statement) });\n } },\n { id: \"fake-tool\", kind: \"external-tool\", label: \"Sign outside Ghostly\",\n instructions: statement => ({ steps: [{ text: \"Sign this statement with your test key:\", copy: statement.text }], paste: { label: \"Signature (base64url)\" } }),\n parse: pasted => { const sig = pasted.trim(); if (!/^[A-Za-z0-9_-]{86}$/.test(sig)) throw new Error(\"That is not a test signature\"); return { sig }; } },\n ],\n parseEvidence(raw) {\n const e = raw as FakeKeyEvidence;\n if (!exactKeys(raw, \"sig\") || typeof e.sig !== \"string\" || !/^[A-Za-z0-9_-]{86}$/.test(e.sig)) throw new Error(\"Invalid test evidence\");\n return { sig: e.sig };\n },\n async verify(statement, evidence) {\n let ok: boolean;\n try { ok = ed25519.verify(fromBase64Url(evidence.sig), statement.bytes, unhex(statement.binding.subject)); } catch { ok = false; }\n if (!ok) throw new Error(\"Invalid test signature\");\n return { subject: statement.binding.subject, source: \"Test signature (Ed25519)\" };\n },\n};\n\n/** The fake issuer's key. Its public half is what fake-account trusts, like a provider's JWKS. */\nconst ISSUER_SEED = unhex(\"7a\".repeat(32));\nexport const FAKE_ISSUER = \"https://issuer.ghostly.test\";\nexport interface FakeAccountEvidence { token: string }\n/** What the fake login page signs: iss, sub, nonce (= statement id), iat. */\nexport function fakeAccountToken(statement: IdentityStatement, account = \"alice\", seed = ISSUER_SEED, iat = statement.binding.issuedAt): FakeAccountEvidence {\n const payload = toBase64Url(utf8Encode(JSON.stringify({ iss: FAKE_ISSUER, sub: account, nonce: statement.id, iat })));\n return { token: `${payload}.${toBase64Url(ed25519.sign(utf8Encode(payload), seed))}` };\n}\nexport const fakeAccount: IdentityProofProvider<FakeAccountEvidence> = {\n id: \"fake-account\", label: \"Test account\", category: \"provider-attested\",\n summary: \"Log in to the test issuer\", description: \"A test login provider that vouches for an account. For tests only.\",\n platforms: [\"web\", \"extension\", \"desktop\"],\n subject: { label: \"Provider\", options: [{ value: FAKE_ISSUER, label: \"Test issuer\" }],\n normalize: input => { if (input.trim() !== FAKE_ISSUER) throw new Error(\"Unknown test issuer\"); return FAKE_ISSUER; } },\n validity: { defaultDays: 30, maxDays: 90 },\n privacy: \"Nothing: the issuer's signature is checked on this device.\",\n signers: [{ id: \"fake-login\", kind: \"redirect\", label: \"Log in to the test issuer\",\n start: async (statement, ctx) => { ctx.signal.throwIfAborted(); return fakeAccountToken(statement, ctx.values.account || \"alice\"); } }],\n parseEvidence(raw) {\n const e = raw as FakeAccountEvidence;\n if (!exactKeys(raw, \"token\") || typeof e.token !== \"string\" || e.token.length > 2048 || !/^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]{86}$/.test(e.token)) throw new Error(\"Invalid test token\");\n return { token: e.token };\n },\n async verify(statement, { token }) {\n const [payload, sig] = token.split(\".\");\n if (!ed25519.verify(fromBase64Url(sig), utf8Encode(payload), ed25519.getPublicKey(ISSUER_SEED))) throw new Error(\"Not signed by the test issuer\");\n const claims = JSON.parse(utf8Decode(fromBase64Url(payload))) as { iss: string; sub: string; nonce: string };\n if (claims.iss !== statement.binding.subject) throw new Error(\"Token from another issuer\");\n if (claims.nonce !== statement.id) throw new Error(\"Token for another statement\");\n if (typeof claims.sub !== \"string\" || !/^[a-z0-9]{1,64}$/.test(claims.sub)) throw new Error(\"Invalid account\");\n return { subject: `${claims.iss}#${claims.sub}`, attester: \"issuer.ghostly.test\", source: \"Token signed by the test issuer\",\n display: { name: claims.sub, source: \"Test issuer profile\", fetchedAt: statement.binding.issuedAt } };\n },\n};\n\n/** fake-record's records: subject → published text. The verifier reads `https://records.ghostly.test/<subject>`. */\nexport const FAKE_RECORD_HOST = \"https://records.ghostly.test/\";\nexport const fakeRecordText = (statement: IdentityStatement) => `ghostly-proof=${statement.id}`;\nexport const fakeRecord: IdentityProofProvider<Record<string, never>> = {\n id: \"fake-record\", label: \"Test record\", category: \"self-custodied\",\n summary: \"Publish a test record\", description: \"A test record published where the verifier looks it up, like DNS. For tests only.\",\n platforms: [\"web\", \"extension\", \"desktop\"],\n subject: { label: \"Name\", placeholder: \"alice\", normalize: input => { const v = input.trim().toLowerCase(); if (!/^[a-z0-9-]{1,63}$/.test(v)) throw new Error(\"Letters, digits and dashes\"); return v; } },\n validity: { defaultDays: 30, maxDays: 365 },\n recheck: { afterSeconds: 3600 },\n privacy: \"The verifier asks records.ghostly.test for the record: that server learns the name was checked.\",\n signers: [{ id: \"fake-publish\", kind: \"publish\", label: \"Publish a record\",\n instructions: statement => ({ steps: [{ text: `Publish this record for ${statement.binding.subject}:`, copy: fakeRecordText(statement) }] }),\n evidence: () => ({}) }],\n parseEvidence(raw) { if (!exactKeys(raw, \"\")) throw new Error(\"Invalid test evidence\"); return {}; },\n async verify(statement, _evidence, ctx) {\n const response = await ctx.fetch(`${FAKE_RECORD_HOST}${statement.binding.subject}`, { maxBytes: 4096, signal: ctx.signal });\n if (response.status !== 200 || !response.text.split(\"\\n\").includes(fakeRecordText(statement))) throw new Error(\"The record is not published\");\n return { subject: statement.binding.subject, source: \"Record published at records.ghostly.test\" };\n },\n};\n\nexport const FAKE_IDENTITY_PROVIDERS: readonly IdentityProofProvider[] = [fakeKey, fakeAccount, fakeRecord];\n","import type { BitcoinNetwork } from \"@ghostly/core\";\nimport type { WalletMode } from \"../../../shared/mints\";\n\n/**\n * What every wallet provider (a source of Lightning or of on-chain Bitcoin) shares: where it runs, how\n * it is configured, and the one distinction that keeps money safe. See PROVIDERS.md.\n */\n\n/** Where Ghostly runs: the web app, the browser extension (engine in an offscreen document), the Tauri app. */\nexport type ProviderPlatform = \"web\" | \"extension\" | \"desktop\";\nexport const PROVIDER_PLATFORMS: readonly ProviderPlatform[] = [\"web\", \"extension\", \"desktop\"];\nexport type ProviderKind = \"lightning\" | \"onchain\";\n/** The chain a provider's sats live on. Only `bitcoin` is real money; the others belong to the Testnet mode. */\nexport type ProviderNetwork = BitcoinNetwork;\nexport const PROVIDER_NETWORKS: readonly ProviderNetwork[] = [\"bitcoin\", \"signet\", \"testnet\", \"regtest\", \"mutinynet\"];\nexport const networkMode = (network: ProviderNetwork): WalletMode => (network === \"bitcoin\" ? \"mainnet\" : \"testnet\");\n\n/**\n * One field of a provider's configuration form. The wallet card renders these; a provider that needs\n * more registers its own form component instead (apps/ui/src/components/wallet/providers/forms.ts).\n * `secret` fields (an NWC URI, a macaroon, a rune, an API key, a password) are sealed before they are\n * stored, never shown again, never put in the engine state, and never logged.\n */\nexport interface ProviderField {\n name: string;\n label: string;\n kind: \"text\" | \"url\" | \"secret\" | \"select\" | \"textarea\";\n placeholder?: string;\n help?: string;\n optional?: boolean;\n /** `select` only. */\n options?: { value: string; label: string }[];\n /** Pre-filled per mode, e.g. a public test server for Testnet. */\n defaults?: Partial<Record<WalletMode, string>>;\n /**\n * Values offered under a `url` or `text` field (a list to pick from, anything may still be typed), each\n * only while the other fields hold `when`: e.g. a public Esplora server per network.\n */\n suggestions?: { value: string; label: string; when?: Record<string, string> }[];\n /**\n * A server address that can be changed on a saved source without its secrets being typed again, and\n * without changing whose money it is: an Esplora server of a BDK wallet (the wallet is the phrase). The\n * source's \"Change server\" edits these fields only. Never mark a field that picks another account (a\n * node's URL: another node is other money).\n */\n changeable?: boolean;\n}\n\n/** What the person typed, split by the field kinds: `config` is shown back, `secrets` never is. */\nexport interface ProviderSettings {\n config: Record<string, string>;\n secrets: Record<string, string>;\n}\n\n/** What the engine hands a provider's factory. */\nexport interface ProviderHost {\n platform: ProviderPlatform;\n /** The wallet mode the source is for: its provider must be on a network of this mode. */\n mode: WalletMode;\n /**\n * Reserved for the built-in Cashu mint source: the engine's own wallet. It is not part of the SDK, and\n * no other provider should read it.\n */\n cashu?: unknown;\n /** Reserved for the built-in Fedimint source: the engine's Fedimint wallet (its joined federations). Not part of the SDK. */\n fedimint?: unknown;\n /** Ends when the source is replaced, the mode switches or the engine stops: abort long waits on it. */\n signal: AbortSignal;\n /**\n * Desktop only: calls a command of the Tauri app (apps/desktop), for what a WebView cannot do (a pinned\n * certificate, no CORS, a raw socket). Absent on the web and in the extension.\n */\n invoke?: <T>(command: string, args: Record<string, unknown>) => Promise<T>;\n}\n\n/**\n * A provider module's export. Registering a provider is adding its module and one line in\n * `registry.ts`; everything else (storage, sealing, per-mode sources, the card, journals) is shared.\n */\nexport interface ProviderDescriptor<P> {\n id: string;\n label: string;\n kind: ProviderKind;\n /** One line under the name in the source picker: what it is and who holds the money. */\n description: string;\n /** Networks it can run on. A mode lists only providers with a network of that mode. */\n networks: readonly ProviderNetwork[];\n platforms: readonly ProviderPlatform[];\n fields: readonly ProviderField[];\n /** Custodial (someone else holds the sats) or still experimental: said in the picker. */\n custodial?: boolean;\n experimental?: boolean;\n /** Checks the form before anything is contacted; throws a message fit for the person. */\n validate?(settings: ProviderSettings, mode: WalletMode): void;\n /** Connects. The engine then asks `info()` and refuses a network of the other mode. */\n create(settings: ProviderSettings, host: ProviderHost): Promise<P>;\n}\n\n/** A provider can be offered here: it runs on this platform and on a network of this mode. */\nexport const offeredIn = (descriptor: { platforms: readonly ProviderPlatform[]; networks: readonly ProviderNetwork[] }, platform: ProviderPlatform, mode: WalletMode) =>\n descriptor.platforms.includes(platform) && descriptor.networks.some((network) => networkMode(network) === mode);\n\nconst FIELD_KINDS = [\"text\", \"url\", \"secret\", \"select\", \"textarea\"];\n/** What is stored with the profile and sent on the wire in journals: stable, short, lowercase. */\nexport const PROVIDER_ID = /^[a-z][a-z0-9-]{0,31}$/;\n\n/**\n * Problems with a provider descriptor, empty when it is well-formed. The registry refuses a descriptor\n * with any; the contract suite checks the same list, so an author sees them before the app does.\n */\nexport function providerDescriptorProblems(d: ProviderDescriptor<unknown>): string[] {\n const problems: string[] = [];\n if (!PROVIDER_ID.test(d.id ?? \"\")) problems.push(\"id must match ^[a-z][a-z0-9-]{0,31}$\");\n if (!d.label?.trim()) problems.push(\"label is empty\");\n if (d.kind !== \"lightning\" && d.kind !== \"onchain\") problems.push(\"kind must be lightning or onchain\");\n if (!d.description?.trim()) problems.push(\"description is empty: say what it is and who holds the money\");\n if (!Array.isArray(d.networks) || !d.networks.length) problems.push(\"no networks\");\n else for (const n of d.networks) if (!PROVIDER_NETWORKS.includes(n)) problems.push(`unknown network ${String(n)}`);\n if (!Array.isArray(d.platforms) || !d.platforms.length) problems.push(\"no platforms\");\n else for (const p of d.platforms) if (!PROVIDER_PLATFORMS.includes(p)) problems.push(`unknown platform ${String(p)}`);\n if (!Array.isArray(d.fields)) problems.push(\"fields must be a list (empty when there is nothing to configure)\");\n else {\n const names = d.fields.map((f) => f.name);\n if (new Set(names).size !== names.length) problems.push(\"field names are not unique\");\n for (const f of d.fields) {\n if (!/^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/.test(f.name ?? \"\")) problems.push(`field ${String(f.name)} has an invalid name`);\n if (!f.label?.trim()) problems.push(`field ${f.name} has no label`);\n if (!FIELD_KINDS.includes(f.kind)) problems.push(`field ${f.name} has an unknown kind`);\n if (f.kind === \"select\" && !f.options?.length) problems.push(`field ${f.name} is a select without options`);\n if (f.suggestions !== undefined && (!Array.isArray(f.suggestions) || f.suggestions.some((o: { value?: unknown; label?: unknown } | undefined) => typeof o?.value !== \"string\" || typeof o?.label !== \"string\"))) problems.push(`field ${f.name} has malformed suggestions`);\n if (f.changeable && (f.kind === \"secret\" || f.kind === \"select\")) problems.push(`field ${f.name} cannot be changeable: only a server address can`);\n }\n }\n if (typeof d.create !== \"function\") problems.push(\"create is not a function\");\n if (d.validate !== undefined && typeof d.validate !== \"function\") problems.push(\"validate is not a function\");\n return problems;\n}\n\n/** The serializable part of a descriptor, for the UI (a function cannot cross the extension's port). */\nexport type ProviderDescriptorView = Omit<ProviderDescriptor<unknown>, \"create\" | \"validate\">;\n/** Only these fields reach the UI, whatever else a provider module keeps on its descriptor. */\nexport const describeProvider = ({ id, label, kind, description, networks, platforms, fields, custodial, experimental }: ProviderDescriptor<unknown>): ProviderDescriptorView =>\n ({ id, label, kind, description, networks, platforms, fields, custodial, experimental });\n\n/**\n * The provider proved that nothing left the wallet (refused before sending, no route found before any\n * HTLC, a transaction that was never broadcast). Safe to try again, with this or another source.\n *\n * Any other error from a spend means the outcome is UNKNOWN: the payment may be in flight or done. It is\n * recorded as such and only reconciled (asked about), never retried. When in doubt, do not throw this.\n */\nexport class NothingSpentError extends Error {\n constructor(message: string) { super(message); this.name = \"NothingSpentError\"; }\n}\n\n/**\n * The source could not be reached: no answer, a time-out, a refused connection, a server error. The\n * engine keeps trying by itself (with backoff) and shows the source as connecting meanwhile.\n */\nexport class SourceUnreachableError extends Error {\n constructor(message: string, cause?: unknown) { super(message); this.name = \"SourceUnreachableError\"; if (cause !== undefined) Object.assign(this, { cause }); }\n}\n\n/**\n * A setting of the source is wrong (a server on another network, a phrase that makes no wallet): trying\n * again changes nothing until the setting does. The engine shows it as unavailable at once.\n */\nexport class SourceConfigError extends Error {\n constructor(message: string, cause?: unknown) { super(message); this.name = \"SourceConfigError\"; if (cause !== undefined) Object.assign(this, { cause }); }\n}\n\n/** What went wrong connecting a source, by name (a plugin bundles its own copies of these classes). */\nexport type SourceProblem = \"unreachable\" | \"config\" | \"other\";\nexport const sourceProblem = (error: unknown): SourceProblem =>\n error instanceof Error && error.name === \"SourceUnreachableError\" ? \"unreachable\" : error instanceof Error && error.name === \"SourceConfigError\" ? \"config\" : \"other\";\n\n/**\n * Whether an error says nothing was spent. By name, not by class: a provider built outside the app (an\n * SDK plugin bundled with its own copy of `NothingSpentError`) must be understood the same way.\n */\nexport const isNothingSpentError = (error: unknown): error is NothingSpentError =>\n error instanceof NothingSpentError || (error instanceof Error && error.name === \"NothingSpentError\");\n\n/**\n * An error message fit to show and store: every secret the source was given is blanked out, in case a\n * library echoed a URI or a key back.\n */\nexport function redact(error: unknown, secrets: Record<string, string> = {}): string {\n let message = error instanceof Error ? error.message : String(error);\n for (const value of Object.values(secrets)) if (value && value.length >= 4) message = message.split(value).join(\"•••\");\n return message.slice(0, 300);\n}\n","import type { GhostlyAdapterPlugin } from \"./registry\";\n\n/**\n * The adapter plugins compiled into this build. Empty unless the build lists some: `GHOSTLY_PLUGINS`\n * names their modules (comma-separated paths from the repository root, each default-exporting a\n * `GhostlyAdapterPlugin`), and the Vite plugin in `packages/browser/vite-plugin.ts` swaps this module\n * for one that imports them. Release builds never set it; the e2e build carries the SDK example.\n */\nexport const BUNDLED_PLUGINS: readonly GhostlyAdapterPlugin[] = [];\n","import type { LightningProviderDescriptor } from \"../engine/paymentAdapters/providers/lightning\";\nimport type { OnchainProviderDescriptor } from \"../engine/paymentAdapters/providers/onchain\";\nimport { providerDescriptorProblems, type ProviderDescriptor, type ProviderKind } from \"../engine/paymentAdapters/providers/types\";\nimport type { IdentityProofProvider } from \"../proofs/contract\";\nimport { descriptorProblems } from \"../proofs/verify\";\nimport { BUNDLED_PLUGINS } from \"./bundled\";\n\n/**\n * Adapters written outside the app: wallet sources (Lightning, on-chain) and identity-proof providers\n * that register here instead of being added to the built-in lists. A plugin is a plain object naming\n * its adapters; the registries (`providers/registry.ts`, `proofs/registry.ts`) list what is registered\n * after the built-ins and before the test fakes, and the same platform and mode rules apply to all.\n *\n * Two ways in:\n * - at build time: `GHOSTLY_PLUGINS` lists the modules and `bundled.ts` is swapped for them;\n * - at run time: code already running in the engine's realm calls `registerAdapters` before the\n * pickers are shown (or after: the engine re-reads the lists when something registers).\n *\n * Trust: a plugin runs with the app's privileges, in the app's own JavaScript realm. There is no\n * sandbox, no permission prompt and no store; whoever builds a plugin in, or loads one, trusts its\n * author with everything the app can do, keys and sats included. A manifest with permissions and an\n * isolated runtime are a later phase (docs/wisps/ADAPTER-ROADMAP.md, \"Plugins, apps, catalogs\").\n *\n * The state lives on `globalThis` under a well-known symbol, so a plugin bundled with its own copy of\n * the SDK (a second copy of this module) still registers into the same lists the app reads.\n */\n\n/** The generation of the adapter contracts. A plugin says which one it was written against. */\nexport const SDK_API = 1;\nexport const PLUGIN_ID = /^[a-z][a-z0-9-]{0,63}$/;\n\nexport interface GhostlyAdapterPlugin {\n /** Stable, unique among plugins: `^[a-z][a-z0-9-]{0,63}$`. */\n id: string;\n /** The plugin's own version, for people. */\n version?: string;\n /** The contract generation it was written against: `SDK_API`. Another one is refused. */\n sdk: number;\n lightning?: readonly LightningProviderDescriptor[];\n onchain?: readonly OnchainProviderDescriptor[];\n identities?: readonly IdentityProofProvider[];\n}\n\nexport type AdapterKind = ProviderKind | \"identity\";\n\ninterface Store {\n plugins: Map<string, GhostlyAdapterPlugin>;\n /** Ids the built-in registries own, per kind: a plugin cannot take one. */\n reserved: Record<AdapterKind, Set<string>>;\n listeners: Set<() => void>;\n seeded: boolean;\n}\n\nconst KEY = Symbol.for(\"ghostly.adapters/1\");\nfunction store(): Store {\n const g = globalThis as unknown as Record<symbol, Store | undefined>;\n return (g[KEY] ??= { plugins: new Map(), reserved: { lightning: new Set(), onchain: new Set(), identity: new Set() }, listeners: new Set(), seeded: false });\n}\n\nconst ids = (kind: AdapterKind, plugin: GhostlyAdapterPlugin): string[] => {\n // Not trusted to be well formed: pluginProblems reads it before saying what is wrong with it.\n const list: unknown = kind === \"identity\" ? plugin.identities : kind === \"lightning\" ? plugin.lightning : plugin.onchain;\n return Array.isArray(list) ? list.map((a: { id?: string } | null) => a?.id as string) : [];\n};\nconst KINDS: readonly AdapterKind[] = [\"lightning\", \"onchain\", \"identity\"];\n\n/** Problems with a plugin, empty when it can be registered. Does not look at what is already registered. */\nexport function pluginProblems(plugin: GhostlyAdapterPlugin): string[] {\n const problems: string[] = [];\n if (!plugin || typeof plugin !== \"object\") return [\"not a plugin object\"];\n if (!PLUGIN_ID.test(plugin.id ?? \"\")) problems.push(\"plugin id must match ^[a-z][a-z0-9-]{0,63}$\");\n if (plugin.sdk !== SDK_API) problems.push(`plugin was written for SDK API ${String(plugin.sdk)}; this app speaks ${SDK_API}`);\n if (plugin.version !== undefined && (typeof plugin.version !== \"string\" || plugin.version.length > 64)) problems.push(\"version must be a short string\");\n for (const kind of [\"lightning\", \"onchain\"] as const) {\n const list = plugin[kind];\n if (list === undefined) continue;\n if (!Array.isArray(list)) { problems.push(`${kind} must be a list of descriptors`); continue; }\n for (const d of list as ProviderDescriptor<unknown>[]) {\n if (!d || typeof d !== \"object\") { problems.push(`${kind} ${String(d)}: not a descriptor`); continue; }\n const own = providerDescriptorProblems(d);\n if (d?.kind && d.kind !== kind) own.push(`is listed under ${kind} but says kind ${String(d.kind)}`);\n problems.push(...own.map((p) => `${kind} ${String(d?.id)}: ${p}`));\n }\n }\n if (plugin.identities !== undefined) {\n if (!Array.isArray(plugin.identities)) problems.push(\"identities must be a list of providers\");\n else for (const p of plugin.identities) {\n if (!p || typeof p !== \"object\") problems.push(`identity ${String(p)}: not a provider`);\n else problems.push(...descriptorProblems(p).map((m) => `identity ${String(p.id)}: ${m}`));\n }\n }\n for (const kind of KINDS) {\n const list = ids(kind, plugin);\n if (new Set(list).size !== list.length) problems.push(`${kind} ids are not unique within the plugin`);\n }\n if (!KINDS.some((kind) => ids(kind, plugin).length)) problems.push(\"the plugin brings no adapter\");\n return problems;\n}\n\n/** The built-in registries say which ids are theirs. A plugin using one is refused. */\nexport function reserveAdapterIds(kind: AdapterKind, list: readonly string[]): void {\n const s = store();\n for (const id of list) s.reserved[kind].add(id);\n}\n\n/**\n * Registers a plugin's adapters. Throws, registering nothing, when the plugin is malformed, its id or\n * one of its adapters' ids is taken (by a built-in or by another plugin), or its contract generation\n * is another one. Returns the function that unregisters it.\n */\nexport function registerAdapters(plugin: GhostlyAdapterPlugin): () => void {\n const problems = pluginProblems(plugin);\n const s = store();\n if (!problems.length) {\n if (s.plugins.has(plugin.id)) problems.push(`a plugin with id ${plugin.id} is already registered`);\n for (const kind of KINDS) for (const id of ids(kind, plugin)) {\n if (s.reserved[kind].has(id)) problems.push(`${kind} ${id} is a built-in of this app`);\n for (const other of s.plugins.values()) if (ids(kind, other).includes(id)) problems.push(`${kind} ${id} is already registered by plugin ${other.id}`);\n }\n }\n if (problems.length) throw new Error(`Cannot register plugin ${String(plugin?.id)}: ${problems.join(\"; \")}`);\n s.plugins.set(plugin.id, plugin);\n notify(s);\n return () => { if (s.plugins.get(plugin.id) === plugin) { s.plugins.delete(plugin.id); notify(s); } };\n}\n\nfunction notify(s: Store) { for (const listener of s.listeners) { try { listener(); } catch { /* a listener's problem */ } } }\n\n/** Called when a plugin registers or unregisters: the engine re-reads the lists. */\nexport function onAdaptersChanged(listener: () => void): () => void {\n const s = store();\n s.listeners.add(listener);\n return () => { s.listeners.delete(listener); };\n}\n\n/** Every registered plugin, in registration order (bundled ones first). */\nexport function registeredPlugins(): readonly GhostlyAdapterPlugin[] { seed(); return [...store().plugins.values()]; }\nexport function registeredLightningProviders(): readonly LightningProviderDescriptor[] { return registeredPlugins().flatMap((p) => p.lightning ?? []); }\nexport function registeredOnchainProviders(): readonly OnchainProviderDescriptor[] { return registeredPlugins().flatMap((p) => p.onchain ?? []); }\nexport function registeredIdentityProviders(): readonly IdentityProofProvider[] { return registeredPlugins().flatMap((p) => p.identities ?? []); }\n\n/**\n * The plugins compiled into this build register once, when they are first asked for. A broken one is\n * reported and skipped rather than taking the app down with it; the e2e that expects it fails instead.\n */\nfunction seed() {\n const s = store();\n if (s.seeded) return;\n s.seeded = true;\n for (const plugin of BUNDLED_PLUGINS) {\n try { registerAdapters(plugin); }\n catch (error) { console.error(`[ghostly] a bundled adapter plugin was not loaded: ${error instanceof Error ? error.message : String(error)}`); }\n }\n}\n\n/** Tests only: forget every registration, including the bundled ones. */\nexport function resetAdapterRegistry(): void {\n const s = store();\n s.plugins.clear(); s.seeded = false;\n for (const kind of KINDS) s.reserved[kind].clear();\n notify(s);\n}\n","import type { IdentityProofProvider } from \"./contract\";\nimport { bitcoin } from \"./providers/bitcoin\";\nimport { nostr } from \"./providers/nostr\";\nimport { domain } from \"./providers/domain\";\nimport { openpgp } from \"./providers/openpgp\";\nimport { ssh, sshGithub, sshGitlab } from \"./providers/ssh\";\nimport { oidc } from \"./providers/oidc\";\nimport { did } from \"./providers/did\";\nimport { atproto } from \"./providers/atproto\";\nimport { pubky } from \"./providers/pubky\";\nimport { FAKE_IDENTITY_PROVIDERS, testIdentitiesEnabled } from \"./testing\";\nimport { registeredIdentityProviders, reserveAdapterIds } from \"../plugins/registry\";\n\n/**\n * Every identity-proof provider built into Ghostly. Adding one is adding its module under `providers/`\n * and ONE line here; see PROOFS.md. The order is the order of the picker on the Identities page. A\n * provider written outside the app registers through `plugins/registry.ts` (the SDK) and comes after.\n */\nexport const IDENTITY_PROVIDERS: readonly IdentityProofProvider[] = [\n nostr,\n pubky,\n domain,\n openpgp,\n bitcoin,\n ssh, sshGithub, sshGitlab,\n oidc,\n did,\n atproto,\n];\n\nreserveAdapterIds(\"identity\", IDENTITY_PROVIDERS.map((p) => p.id));\n\nconst warned = new Set<string>();\n/** The built-in providers, then the ones plugins registered (a built-in's id wins), then the fakes when this browser asked for them (e2e). */\nexport function identityProviders(): readonly IdentityProofProvider[] {\n const external = registeredIdentityProviders().filter((p) => {\n const taken = IDENTITY_PROVIDERS.some((b) => b.id === p.id);\n if (taken && !warned.has(p.id)) { warned.add(p.id); console.error(`[ghostly] identity provider ${p.id} from a plugin is ignored: a built-in has that id`); }\n return !taken;\n });\n return [...IDENTITY_PROVIDERS, ...external, ...(testIdentitiesEnabled() ? FAKE_IDENTITY_PROVIDERS : [])];\n}\n\nexport function identityProvider(id: string, providers = identityProviders()): IdentityProofProvider | undefined {\n return providers.find(p => p.id === id);\n}\n","import { identityStatement, type IdentityBinding, type IdentityStatement } from \"@ghostly/core\";\nimport { IDENTITY_PROVIDERS } from \"@ghostly/browser/proofs/registry\";\nimport type { IdentityProofProvider, IdentitySigner, SignerInstructions } from \"@ghostly/browser/proofs/contract\";\nimport type { IdentityProofView } from \"@ghostly/browser/shared/types\";\nimport { chatOf, node, num, state, str, type ApiContext, type Method, type Params } from \"./apiKit\";\nimport { CliError } from \"./errors\";\n\n/**\n * Identity proofs (WISP 300, WISP 1100 phase 3b), as the Identities page makes them: the engine draws the proof key\n * and the statement; a signer of the provider makes the evidence; the engine checks it as a contact would before it\n * keeps it. Signers run here, where the app runs them in its page:\n *\n * - `external-tool` (ssh-keygen, gpg, a Bitcoin wallet): `identity add` prints the statement and what to run, and\n * `identity complete` takes what the tool printed. Between the two the draft lives in the daemon's memory.\n * - `publish` (a DNS record, a file on a domain): the same, and `complete` asks the engine to look it up.\n * - `in-app` (a Nostr signer over NIP-46, Pubky, AT Protocol): one `identity add`, with the signer's fields; a link\n * or code the signer waits on is reported as an `identity.approval` event and printed by the command.\n * - `redirect` (OpenID Connect) needs a browser window: app only.\n */\n\ninterface Draft { draftId: string; provider: string; signer: string; binding: IdentityBinding; createdAt: number }\n/** Drafts per daemon: a proof made in two steps needs the same process for both. */\nconst drafts = new Map<string, Draft>();\nconst DRAFT_TTL_MS = 60 * 60 * 1000;\n\nfunction provider(id: string): IdentityProofProvider {\n const found = IDENTITY_PROVIDERS.find((p) => p.id === id);\n if (!found) throw new CliError(\"not_found\", `No identity provider ${id} (ghostly identity providers)`);\n return found;\n}\n\nfunction signerOf(p: IdentityProofProvider, id?: string): IdentitySigner<unknown> {\n const usable = p.signers.filter((s) => s.kind !== \"redirect\");\n const found = id ? p.signers.find((s) => s.id === id) : usable[0];\n if (!found) throw new CliError(id ? \"not_found\" : \"unavailable\", id ? `${p.label} has no signer ${id}` : `${p.label} proofs need a browser window: make them in the app`);\n if (found.kind === \"redirect\") throw new CliError(\"unavailable\", `${found.label} signs in through a browser window: make this proof in the app`);\n return found as IdentitySigner<unknown>;\n}\n\nfunction instructionsJson(i: SignerInstructions) {\n return { steps: i.steps.map((s) => ({ text: s.text, ...(s.copy ? { copy: s.copy } : {}) })), ...(i.paste ? { paste: i.paste.label } : {}) };\n}\n\nexport function proofJson(p: IdentityProofView) {\n return {\n id: p.id, provider: p.provider, subject: p.subject, verified: p.verified, issuedAt: p.issuedAt, expiresAt: p.expiresAt,\n sharedWith: p.sharedWith, ...(p.publicUri ? { publicUri: p.publicUri } : {}),\n };\n}\n\nfunction pruneDrafts(now = Date.now()) {\n for (const [id, draft] of drafts) if (now - draft.createdAt > DRAFT_TTL_MS) drafts.delete(id);\n}\n\nasync function begin(ctx: ApiContext, p: IdentityProofProvider, subject: string, params: Params) {\n const normalized = p.subject?.normalize ? p.subject.normalize(subject) : subject;\n if (!normalized) throw new CliError(\"bad_request\", `Not a ${p.subject?.label ?? \"subject\"} ${p.label} takes`);\n const days = params.days === undefined ? undefined : num(params, \"days\", 0, { min: 1, max: 400 });\n return node(ctx).beginIdentityProof({ provider: p.id, subject: normalized, ...(days ? { validityDays: days } : {}) });\n}\n\nexport const IDENTITY_METHODS: Record<string, Method> = {\n async \"identity.providers\"() {\n return {\n providers: IDENTITY_PROVIDERS.map((p) => ({\n id: p.id, label: p.label, category: p.category, subject: p.subject ? { label: p.subject.label, placeholder: p.subject.placeholder ?? null } : null,\n signers: p.signers.map((s) => ({ id: s.id, kind: s.kind, label: s.label, headless: s.kind !== \"redirect\",\n ...(s.kind === \"in-app\" && s.fields ? { fields: s.fields.map((f) => ({ name: f.name, label: f.label, kind: f.kind, optional: !!f.optional })) } : {}) })),\n })),\n };\n },\n\n async \"identity.list\"(ctx) {\n return { proofs: state(ctx).identityProofs.map(proofJson) };\n },\n\n /** Starts a proof. In-app signers finish it here; the others return the statement and what to do with it. */\n async \"identity.add\"(ctx, params) {\n pruneDrafts();\n const p = provider(str(params, \"provider\", true));\n const signer = signerOf(p, str(params, \"signer\"));\n if (signer.kind === \"in-app\") {\n const values = (params.fields ?? {}) as Record<string, string>;\n if (typeof values !== \"object\" || Array.isArray(values) || Object.values(values).some((v) => typeof v !== \"string\")) throw new CliError(\"bad_request\", \"fields must be an object of strings\");\n for (const field of signer.fields ?? []) if (!field.optional && !values[field.name]) throw new CliError(\"bad_request\", `${signer.label} needs ${field.name} (${field.label})`);\n const signal = AbortSignal.timeout(num(params, \"timeout\", 300, { min: 5, max: 3600 }) * 1000);\n const proof = await signer.run({\n values, signal,\n onAuthUrl: (url) => { ctx.hub.emit(\"identity.approval\", `identity.approval:${Date.now()}`, { provider: p.id, url }); },\n onProgress: (message) => { ctx.hub.emit(\"identity.progress\", `identity.progress:${Date.now()}`, { provider: p.id, message }); },\n onApproval: (request) => {\n if (request) ctx.hub.emit(\"identity.approval\", `identity.approval:${Date.now()}`, { provider: p.id, ...(request.qr ? { qr: request.qr.value, label: request.qr.label } : {}), notes: request.notes ?? [] });\n },\n }, async (session) => {\n const { draftId, binding } = await begin(ctx, p, await session.subject(), params);\n try {\n return await node(ctx).completeIdentityProof({ draftId, evidence: await session.sign(identityStatement(binding)) });\n } catch (error) {\n try { await node(ctx).cancelIdentityProof({ draftId }); } catch { /* already gone */ }\n throw error;\n }\n });\n return { done: true, proof: proofJson(proof) };\n }\n if (signer.kind === \"redirect\") throw new CliError(\"unavailable\", `${signer.label} needs a browser window: make this proof in the app`);\n const { draftId, binding } = await begin(ctx, p, str(params, \"subject\", true), params);\n const statement = identityStatement(binding);\n drafts.set(draftId, { draftId, provider: p.id, signer: signer.id, binding, createdAt: Date.now() });\n return { done: false, draft: draftId, provider: p.id, signer: signer.id, statement: statement.text, instructions: instructionsJson(signer.instructions(statement)), expiresInSeconds: DRAFT_TTL_MS / 1000 };\n },\n\n /** Finishes a two-step proof: what the tool printed (external-tool), or nothing (publish: the engine looks it up). */\n async \"identity.complete\"(ctx, params) {\n const draft = drafts.get(str(params, \"draft\", true));\n if (!draft) throw new CliError(\"not_found\", \"No such draft here: it is kept by the daemon that made it, for an hour\");\n const p = provider(draft.provider);\n const signer = signerOf(p, draft.signer);\n const statement: IdentityStatement = identityStatement(draft.binding);\n let evidence: unknown;\n try {\n if (signer.kind === \"external-tool\") evidence = await signer.parse(str(params, \"evidence\", true), statement);\n else if (signer.kind === \"publish\") evidence = await signer.evidence(statement);\n else throw new CliError(\"bad_request\", \"This draft needs no completion\");\n } catch (error) {\n if (error instanceof CliError) throw error;\n throw new CliError(\"bad_request\", error instanceof Error ? error.message : String(error));\n }\n const proof = await node(ctx).completeIdentityProof({ draftId: draft.draftId, evidence });\n drafts.delete(draft.draftId);\n return { done: true, proof: proofJson(proof) };\n },\n\n async \"identity.cancel\"(ctx, params) {\n const draftId = str(params, \"draft\", true);\n // As `identity.complete` says it: a draft that is not here was never made, is done, or is another daemon's.\n if (!drafts.delete(draftId)) throw new CliError(\"not_found\", \"No such draft here: it is kept by the daemon that made it, for an hour\");\n await node(ctx).cancelIdentityProof({ draftId });\n return { cancelled: draftId };\n },\n\n async \"identity.remove\"(ctx, params) {\n const id = str(params, \"id\", true);\n // The engine removes nothing for an id it does not know, and says nothing.\n if (!state(ctx).identityProofs.some((p) => p.id === id)) throw new CliError(\"not_found\", `No identity proof ${JSON.stringify(id)} (ghostly identity list)`);\n await node(ctx).removeIdentityProof({ id });\n return { removed: id };\n },\n\n async \"identity.share\"(ctx, params) {\n const link = chatOf(ctx, params);\n await node(ctx).shareIdentityProof({ linkId: link.id, id: str(params, \"id\", true) });\n return { chat: link.id, shared: str(params, \"id\", true) };\n },\n\n async \"identity.withdraw\"(ctx, params) {\n const link = chatOf(ctx, params);\n const id = str(params, \"id\", true);\n if (!link.identities?.shared.some((s) => s.id === id)) throw new CliError(\"not_found\", `No identity proof ${JSON.stringify(id)} shared in this chat (ghostly identity contact ${link.id})`);\n await node(ctx).withdrawIdentityProof({ linkId: link.id, id });\n return { chat: link.id, withdrawn: id };\n },\n\n /** What a contact shared in a chat, as this device checked it, and what this side shares there. */\n async \"identity.contact\"(ctx, params) {\n const link = chatOf(ctx, params);\n const view = link.identities;\n return {\n chat: link.id, support: view?.support ?? false,\n received: (view?.received ?? []).map((r) => ({ id: r.id, provider: r.provider, subject: r.subject, status: r.status, verified: r.verified, verifiedAt: r.verifiedAt, checkedAt: r.checkedAt, expiresAt: r.expiresAt, error: r.error ?? null, recheckDue: r.recheckDue, ...(r.publicProfile ? { publicProfile: r.publicProfile } : {}) })),\n shared: view?.shared ?? [],\n };\n },\n\n async \"identity.recheck\"(ctx, params) {\n const link = chatOf(ctx, params);\n await node(ctx).recheckIdentityProof({ linkId: link.id, id: str(params, \"id\", true) });\n return IDENTITY_METHODS[\"identity.contact\"](ctx, { chat: link.id });\n },\n};\n","import { randomBytes, timingSafeEqual } from \"node:crypto\";\nimport { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\";\nimport type { AddressInfo } from \"node:net\";\nimport { Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport { GhostlyHttpError, LIMITS, type ClientRequest, type ClientResponse, type LocalFetch } from \"@ghostly/core\";\nimport { bool, chatOf, node, num, state, str, type ApiContext, type Method, type Params } from \"./apiKit\";\nimport { CliError } from \"./errors\";\nimport { isLive } from \"./views\";\n\n/**\n * Shared services (WISP 700/701, WISP 1100 phase 3b): a web app on this machine a contact may open over the chat's live\n * link, and a contact's app opened here on a loopback port. As on the Desktop: only loopback is reached, redirects are\n * handed back rather than followed, and a service is shared with each contact by name, never by default.\n */\n\nconst LOOPBACK = new Set([\"127.0.0.1\", \"localhost\", \"[::1]\", \"::1\"]);\n\n/** The engine's way to reach a shared app: loopback only, no redirect followed, no cookies (Node sends none). */\nexport const nodeLocalFetch: LocalFetch = async (request) => {\n const url = new URL(request.url);\n if (![\"http:\", \"https:\"].includes(url.protocol) || !(LOOPBACK.has(url.hostname) || /^127\\.\\d+\\.\\d+\\.\\d+$/.test(url.hostname))) {\n throw new Error(\"Shared services are reached on this machine only\");\n }\n const response = await fetch(url, { method: request.method, headers: request.headers, body: request.body as BodyInit | null, signal: request.signal, redirect: \"manual\", ...(request.body ? { duplex: \"half\" } : {}) } as RequestInit);\n const headers: [string, string][] = [];\n response.headers.forEach((value, name) => headers.push([name, value]));\n return {\n status: response.status, headers,\n body: response.body ? (async function* () {\n const reader = response.body!.getReader();\n try { for (;;) { const { done, value } = await reader.read(); if (done) return; if (value?.length) yield value; } } finally { reader.cancel().catch(() => {}); }\n })() : null,\n };\n};\n\n/** Contact services open on loopback ports, by `<peer>/<service>`, while this process runs. */\nconst open = new Map<string, OpenedService>();\nconst MAX_RESPONSE_BYTES = 32 * 1024 * 1024;\n/** Cookie names an opened service may set and have passed back (browsers keep about 180 per host). */\nconst MAX_COOKIE_NAMES = 256;\n\n/**\n * The cookie that shows a browser was given this service's link here. It is this machine's, never the contact's:\n * it is not passed on to the contact, and a `Set-Cookie` from the contact by that name is dropped.\n */\nexport const OPEN_COOKIE = \"ghostly_open\";\n/** The link `service open` prints: it sets `OPEN_COOKIE` and sends the browser on to `/`. */\nconst OPEN_PATH = \"/.ghostly-open/\";\n\n/**\n * Only the cookies this service set itself (by name, from its own `Set-Cookie`) go on to the contact; any other a\n * browser sends (one set for localhost by another app here, this machine's `OPEN_COOKIE`) stays here.\n */\nexport function ownCookies(header: string, names: ReadonlySet<string>): string | null {\n const kept = header.split(\";\").map((pair) => pair.trim()).filter((pair) => {\n const at = pair.indexOf(\"=\");\n const name = at > 0 ? pair.slice(0, at).trim() : \"\";\n return !!name && name !== OPEN_COOKIE && names.has(name);\n });\n return kept.length ? kept.join(\"; \") : null;\n}\n\n/** The cookie a `Set-Cookie` sets, by name. */\nexport function setCookieName(value: string): string | null {\n const at = value.indexOf(\"=\"), end = value.indexOf(\";\");\n if (at <= 0 || (end !== -1 && end < at)) return null;\n return value.slice(0, at).trim() || null;\n}\n\n/**\n * A contact's `Set-Cookie` as this machine passes it to the browser: without `Domain`, so the cookie stays on the\n * service's own host name (not every *.localhost). Null for one this machine does not take (`OPEN_COOKIE`, no name).\n */\nexport function hostOnlyCookie(value: string): string | null {\n const name = setCookieName(value);\n if (!name || name === OPEN_COOKIE) return null;\n return value.split(\";\").filter((part, i) => i === 0 || !/^\\s*domain\\s*(=|$)/i.test(part)).join(\";\");\n}\n\n/** A cookie's values in a `Cookie` header, by name (a page may set another by the same name on a narrower path). */\nfunction cookieValues(header: string | undefined, name: string): string[] {\n return (header ?? \"\").split(\";\").flatMap((pair) => {\n const at = pair.indexOf(\"=\");\n return at > 0 && pair.slice(0, at).trim() === name ? [pair.slice(at + 1).trim()] : [];\n });\n}\n\nfunction sameSecret(a: string, b: string): boolean {\n return a.length === b.length && timingSafeEqual(Buffer.from(a), Buffer.from(b));\n}\n\n/** How an opened service reaches the contact's app: the engine's `request` for that contact and service. */\nexport type ServiceSend = (request: ClientRequest) => Promise<ClientResponse>;\n\nexport interface OpenedService {\n server: Server;\n /** The link to open: it hands the browser this service's cookie, then shows `/`. */\n url: string;\n /** The service's own host name, `<random>.localhost:<port>`: any other `Host` is not this service. */\n host: string;\n}\n\nfunction plain(response: ServerResponse, status: number, text: string, headers: Record<string, string> = {}): void {\n response.writeHead(status, { \"content-type\": \"text/plain; charset=utf-8\", \"cache-control\": \"no-store\", ...headers });\n response.end(text);\n}\n\n/**\n * A contact's app on a loopback port of this machine, under a host name of its own (`<random>.localhost`): its\n * cookies and storage are its own, apart from any other app on 127.0.0.1 or localhost, and a name that merely\n * resolves here (DNS rebinding) is not it. Only a browser given the link (which sets `OPEN_COOKIE`) is served; anyone\n * else on this machine who finds the port gets 404. The body streams through, bounded by `MAX_RESPONSE_BYTES`.\n */\nexport async function openServiceServer(send: ServiceSend, port = 0): Promise<OpenedService> {\n const name = `${randomBytes(16).toString(\"hex\")}.localhost`;\n const token = randomBytes(32).toString(\"hex\");\n const cookies = new Set<string>();\n let host = \"\";\n const server = createServer((request, response) => {\n if (request.headers.host?.toLowerCase() !== host) return plain(response, 404, \"Not found\");\n const path = request.url ?? \"/\";\n if (path.startsWith(OPEN_PATH)) {\n if (!sameSecret(path.slice(OPEN_PATH.length), token)) return plain(response, 404, \"Not found\");\n return plain(response, 303, \"\", { location: \"/\", \"referrer-policy\": \"no-referrer\", \"set-cookie\": `${OPEN_COOKIE}=${token}; Path=/; HttpOnly; SameSite=Lax` });\n }\n if (!cookieValues(request.headers.cookie, OPEN_COOKIE).some((value) => sameSecret(value, token))) return plain(response, 404, \"Not found\");\n const chunks: Buffer[] = [];\n let size = 0, refused = false;\n request.on(\"data\", (chunk: Buffer) => {\n if (refused) return;\n size += chunk.length;\n if (size > LIMITS.maxRequestBodyBytes) {\n refused = true;\n chunks.length = 0;\n plain(response, 413, \"The request body is too large\", { connection: \"close\" });\n return;\n }\n chunks.push(chunk);\n });\n request.on(\"end\", () => { if (!refused) void proxy(request, response, chunks, send, cookies); });\n });\n await new Promise<void>((resolve, reject) => { server.once(\"error\", reject); server.listen(port, \"127.0.0.1\", () => { server.off(\"error\", reject); resolve(); }); });\n host = `${name}:${(server.address() as AddressInfo).port}`;\n return { server, host, url: `http://${host}${OPEN_PATH}${token}` };\n}\n\nasync function proxy(request: IncomingMessage, response: ServerResponse, chunks: Buffer[], send: ServiceSend, cookies: Set<string>): Promise<void> {\n // A browser that goes away stops the contact's answer too.\n const stop = new AbortController();\n response.on(\"close\", () => { if (!response.writableFinished) stop.abort(); });\n try {\n const headers = Object.entries(request.headers).flatMap(([name, value]) => (Array.isArray(value) ? value.map((v) => [name, v] as [string, string]) : value === undefined ? [] : [[name, value] as [string, string]]))\n .flatMap(([name, value]): [string, string][] => {\n if (name.toLowerCase() !== \"cookie\") return [[name, value]];\n const own = ownCookies(value, cookies);\n return own ? [[name, own]] : [];\n });\n const answer = await send({ method: request.method ?? \"GET\", path: request.url ?? \"/\", headers, body: chunks.length ? new Uint8Array(Buffer.concat(chunks)) : null, maxResponseBytes: MAX_RESPONSE_BYTES, signal: stop.signal });\n const out: Record<string, string | string[]> = {};\n for (const [name, value] of answer.headers) {\n const lower = name.toLowerCase();\n if (lower === \"transfer-encoding\" || lower === \"connection\") continue;\n if (lower === \"set-cookie\") {\n const cookie = hostOnlyCookie(value);\n if (!cookie) continue;\n if (cookies.size < MAX_COOKIE_NAMES) cookies.add(setCookieName(cookie)!);\n out[lower] = [...((out[lower] as string[] | undefined) ?? []), cookie];\n } else out[lower] = value;\n }\n response.writeHead(answer.status, out);\n // Streamed, not gathered: past the cap (or when the contact drops it) the body stops and the connection closes.\n await pipeline(Readable.from(answer.body, { objectMode: false }), response);\n } catch (error) {\n // Once the status line is out, a second one cannot follow: the response is cut, and the browser sees it end short.\n if (response.headersSent) { response.destroy(); return; }\n const gone = error instanceof GhostlyHttpError && [\"unreachable\", \"timeout\", \"closed\", \"offline\"].includes(error.code);\n plain(response, gone ? 503 : 502, gone ? \"This service is not reachable. Services exist while their ghost is online.\" : `The request failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n}\n\n/**\n * The service a command names, by its id: one of this profile's. The engine changes nothing for an id it does not\n * know and says nothing, so a mistyped id read as done (\"shared\": true, with a contact).\n */\nfunction serviceOf(ctx: ApiContext, params: Params): string {\n const serviceId = str(params, \"service\", true);\n if (!state(ctx).services.some((s) => s.id === serviceId)) throw new CliError(\"not_found\", `No service ${JSON.stringify(serviceId)} (ghostly service list)`);\n return serviceId;\n}\n\nexport const SERVICE_METHODS: Record<string, Method> = {\n async \"service.list\"(ctx) {\n return { services: state(ctx).services.map((s) => ({ id: s.id, name: s.name, target: s.target, enabled: s.enabled, sharedWith: s.sharedWith ?? [], requests: s.requests })) };\n },\n async \"service.add\"(ctx, params) {\n const target = str(params, \"target\", true);\n let url: URL;\n try { url = new URL(target); } catch { throw new CliError(\"bad_request\", `Not a URL: ${target}`); }\n if (!(LOOPBACK.has(url.hostname) || /^127\\.\\d+\\.\\d+\\.\\d+$/.test(url.hostname))) throw new CliError(\"refused\", \"Only a web app on this machine (127.0.0.1, localhost) can be shared\");\n return node(ctx).addService({ name: str(params, \"name\", true), target });\n },\n async \"service.remove\"(ctx, params) {\n const serviceId = serviceOf(ctx, params);\n await node(ctx).removeService({ serviceId });\n return { removed: serviceId };\n },\n async \"service.enable\"(ctx, params) {\n const serviceId = serviceOf(ctx, params);\n await node(ctx).setServiceEnabled({ serviceId, enabled: !bool(params, \"off\") });\n return { service: serviceId, enabled: !bool(params, \"off\") };\n },\n async \"service.share\"(ctx, params) {\n const link = chatOf(ctx, params);\n const shared = !bool(params, \"off\");\n const serviceId = serviceOf(ctx, params);\n await node(ctx).setServiceShared({ serviceId, peerPubKeyZ32: link.peerPubKeyZ32, shared });\n return { service: serviceId, chat: link.id, shared };\n },\n /** What a contact shares with this profile (while its app is live). */\n async \"service.peer\"(ctx, params) {\n const link = chatOf(ctx, params);\n return { chat: link.id, live: isLive(link), services: link.peerServices ?? [] };\n },\n /** A contact's shared app on a loopback port of this machine, as the Desktop opens it in a window. */\n async \"service.open\"(ctx, params) {\n if (ctx.mode !== \"daemon\") throw new CliError(\"unavailable\", \"An opened service lives in the daemon: start `ghostly daemon --detach` first\");\n const link = chatOf(ctx, params);\n const service = str(params, \"service\", true);\n const key = `${link.peerPubKeyZ32}/${service}`;\n const already = open.get(key);\n if (already) return { chat: link.id, service, url: already.url };\n if (!link.peerServices?.some((s) => s.id === service)) throw new CliError(\"not_found\", `The contact shares no service ${service} (or is not live)`);\n const peer = link.peerPubKeyZ32;\n const opened = await openServiceServer((request) => node(ctx).request(peer, service, request), num(params, \"port\", 0, { min: 0, max: 65535 }));\n open.set(key, opened);\n return { chat: link.id, service, url: opened.url };\n },\n async \"service.close\"(ctx, params) {\n const link = chatOf(ctx, params);\n const key = `${link.peerPubKeyZ32}/${str(params, \"service\", true)}`;\n const entry = open.get(key);\n if (!entry) throw new CliError(\"not_found\", \"That service is not open here\");\n open.delete(key);\n await new Promise<void>((resolve) => entry.server.close(() => resolve()));\n return { closed: key };\n },\n};\n","import { toBase64Url } from \"@ghostly/core\";\n\n/**\n * Base64url without padding. Core's encoder, not `btoa`: every picture, voice note and kept file (up to 16 MiB each)\n * goes through it on the page's thread, where `btoa` spends over a second per 16 MiB.\n */\nexport { toBase64Url };\nexport function fromBase64Url(text: string): Uint8Array {\n if (!/^[A-Za-z0-9_-]*$/.test(text)) throw new Error(\"Invalid base64url\");\n const binary = atob(text.replace(/-/g, \"+\").replace(/_/g, \"/\") + \"=\".repeat((4 - (text.length % 4)) % 4));\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n\ntype Tagged = { $ghostly: \"bigint\" | \"bytes\" | \"blob\"; value: string; type?: string };\n/** Data that happens to look like a tag (a contact can send anything) travels as its entries instead. */\ntype Escaped = { $ghostly: \"object\"; entries: [string, unknown][] };\n\n/**\n * Blobs cannot be read synchronously: turn them into tagged values first, everywhere in the tree. An\n * object of the data's own with a `$ghostly` key is escaped, so it comes back as it was and not as a tag.\n */\nasync function inlineBlobs(value: unknown): Promise<unknown> {\n if (typeof Blob !== \"undefined\" && value instanceof Blob) return { $ghostly: \"blob\", type: value.type, value: toBase64Url(new Uint8Array(await value.arrayBuffer())) } satisfies Tagged;\n if (Array.isArray(value)) return Promise.all(value.map(inlineBlobs));\n if (value && typeof value === \"object\" && !(value instanceof Uint8Array) && !(value instanceof ArrayBuffer) && !ArrayBuffer.isView(value)) {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) out[k] = await inlineBlobs(v);\n return Object.prototype.hasOwnProperty.call(out, \"$ghostly\") ? ({ $ghostly: \"object\", entries: Object.entries(out) } satisfies Escaped) : out;\n }\n return value;\n}\n\n/** JSON that also carries what IndexedDB holds: bigints, bytes and blobs (WISP 05). */\nexport async function encode(value: unknown): Promise<string> {\n return JSON.stringify(await inlineBlobs(value), (_key, v) => {\n if (typeof v === \"bigint\") return { $ghostly: \"bigint\", value: String(v) } satisfies Tagged;\n if (v instanceof Uint8Array) return { $ghostly: \"bytes\", value: toBase64Url(v) } satisfies Tagged;\n if (v instanceof ArrayBuffer) return { $ghostly: \"bytes\", value: toBase64Url(new Uint8Array(v)) } satisfies Tagged;\n return v;\n });\n}\n/** Never throws on a tag it cannot read: that value stays as it is, and the rest of the backup restores. */\nexport function decode(text: string): unknown {\n return JSON.parse(text, (_key, v) => {\n const escaped = v as Partial<Escaped> | null;\n if (escaped && typeof escaped === \"object\" && escaped.$ghostly === \"object\" && Array.isArray(escaped.entries)) {\n const out: Record<string, unknown> = {};\n for (const entry of escaped.entries) if (Array.isArray(entry) && typeof entry[0] === \"string\") Object.defineProperty(out, entry[0], { value: entry[1], enumerable: true, writable: true, configurable: true });\n return out;\n }\n const tag = v as Partial<Tagged> | null;\n if (!tag || typeof tag !== \"object\" || typeof tag.$ghostly !== \"string\" || typeof tag.value !== \"string\") return v;\n if (tag.$ghostly === \"bigint\") return /^-?\\d+$/.test(tag.value) ? BigInt(tag.value) : v;\n if (!/^[A-Za-z0-9_-]*$/.test(tag.value)) return v;\n if (tag.$ghostly === \"bytes\") return fromBase64Url(tag.value);\n if (tag.$ghostly === \"blob\") return new Blob([fromBase64Url(tag.value) as BlobPart], { type: typeof tag.type === \"string\" ? tag.type : \"\" });\n return v;\n });\n}\n","import { fromBase64Url, toBase64Url } from \"./codec\";\n\n/** The clear part of a bundle (WISP 05): only what decryption needs. */\nexport interface Envelope {\n format: \"ghostly-backup\";\n version: 1;\n kdf: { name: \"PBKDF2-SHA256\"; iterations: number; salt: string };\n cipher: { name: \"AES-256-GCM\"; iv: string };\n compression: \"gzip\" | \"none\";\n ciphertext: string;\n}\nexport const BACKUP_MEDIA_TYPE = \"application/vnd.ghostly.backup+json\";\nconst ITERATIONS = 600_000;\n\nasync function keyFrom(passphrase: string, salt: Uint8Array, iterations: number): Promise<CryptoKey> {\n const material = await crypto.subtle.importKey(\"raw\", new TextEncoder().encode(passphrase), \"PBKDF2\", false, [\"deriveKey\"]);\n return crypto.subtle.deriveKey({ name: \"PBKDF2\", hash: \"SHA-256\", salt: salt as BufferSource, iterations }, material, { name: \"AES-GCM\", length: 256 }, false, [\"encrypt\", \"decrypt\"]);\n}\n/** A profile with years of files is large, but not this large: past it, a bundle is refused, not read. */\nexport const MAX_BACKUP_BYTES = 1024 * 1024 * 1024;\n\nasync function through(bytes: Uint8Array, stream: CompressionStream | DecompressionStream): Promise<Uint8Array> {\n const reader = new Blob([bytes as BlobPart]).stream().pipeThrough(stream).getReader();\n const chunks: Uint8Array[] = [];\n let size = 0;\n for (let next = await reader.read(); !next.done; next = await reader.read()) {\n size += next.value.length;\n if (size > MAX_BACKUP_BYTES) { await reader.cancel(); throw new Error(\"This backup is too large to restore\"); }\n chunks.push(next.value);\n }\n const out = new Uint8Array(size);\n let at = 0;\n for (const chunk of chunks) { out.set(chunk, at); at += chunk.length; }\n return out;\n}\n\n/**\n * The clear header is authenticated with the ciphertext (AES-GCM associated data): changing any of it,\n * the compression included, makes the bundle fail to open instead of being read differently.\n */\nconst header = (e: Pick<Envelope, \"format\" | \"version\" | \"kdf\" | \"cipher\" | \"compression\">) =>\n new TextEncoder().encode([e.format, e.version, e.kdf.name, e.kdf.iterations, e.kdf.salt, e.cipher.name, e.cipher.iv, e.compression].join(\"\\n\"));\nconst canCompress = () => typeof CompressionStream !== \"undefined\";\n\n/** Compresses and encrypts a payload under a passphrase the person chose. */\nexport async function seal(payload: string, passphrase: string): Promise<string> {\n if (passphrase.length < 12) throw new Error(\"Use at least 12 characters for the backup passphrase\");\n const salt = crypto.getRandomValues(new Uint8Array(16)), iv = crypto.getRandomValues(new Uint8Array(12));\n const plain = new TextEncoder().encode(payload);\n const compression = canCompress() ? \"gzip\" : \"none\";\n const body = compression === \"gzip\" ? await through(plain, new CompressionStream(\"gzip\")) : plain;\n const clear = { format: \"ghostly-backup\", version: 1, kdf: { name: \"PBKDF2-SHA256\", iterations: ITERATIONS, salt: toBase64Url(salt) }, cipher: { name: \"AES-256-GCM\", iv: toBase64Url(iv) }, compression } as const;\n const ciphertext = new Uint8Array(await crypto.subtle.encrypt({ name: \"AES-GCM\", iv, additionalData: header(clear) as BufferSource }, await keyFrom(passphrase, salt, ITERATIONS), body as BufferSource));\n const envelope: Envelope = { ...clear, ciphertext: toBase64Url(ciphertext) };\n return JSON.stringify(envelope);\n}\n\n/** Checks, decrypts and decompresses a bundle. Nothing from a bundle is used before this succeeds. */\nexport async function open(text: string, passphrase: string): Promise<string> {\n if (text.length > MAX_BACKUP_BYTES * 1.4) throw new Error(\"This backup is too large to restore\");\n let envelope: Envelope;\n try { envelope = JSON.parse(text) as Envelope; } catch { throw new Error(\"This is not a Ghostly backup\"); }\n if (envelope?.format !== \"ghostly-backup\") throw new Error(\"This is not a Ghostly backup\");\n if (envelope.version !== 1) throw new Error(\"This backup comes from a newer Ghostly; update to restore it\");\n if (envelope.kdf?.name !== \"PBKDF2-SHA256\" || !(envelope.kdf.iterations >= ITERATIONS) || envelope.kdf.iterations > 10_000_000 || envelope.cipher?.name !== \"AES-256-GCM\" || ![\"gzip\", \"none\"].includes(envelope.compression)) throw new Error(\"Unsupported backup encryption\");\n let body: Uint8Array;\n try {\n body = new Uint8Array(await crypto.subtle.decrypt({ name: \"AES-GCM\", iv: fromBase64Url(envelope.cipher.iv) as BufferSource, additionalData: header(envelope) as BufferSource }, await keyFrom(passphrase, fromBase64Url(envelope.kdf.salt), envelope.kdf.iterations), fromBase64Url(envelope.ciphertext) as BufferSource));\n } catch { throw new Error(\"Wrong passphrase, or the backup was changed\"); }\n const plain = envelope.compression === \"gzip\" ? await through(body, new DecompressionStream(\"gzip\")) : body;\n return new TextDecoder().decode(plain);\n}\n","import { fromBase64Url, toBase64Url } from \"./codec\";\n\n/**\n * A backup written and read a piece at a time (WISP 05, envelope version 2), so a profile with years of files never\n * has to fit in memory. The file is one line of clear JSON (the header), then frames:\n *\n * header line: {\"format\":\"ghostly-backup\",\"version\":2,\"protection\":\"passphrase\"|\"none\",...}\\n\n * frame: kind (1 byte) | length (4 bytes, big endian) | body\n *\n * A frame carries one record: a piece of JSON (gzip or plain) or a piece of a file's bytes, at most `FRAME_BYTES`\n * of them. The last frame is the final marker; a file that ends without it was cut short and is refused.\n *\n * - `passphrase`: the key comes from the passphrase as in version 1 (PBKDF2-SHA256, 600 000 rounds, 16 byte salt).\n * Every frame is sealed alone with AES-256-GCM: its nonce is the header's 7 random bytes, the frame's number\n * (4 bytes) and its kind (1 byte), and the SHA-256 of the header line is its associated data. A frame changed,\n * moved, dropped or retyped, a changed header, or a missing final marker fails to open.\n * - `none`: nothing is encrypted. A SHA-256 chain runs over the header line and every frame, and the final marker\n * holds its last value, so a damaged or cut file is refused. It proves nothing about who made the file.\n */\nexport const FRAME_BYTES = 1024 * 1024;\n/** No frame is larger than this: a reader refuses one that says it is, before reading it. */\nconst MAX_FRAME_BYTES = 64 * 1024 * 1024;\n/** One piece of JSON, decompressed: past it the backup is refused, not read. */\nconst MAX_JSON_BYTES = 256 * 1024 * 1024;\nconst MAX_HEADER_BYTES = 4096;\nconst ITERATIONS = 600_000;\nconst TAG_BYTES = 16;\n\nconst KIND = { gzipJson: 1, json: 2, bytes: 3, final: 255 } as const;\n\n/**\n * The envelope version of a bundle that carries a device set (WISP 06 § Compatibility and rollout): an app from before\n * WISP 06 refuses it with \"This backup comes from a newer Ghostly\", rather than restoring a copy that stops nothing.\n * WISP 06 names it version 2, but WISP 05's streamed bundle took 2 first; every other bundle is still 2.\n */\nexport const DEVICE_SET_BACKUP_VERSION = 3;\n\nexport interface BackupHeader {\n format: \"ghostly-backup\";\n version: 2 | 3;\n protection: \"passphrase\" | \"none\";\n kdf?: { name: \"PBKDF2-SHA256\"; iterations: number; salt: string };\n cipher?: { name: \"AES-256-GCM\"; nonce: string };\n check?: { name: \"SHA-256-chain\" };\n}\n\n/** Where a backup is written: in order, a piece at a time. */\nexport interface BackupSink { write(bytes: Uint8Array): Promise<void> }\n/** Where a backup is read from: any range, so nothing is read before it is needed. */\nexport interface BackupSource { readonly size: number; read(offset: number, length: number): Promise<Uint8Array> }\n/** One record of a backup: JSON text, or a piece of the bytes of the file the JSON before it announced. */\nexport type BackupRecord = { json: string; bytes?: undefined } | { bytes: Uint8Array; json?: undefined };\n\n/** Thrown when the work was cancelled (`AbortSignal`): nothing is wrong with the backup. */\nexport class BackupCancelled extends Error {\n constructor() { super(\"Cancelled\"); this.name = \"AbortError\"; }\n}\nexport const isCancelled = (error: unknown) => (error as { name?: string })?.name === \"AbortError\";\nconst check = (signal?: AbortSignal) => { if (signal?.aborted) throw new BackupCancelled(); };\n\n/** A Blob (a picked file) as a source, read in slices: never `stream()`, which WebKit fails on for some stored Blobs. */\nexport const blobSource = (blob: Blob): BackupSource => ({ size: blob.size, read: async (offset, length) => new Uint8Array(await blob.slice(offset, offset + length).arrayBuffer()) });\nexport const bytesSource = (bytes: Uint8Array): BackupSource => ({ size: bytes.length, read: async (offset, length) => bytes.subarray(offset, offset + length) });\n/** A sink that keeps what is written (tests, and a backup small enough to hand over whole). */\nexport function memorySink(): BackupSink & { bytes(): Uint8Array } {\n const parts: Uint8Array[] = [];\n return {\n write: async (bytes) => { parts.push(bytes.slice()); },\n bytes: () => { const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0)); let at = 0; for (const p of parts) { out.set(p, at); at += p.length; } return out; },\n };\n}\n\nconst canCompress = () => typeof CompressionStream !== \"undefined\";\nasync function through(bytes: Uint8Array, stream: CompressionStream | DecompressionStream, limit = Infinity): Promise<Uint8Array> {\n const reader = new Blob([bytes as BlobPart]).stream().pipeThrough(stream).getReader();\n const chunks: Uint8Array[] = [];\n let size = 0;\n for (let next = await reader.read(); !next.done; next = await reader.read()) {\n size += next.value.length;\n if (size > limit) { await reader.cancel(); throw new Error(\"This backup is too large to restore\"); }\n chunks.push(next.value);\n }\n if (chunks.length === 1) return chunks[0];\n const out = new Uint8Array(size);\n let at = 0;\n for (const chunk of chunks) { out.set(chunk, at); at += chunk.length; }\n return out;\n}\n\nasync function keyFrom(passphrase: string, salt: Uint8Array, iterations: number): Promise<CryptoKey> {\n const material = await crypto.subtle.importKey(\"raw\", new TextEncoder().encode(passphrase), \"PBKDF2\", false, [\"deriveKey\"]);\n return crypto.subtle.deriveKey({ name: \"PBKDF2\", hash: \"SHA-256\", salt: salt as BufferSource, iterations }, material, { name: \"AES-GCM\", length: 256 }, false, [\"encrypt\", \"decrypt\"]);\n}\nconst sha256 = async (...parts: Uint8Array[]) => new Uint8Array(await crypto.subtle.digest(\"SHA-256\", join(parts) as BufferSource));\nfunction join(parts: Uint8Array[]): Uint8Array {\n if (parts.length === 1) return parts[0];\n const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));\n let at = 0;\n for (const part of parts) { out.set(part, at); at += part.length; }\n return out;\n}\nfunction prefix(kind: number, length: number): Uint8Array {\n const out = new Uint8Array(5);\n out[0] = kind;\n new DataView(out.buffer).setUint32(1, length);\n return out;\n}\nfunction nonce(base: Uint8Array, frame: number, kind: number): Uint8Array {\n const out = new Uint8Array(12);\n out.set(base, 0);\n new DataView(out.buffer).setUint32(7, frame);\n out[11] = kind;\n return out;\n}\n\n/** Writes a backup: `json` and `bytes` in the order they are to be read, then `finish`. */\nexport class BackupWriter {\n private frame = 0;\n /** Bytes handed to the sink so far. */\n written = 0;\n private finished = false;\n /** Set once the sink refused a frame: the error it gave, thrown again at every later write. */\n private failed: { error: unknown } | null = null;\n\n private constructor(private readonly sink: BackupSink, private readonly key: CryptoKey | null, private readonly base: Uint8Array, private chain: Uint8Array, private readonly signal?: AbortSignal) {}\n\n /** `passphrase` null: a backup anyone can read (the caller has made sure that is what the person chose). */\n static async start(sink: BackupSink, passphrase: string | null, signal?: AbortSignal, version: 2 | 3 = 2): Promise<BackupWriter> {\n if (passphrase !== null && passphrase.length < 12) throw new Error(\"Use at least 12 characters for the backup passphrase\");\n check(signal);\n const salt = crypto.getRandomValues(new Uint8Array(16)), base = crypto.getRandomValues(new Uint8Array(7));\n const header: BackupHeader = passphrase === null\n ? { format: \"ghostly-backup\", version, protection: \"none\", check: { name: \"SHA-256-chain\" } }\n : { format: \"ghostly-backup\", version, protection: \"passphrase\", kdf: { name: \"PBKDF2-SHA256\", iterations: ITERATIONS, salt: toBase64Url(salt) }, cipher: { name: \"AES-256-GCM\", nonce: toBase64Url(base) } };\n const line = new TextEncoder().encode(`${JSON.stringify(header)}\\n`);\n const key = passphrase === null ? null : await keyFrom(passphrase, salt, ITERATIONS);\n const writer = new BackupWriter(sink, key, base, await sha256(line), signal);\n await sink.write(line);\n writer.written = line.length;\n return writer;\n }\n\n private async put(kind: number, plain: Uint8Array): Promise<void> {\n check(this.signal);\n if (this.failed) throw this.failed.error;\n if (this.finished) throw new Error(\"This backup is already finished\");\n if (this.frame >= 0xffffffff) throw new Error(\"This backup is too large\");\n // What a reader refuses is not written: found out now, not when the backup is needed.\n if (plain.length > MAX_FRAME_BYTES) throw new Error(\"A record of this profile is too large for a backup\");\n let out: Uint8Array;\n if (this.key) {\n // The header's digest is every frame's associated data: a frame of another backup, or under a changed header, fails.\n const sealed = new Uint8Array(await crypto.subtle.encrypt({ name: \"AES-GCM\", iv: nonce(this.base, this.frame, kind) as BufferSource, additionalData: this.chain as BufferSource }, this.key, plain as BufferSource));\n out = join([prefix(kind, sealed.length), sealed]);\n } else {\n const head = prefix(kind, kind === KIND.final ? 32 : plain.length);\n if (kind === KIND.final) out = join([head, this.chain]);\n else { out = join([head, plain]); this.chain = await sha256(this.chain, out); }\n }\n this.frame += 1;\n // A frame the sink did not take (no room left, say) is the end of this backup: every frame after it would be\n // numbered past a gap, and the file would never open. Nothing more is written, whatever the caller does next.\n try { await this.sink.write(out); } catch (error) { this.failed = { error }; throw error; }\n this.written += out.length;\n }\n\n /** One piece of JSON, compressed where the platform can. */\n async json(text: string): Promise<void> {\n const plain = new TextEncoder().encode(text);\n if (plain.length > MAX_JSON_BYTES) throw new Error(\"A record of this profile is too large for a backup\");\n if (canCompress()) await this.put(KIND.gzipJson, await through(plain, new CompressionStream(\"gzip\")));\n else await this.put(KIND.json, plain);\n }\n\n /** Bytes of the file the last JSON announced, cut into frames. */\n async bytes(chunk: Uint8Array): Promise<void> {\n for (let at = 0; at < chunk.length; at += FRAME_BYTES) await this.put(KIND.bytes, chunk.subarray(at, Math.min(chunk.length, at + FRAME_BYTES)));\n }\n\n /** The final marker: without it a reader refuses the backup. Resolves to the size of the whole backup. */\n async finish(): Promise<number> {\n await this.put(KIND.final, new Uint8Array());\n this.finished = true;\n return this.written;\n }\n}\n\n/** The first line of a version 2 backup, or null when this is not one (version 1 is one JSON object with no line end). */\nexport async function readBackupHeader(source: BackupSource): Promise<{ header: BackupHeader; line: Uint8Array } | null> {\n const start = await source.read(0, Math.min(MAX_HEADER_BYTES, source.size));\n const end = start.indexOf(10);\n if (end < 0) return null;\n let header: BackupHeader;\n try { header = JSON.parse(new TextDecoder().decode(start.subarray(0, end))) as BackupHeader; } catch { return null; }\n if (header?.format !== \"ghostly-backup\" || typeof header.version !== \"number\" || header.version < 2) return null;\n if (header.version !== 2 && header.version !== DEVICE_SET_BACKUP_VERSION) throw new Error(\"This backup comes from a newer Ghostly; update to restore it\");\n if (header.protection === \"none\") {\n if (header.check?.name !== \"SHA-256-chain\") throw new Error(\"Unsupported backup format\");\n } else if (header.protection === \"passphrase\") {\n if (header.kdf?.name !== \"PBKDF2-SHA256\" || !(header.kdf.iterations >= ITERATIONS) || header.kdf.iterations > 10_000_000 || header.cipher?.name !== \"AES-256-GCM\") throw new Error(\"Unsupported backup encryption\");\n } else throw new Error(\"Unsupported backup format\");\n return { header, line: start.slice(0, end + 1) };\n}\n\n/** How a backup is protected, read from its clear header without opening it: version 1 is always sealed. */\nexport async function backupProtection(source: BackupSource): Promise<\"passphrase\" | \"none\"> {\n return (await readBackupHeader(source))?.header.protection ?? \"passphrase\";\n}\n\nconst DAMAGED = \"This backup is damaged: it was changed or cut short\";\n\n/** Reads a version 2 backup record by record. Nothing of a frame is handed out before it passed its check. */\nexport class BackupReader {\n private frame = 0;\n private at: number;\n private block: Uint8Array = new Uint8Array();\n private blockAt = 0;\n private done = false;\n\n private constructor(private readonly source: BackupSource, private readonly key: CryptoKey | null, private readonly base: Uint8Array, private chain: Uint8Array, start: number, private readonly signal?: AbortSignal) { this.at = start; }\n\n get protection(): \"passphrase\" | \"none\" { return this.key ? \"passphrase\" : \"none\"; }\n\n /** Null when `source` is not a version 2 backup. `passphrase` is needed, and used, only for a sealed one. */\n static async open(source: BackupSource, passphrase?: string, signal?: AbortSignal): Promise<BackupReader | null> {\n const found = await readBackupHeader(source);\n if (!found) return null;\n check(signal);\n const { header, line } = found;\n if (header.protection === \"none\") return new BackupReader(source, null, new Uint8Array(), await sha256(line), line.length, signal);\n let key: CryptoKey, base: Uint8Array;\n try {\n base = fromBase64Url(header.cipher!.nonce);\n if (base.length !== 7) throw new Error(\"nonce\");\n key = await keyFrom(passphrase ?? \"\", fromBase64Url(header.kdf!.salt), header.kdf!.iterations);\n } catch { throw new Error(\"Unsupported backup encryption\"); }\n return new BackupReader(source, key, base, await sha256(line), line.length, signal);\n }\n\n /** Bytes of the source read so far (for progress). */\n get position(): number { return this.at; }\n\n /** `length` bytes at the reading point, through a read-ahead block so a source is asked a few times per megabyte, not per frame. */\n private async take(length: number): Promise<Uint8Array> {\n if (this.at + length > this.source.size) throw new Error(DAMAGED);\n const from = this.at - this.blockAt;\n if (from < 0 || from + length > this.block.length) {\n this.block = await this.source.read(this.at, Math.min(this.source.size - this.at, Math.max(length, 4 * FRAME_BYTES)));\n this.blockAt = this.at;\n if (this.block.length < length) throw new Error(DAMAGED);\n }\n const out = this.block.subarray(this.at - this.blockAt, this.at - this.blockAt + length);\n this.at += length;\n return out;\n }\n\n /** The next record, or null at the end: only then is the backup known to be whole. */\n async next(): Promise<BackupRecord | null> {\n check(this.signal);\n if (this.done) return null;\n const head = await this.take(5);\n const kind = head[0], length = new DataView(head.buffer, head.byteOffset, 5).getUint32(1);\n if (length > MAX_FRAME_BYTES + TAG_BYTES || !Object.values(KIND).includes(kind as never)) throw new Error(DAMAGED);\n const headCopy = head.slice();\n const body = await this.take(length);\n let plain: Uint8Array;\n if (this.key) {\n try {\n plain = new Uint8Array(await crypto.subtle.decrypt({ name: \"AES-GCM\", iv: nonce(this.base, this.frame, kind) as BufferSource, additionalData: this.chain as BufferSource }, this.key, body as BufferSource));\n } catch { throw new Error(this.frame === 0 ? \"Wrong passphrase, or the backup was changed\" : DAMAGED); }\n } else if (kind === KIND.final) {\n if (body.length !== 32 || body.some((byte, i) => byte !== this.chain[i])) throw new Error(DAMAGED);\n plain = new Uint8Array();\n } else {\n this.chain = await sha256(this.chain, headCopy, body);\n plain = body.slice();\n }\n this.frame += 1;\n if (kind === KIND.final) {\n if (plain.length || this.at !== this.source.size) throw new Error(DAMAGED);\n this.done = true;\n return null;\n }\n if (kind === KIND.bytes) return { bytes: plain };\n const text = kind === KIND.gzipJson ? await through(plain, new DecompressionStream(\"gzip\"), MAX_JSON_BYTES).catch((error: unknown) => { throw error instanceof Error && error.message.startsWith(\"This backup is too large\") ? error : new Error(DAMAGED); }) : plain;\n return { json: new TextDecoder().decode(text) };\n }\n}\n","/**\n * A light backup (WISP 05 § Light backups): everything of a profile but the bytes of its larger files. Their messages\n * and records stay; after a restore they show as not in this backup.\n *\n * The cut: a file of at most 1 MiB keeps its bytes (a short clip, a sticker, a screenshot, a document), and so does a\n * voice message of at most 4 MiB. Voice messages are part of the conversation, and the longest one the apps record\n * (15 minutes at 32 kbit/s, `VOICE_LIMITS`) is about 3.6 MB, so every recorded one stays. Photos from a camera\n * (usually 2 to 8 MB), videos and large files are what is left out.\n */\nexport const LIGHT_FILE_BYTES = 1024 * 1024;\nexport const LIGHT_VOICE_BYTES = 4 * 1024 * 1024;\n\n/** What a light bundle says of itself in its first record: its cut, and what it left out. */\nexport interface LightMark {\n maxFileBytes: number;\n maxVoiceBytes: number;\n /** How many files were left out, and their bytes. */\n files: number;\n bytes: number;\n}\n\n/** Whether a file of this size keeps its bytes in a light backup. */\nexport const keptInLight = (size: number, voice: boolean): boolean => size <= (voice ? LIGHT_VOICE_BYTES : LIGHT_FILE_BYTES);\n","import { closeSync, existsSync, mkdirSync, openSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync, writeSync } from \"node:fs\";\nimport { createHash } from \"node:crypto\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { CliError } from \"./errors\";\n\n/**\n * Profiles on disk (WISP 1100 § Profiles and files): `<home>/profiles/<name>/`, 0700, one engine store each.\n * `<home>/current` names the profile commands use when none is given.\n */\nexport const DEFAULT_PROFILE = \"default\";\nconst NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/i;\n\n/**\n * An empty `--home` or `--profile` (an unset variable in a script) is a usage error: taken as not given, it ran the\n * command in the current folder, or as the current profile. An empty variable of the environment counts as unset.\n */\nexport function ghostlyHome(option?: string): string {\n if (option === \"\") throw new CliError(\"usage\", \"--home needs a folder\");\n return resolve(option ?? (process.env.GHOSTLY_HOME || join(homedir(), \".ghostly\")));\n}\n\nexport function checkProfileName(name: string): string {\n if (!NAME.test(name) || name.includes(\"..\")) throw new CliError(\"usage\", `Not a profile name: ${JSON.stringify(name)} (letters, digits, \".\", \"_\" and \"-\", up to 64)`);\n return name;\n}\n\nexport function currentProfile(home: string, option?: string): string {\n if (option === \"\") throw new CliError(\"usage\", \"--profile needs a profile name\");\n if (option !== undefined) return checkProfileName(option);\n if (process.env.GHOSTLY_PROFILE) return checkProfileName(process.env.GHOSTLY_PROFILE);\n const file = join(home, \"current\");\n if (existsSync(file)) {\n const name = readFileSync(file, \"utf8\").trim();\n if (name) return checkProfileName(name);\n }\n return DEFAULT_PROFILE;\n}\n\nexport function profileDir(home: string, name: string): string {\n return join(home, \"profiles\", checkProfileName(name));\n}\n\nexport interface ProfilePaths { name: string; dir: string; db: string; socket: string; lock: string; events: string; files: string; log: string }\nexport function profilePaths(home: string, name: string): ProfilePaths {\n const dir = profileDir(home, name);\n return {\n name, dir, db: join(dir, \"db\"), socket: socketPath(dir), lock: join(dir, \"daemon.lock\"),\n events: join(dir, \"events.jsonl\"), files: join(dir, \"files\"), log: join(dir, \"daemon.log\"),\n };\n}\n\n/**\n * The daemon's socket: in the profile's folder, unless that path is longer than a Unix socket may be (104 bytes on\n * macOS); then in a folder of /tmp named after the profile's, which the daemon makes owner-only and checks before\n * use (`privateFolder`), and whose socket a client connects to only when it is this user's (`ownSocket`).\n */\nfunction socketPath(dir: string): string {\n const inside = join(dir, \"daemon.sock\");\n if (process.platform === \"win32\") return `\\\\\\\\.\\\\pipe\\\\ghostly-${createHash(\"sha256\").update(dir).digest(\"hex\").slice(0, 24)}`;\n if (Buffer.byteLength(inside) <= 100) return inside;\n return join(\"/tmp\", `ghostly-${createHash(\"sha256\").update(dir).digest(\"hex\").slice(0, 24)}`, \"daemon.sock\");\n}\n\nfunction ensureFolder(path: string): void {\n mkdirSync(path, { recursive: true, mode: 0o700 });\n}\n\n/** Makes the profile's folder; refused when it exists and `existing` is not allowed. */\nexport function createProfile(home: string, name: string, { existing = false } = {}): ProfilePaths {\n const paths = profilePaths(home, name);\n if (existsSync(paths.dir) && !existing) throw new CliError(\"refused\", `Profile ${name} exists`);\n ensureFolder(join(home, \"profiles\"));\n ensureFolder(paths.dir);\n return paths;\n}\n\n/**\n * A file in the folder of a profile a restore made, until its engine starts for the first time: the store came back\n * whole, as the backup held it, and the money in it is then treated as every restore treats it (WISP 05 § Restoring).\n */\nexport const RESTORED_MARK = \"restored\";\n\nexport function profileExists(home: string, name: string): boolean {\n return existsSync(profileDir(home, name));\n}\n\nexport function listProfiles(home: string): string[] {\n const root = join(home, \"profiles\");\n if (!existsSync(root)) return [];\n return readdirSync(root).filter((name) => NAME.test(name) && statSync(join(root, name)).isDirectory()).sort();\n}\n\nexport function selectProfile(home: string, name: string): void {\n if (!profileExists(home, name)) throw new CliError(\"not_found\", `No profile ${name}`);\n ensureFolder(home);\n writeFileSync(join(home, \"current\"), name + \"\\n\", { mode: 0o600 });\n}\n\n/**\n * One process per profile: the daemon or a one-shot command. The lock holds the owner's pid; a lock whose\n * process is gone is taken over.\n */\nexport function acquireLock(paths: ProfilePaths): () => void {\n for (let attempt = 0; attempt < 2; attempt++) {\n try {\n const fd = openSync(paths.lock, \"wx\", 0o600);\n writeSync(fd, String(process.pid));\n closeSync(fd);\n let released = false;\n return () => {\n if (released) return;\n released = true;\n try { if (readFileSync(paths.lock, \"utf8\").trim() === String(process.pid)) rmSync(paths.lock, { force: true }); } catch { /* gone already */ }\n };\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n const owner = lockOwner(paths);\n if (owner !== null) throw new CliError(\"busy\", `Profile ${paths.name} is in use by process ${owner}`, { pid: owner });\n rmSync(paths.lock, { force: true });\n }\n }\n throw new CliError(\"busy\", `Profile ${paths.name} is in use`);\n}\n\n/** The pid holding the profile, or null when nobody (or a process that is gone) does. */\nexport function lockOwner(paths: ProfilePaths): number | null {\n let pid: number;\n try { pid = Number(readFileSync(paths.lock, \"utf8\").trim()); } catch { return null; }\n if (!Number.isInteger(pid) || pid <= 0) return null;\n try { process.kill(pid, 0); return pid; } catch (error) { return (error as NodeJS.ErrnoException).code === \"EPERM\" ? pid : null; }\n}\n","import { existsSync } from \"node:fs\";\nimport { link, mkdir, open, readdir, readFile, rename, rm, stat, writeFile, type FileHandle } from \"node:fs/promises\";\nimport { basename, dirname, join, relative, resolve, sep } from \"node:path\";\nimport { open as openEnvelope } from \"@ghostly/browser/backup/envelope\";\nimport { BackupReader, BackupWriter, FRAME_BYTES, readBackupHeader, type BackupSource } from \"@ghostly/browser/backup/stream\";\nimport { LIGHT_FILE_BYTES, LIGHT_VOICE_BYTES, keptInLight, type LightMark } from \"@ghostly/browser/backup/light\";\nimport { fileStore } from \"@ghostly/browser/shared/idb\";\nimport { bool, str, type Method } from \"./apiKit\";\nimport { CliError } from \"./errors\";\nimport { RESTORED_MARK, profileExists, profilePaths, type ProfilePaths } from \"./profiles\";\n\n/**\n * A headless profile's backup: the WISP 05 envelope around the profile itself, its store and its files, written and\n * read a piece at a time (envelope version 2), so a profile with large files never has to fit in memory. It restores\n * into a new headless profile; the app's own backups are made from its page's storage and are not this format\n * (WISP 1100 § Parity). A backup an older CLI made (version 1: everything in one sealed JSON document) still restores.\n */\nconst FORMAT = \"ghostly-cli-profile/2\";\nconst FORMAT_1 = \"ghostly-cli-profile/1\";\n\n/** Version 1: the store and every file as base64 in one document. */\ninterface Payload1 { format: typeof FORMAT_1; createdAt: number; store: string; files: { path: string; data: string }[] }\n/** Version 2, in order: the head, then each file (the store first) followed by its bytes, then the end. */\n/**\n * `light`: a light backup (WISP 05 § Light backups) left the bytes of these files out. Their records are in the store;\n * the restored profile marks them when it first starts (`markLeftOutFiles`).\n */\ntype BackupRecord =\n | { t: \"profile\"; format: typeof FORMAT; createdAt: number; files: number; bytes: number; light?: LightMark & { ids: string[] } }\n | { t: \"file\"; kind: \"store\" | \"file\"; path?: string; size: number }\n | { t: \"end\"; files: number; bytes: number };\n\nasync function walk(root: string, dir = root, out: string[] = []): Promise<string[]> {\n let names: string[];\n try { names = await readdir(dir); } catch { return out; }\n for (const name of names) {\n const path = join(dir, name);\n if ((await stat(path)).isDirectory()) await walk(root, path, out);\n else out.push(relative(root, path));\n }\n return out;\n}\n\n/** A file's first `size` bytes into the backup, a frame at a time. One that got shorter meanwhile stops the backup. */\nasync function copyIn(writer: BackupWriter, path: string, size: number): Promise<void> {\n const handle = await open(path, \"r\");\n try {\n const buffer = new Uint8Array(FRAME_BYTES);\n for (let at = 0; at < size;) {\n const { bytesRead } = await handle.read(buffer, 0, Math.min(FRAME_BYTES, size - at), at);\n if (!bytesRead) throw new CliError(\"unavailable\", `${path} changed while it was backed up: try again`);\n await writer.bytes(buffer.subarray(0, bytesRead));\n at += bytesRead;\n }\n } finally { await handle.close(); }\n}\n\nexport const BACKUP_METHODS: Record<string, Method> = {\n /**\n * Writes the backup to `path` (resolved by the command), sealed with `passphrase`, or not encrypted when\n * `noPassphrase` is true: one of the two, never a guess. The store is folded first, so the copy is whole. It is\n * written beside `path` and only takes its name once it is complete: a backup that fails leaves no file. `light`:\n * the files over the cut (`keptInLight`) are left out, their records kept.\n */\n async \"profile.backup\"(ctx, params) {\n const path = resolve(str(params, \"path\", true));\n if (existsSync(path)) throw new CliError(\"confirm\", `${path} exists: choose another name`);\n const passphrase = str(params, \"passphrase\"), unsealed = bool(params, \"noPassphrase\");\n if (!passphrase && !unsealed) throw new CliError(\"bad_request\", \"passphrase is required (or noPassphrase: true for a backup anyone can read)\");\n if (passphrase && unsealed) throw new CliError(\"bad_request\", \"Give a passphrase or noPassphrase, not both\");\n await ctx.runtime.store.compact();\n const paths = ctx.runtime.paths;\n const store = join(paths.db, \"snapshot.bin\");\n const light = bool(params, \"light\");\n const all = await Promise.all((await walk(paths.files)).map(async (name) => ({ name, size: (await stat(join(paths.files, name))).size })));\n // A file's name is its id: only one that could be a voice message under its larger cut is looked up.\n const kept = async (file: { name: string; size: number }) => keptInLight(file.size, false) || (file.size <= LIGHT_VOICE_BYTES && !!(await fileStore.get(basename(file.name)).catch(() => undefined))?.metadata?.voice);\n const files: typeof all = [], leftOut: typeof all = [];\n for (const file of all) (!light || (await kept(file)) ? files : leftOut).push(file);\n const storeSize = (await stat(store)).size, bytes = files.reduce((sum, file) => sum + file.size, 0);\n const leftOutBytes = leftOut.reduce((sum, file) => sum + file.size, 0);\n\n const partial = `${path}.partial-${process.pid}`;\n let out: FileHandle | null = null;\n try {\n out = await openFile(partial);\n const handle = out;\n let writer: BackupWriter;\n try { writer = await BackupWriter.start({ write: async (chunk) => { await handle.write(chunk); } }, unsealed ? null : passphrase!); } catch (error) { throw new CliError(\"bad_request\", error instanceof Error ? error.message : String(error)); }\n const put = (record: BackupRecord) => writer.json(JSON.stringify(record));\n await put({ t: \"profile\", format: FORMAT, createdAt: Date.now(), files: files.length, bytes, ...(light && { light: { maxFileBytes: LIGHT_FILE_BYTES, maxVoiceBytes: LIGHT_VOICE_BYTES, files: leftOut.length, bytes: leftOutBytes, ids: leftOut.map((file) => basename(file.name)) } }) });\n await put({ t: \"file\", kind: \"store\", size: storeSize });\n await copyIn(writer, store, storeSize);\n for (const file of files) {\n await put({ t: \"file\", kind: \"file\", path: file.name.split(sep).join(\"/\"), size: file.size });\n await copyIn(writer, join(paths.files, file.name), file.size);\n }\n await put({ t: \"end\", files: files.length, bytes });\n const written = await writer.finish();\n await handle.sync();\n await handle.close();\n out = null;\n // Its name only now, and never over a file that appeared meanwhile.\n try { await link(partial, path); } catch (error) { throw (error as NodeJS.ErrnoException).code === \"EEXIST\" ? new CliError(\"confirm\", `${path} exists: choose another name`) : error; }\n return { path, bytes: written, files: files.length, protection: unsealed ? \"none\" : \"passphrase\", ...(light && { light: true, leftOut: leftOut.length, leftOutBytes }) };\n } finally {\n await out?.close().catch(() => {});\n await rm(partial, { force: true });\n }\n },\n};\n\nasync function openFile(path: string): Promise<FileHandle> {\n try { return await open(path, \"wx\", 0o600); } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"EEXIST\") { await rm(path, { force: true }); return open(path, \"wx\", 0o600); }\n throw error;\n }\n}\n\n/** A file on disk as a backup's source: read in ranges, never whole. */\nasync function fileSource(path: string): Promise<BackupSource & { close(): Promise<void> }> {\n const handle = await open(path, \"r\");\n const { size } = await handle.stat();\n return {\n size,\n read: async (offset, length) => {\n const buffer = new Uint8Array(Math.max(0, Math.min(length, size - offset)));\n let got = 0;\n while (got < buffer.length) { const { bytesRead } = await handle.read(buffer, got, buffer.length - got, offset + got); if (!bytesRead) break; got += bytesRead; }\n return buffer.subarray(0, got);\n },\n close: () => handle.close(),\n };\n}\n\n/** A version 1 backup is one JSON object, and `seal` writes its format first. */\nconst VERSION_1_START = /^\\s*\\{\\s*\"format\"\\s*:\\s*\"ghostly-backup\"/;\n\n/**\n * Whether the backup at `file` is sealed with a passphrase or was made without one, read from its clear header.\n * A file that is neither version is refused here, before any passphrase is asked for: the path is what is wrong.\n */\nexport async function backupFileProtection(file: string): Promise<\"passphrase\" | \"none\"> {\n const source = await fileSource(file);\n try {\n let found: Awaited<ReturnType<typeof readBackupHeader>>;\n try { found = await readBackupHeader(source); } catch (error) { throw new CliError(\"refused\", error instanceof Error ? error.message : String(error)); }\n if (found) return found.header.protection;\n if (!VERSION_1_START.test(new TextDecoder().decode(await source.read(0, 256)))) throw new CliError(\"refused\", \"This is not a Ghostly backup\");\n return \"passphrase\";\n } finally { await source.close(); }\n}\n\n/** Where a path named by a backup lands inside the new profile's files folder; never outside it. */\nfunction inside(root: string, path: unknown): string {\n const target = typeof path === \"string\" && path ? resolve(root, path) : \"\";\n if (!target.startsWith(resolve(root) + sep)) throw new CliError(\"refused\", \"The backup names a file outside its folder\");\n return target;\n}\n\n/** While a restore writes it, the profile's folder is not a profile: no profile name starts with a dot (`listProfiles`). */\nconst RESTORING = /^\\.restoring-(.+)-(\\d+)$/;\n\n/**\n * The folder a restore writes into, beside the profiles: it takes the profile's name only once everything is in it,\n * so a restore that is stopped at any moment (Ctrl-C, a kill, the power) leaves no profile, whole or half-made.\n * What an earlier restore that was stopped left behind is removed here, once the process that made it is gone.\n */\nasync function restoringFolder(home: string, name: string): Promise<ProfilePaths> {\n const root = join(home, \"profiles\");\n await mkdir(root, { recursive: true, mode: 0o700 });\n for (const entry of await readdir(root)) {\n const pid = Number(RESTORING.exec(entry)?.[2]);\n if (!pid) continue;\n let alive = pid === process.pid;\n if (!alive) try { process.kill(pid, 0); alive = true; } catch (error) { alive = (error as NodeJS.ErrnoException).code === \"EPERM\"; }\n if (!alive) await rm(join(root, entry), { recursive: true, force: true });\n }\n const dir = join(root, `.restoring-${name}-${process.pid}`);\n await rm(dir, { recursive: true, force: true });\n await mkdir(dir, { mode: 0o700 });\n return { ...profilePaths(home, name), dir, db: join(dir, \"db\"), files: join(dir, \"files\") };\n}\n\n/**\n * The folder is whole: marked as a restored copy (`RESTORED_MARK`, which names the files a light backup left out),\n * then given the profile's name. Never over a profile.\n */\nasync function finishRestore(home: string, name: string, work: ProfilePaths, leftOut: string[] = []): Promise<ProfilePaths> {\n const paths = profilePaths(home, name);\n await writeFile(join(work.dir, RESTORED_MARK), leftOut.length ? JSON.stringify({ leftOut }) : \"\", { mode: 0o600 });\n if (existsSync(paths.dir)) throw new CliError(\"refused\", `Profile ${name} exists: restore into a new name`);\n await rename(work.dir, paths.dir);\n return paths;\n}\n\n/**\n * Opens the backup at `file` into a new profile (never over an existing one), with its passphrase when it has one.\n * Runs with no engine: nothing is open yet. A backup that is refused, damaged or cut short leaves no profile behind,\n * and neither does a restore that is stopped while it runs (`restoringFolder`). The profile is marked as a restored\n * copy: its engine, the first time it starts, treats the money in it as a restore does (`RESTORED_MARK`).\n */\nexport async function restoreProfile(home: string, name: string, file: string, passphrase?: string): Promise<ProfilePaths> {\n if (profileExists(home, name)) throw new CliError(\"refused\", `Profile ${name} exists: restore into a new name`);\n const source = await fileSource(file);\n let work: ProfilePaths | null = null;\n try {\n let reader: BackupReader | null;\n try { reader = await BackupReader.open(source, passphrase); } catch (error) { throw new CliError(\"refused\", error instanceof Error ? error.message : String(error)); }\n if (!reader) {\n const payload = await readWhole(file, passphrase ?? \"\");\n work = await restoringFolder(home, name);\n await writeWhole(work, payload);\n return await finishRestore(home, name, work);\n }\n const next = async () => { try { return await reader.next(); } catch (error) { throw new CliError(\"refused\", error instanceof Error ? error.message : String(error)); } };\n const parse = (json: string) => { try { return JSON.parse(json) as BackupRecord; } catch { throw new CliError(\"refused\", \"This backup is damaged: it was changed or cut short\"); } };\n\n // The first record is read (and with it the passphrase checked) before any folder is made.\n const first = await next();\n const head = first?.json === undefined ? null : parse(first.json);\n if (head?.t !== \"profile\" || head.format !== FORMAT) throw new CliError(\"refused\", \"This backup is not a headless Ghostly profile\");\n const leftOut = Array.isArray(head.light?.ids) ? head.light.ids.filter((id): id is string => typeof id === \"string\") : [];\n work = await restoringFolder(home, name);\n const paths = work;\n await mkdir(paths.db, { recursive: true, mode: 0o700 });\n let out: { handle: FileHandle; left: number } | null = null, files = 0, bytes = 0, ended = false, stored = false;\n try {\n for (let record = await next(); record; record = await next()) {\n if (ended) throw new CliError(\"refused\", \"This backup is damaged: it was changed or cut short\");\n if (record.bytes) {\n if (!out || record.bytes.length > out.left) throw new CliError(\"refused\", \"This backup is damaged: it was changed or cut short\");\n await out.handle.write(record.bytes);\n out.left -= record.bytes.length;\n continue;\n }\n if (out) { if (out.left) throw new CliError(\"refused\", \"This backup is damaged: it was changed or cut short\"); await out.handle.close(); out = null; }\n const value = parse(record.json);\n if (value.t === \"end\") { ended = value.files === files && value.bytes === bytes; if (!ended) throw new CliError(\"refused\", \"This backup is damaged: it was changed or cut short\"); continue; }\n if (value.t !== \"file\" || !Number.isSafeInteger(value.size) || value.size < 0) continue;\n let target: string;\n if (value.kind === \"store\") { target = join(paths.db, \"snapshot.bin\"); stored = true; }\n else { target = inside(paths.files, value.path); await mkdir(dirname(target), { recursive: true, mode: 0o700 }); files += 1; bytes += value.size; }\n out = { handle: await open(target, \"wx\", 0o600), left: value.size };\n }\n if (out || !ended || !stored) throw new CliError(\"refused\", \"This backup is damaged: it was changed or cut short\");\n } finally { await out?.handle.close().catch(() => {}); }\n return await finishRestore(home, name, work, leftOut);\n } catch (error) {\n if (work) await rm(work.dir, { recursive: true, force: true });\n throw error;\n } finally {\n await source.close();\n }\n}\n\n/** A version 1 backup: one sealed JSON document, read whole. */\nasync function readWhole(file: string, passphrase: string): Promise<Payload1> {\n let payload: Payload1;\n try { payload = JSON.parse(await openEnvelope(await readFile(file, \"utf8\"), passphrase)) as Payload1; } catch (error) { throw new CliError(\"refused\", error instanceof Error ? error.message : String(error)); }\n if (payload?.format !== FORMAT_1 || typeof payload.store !== \"string\" || !Array.isArray(payload.files)) throw new CliError(\"refused\", \"This backup is not a headless Ghostly profile\");\n return payload;\n}\n\nasync function writeWhole(paths: ProfilePaths, payload: Payload1): Promise<void> {\n await mkdir(paths.db, { recursive: true, mode: 0o700 });\n await writeFile(join(paths.db, \"snapshot.bin\"), Buffer.from(payload.store, \"base64\"), { mode: 0o600 });\n for (const entry of payload.files) {\n const target = inside(paths.files, entry.path);\n await mkdir(dirname(target), { recursive: true, mode: 0o700 });\n await writeFile(target, Buffer.from(entry.data, \"base64\"), { mode: 0o600 });\n }\n}\n","import { bool, list, str, type Method } from \"../apiKit\";\n\n/** Voice calls (WISP 1100 § Calls): the API methods. The audio itself goes over each call's own socket. */\nexport const CALL_METHODS: Record<string, Method> = {\n async \"call.start\"(ctx, params) {\n return ctx.calls.start(str(params, \"chat\", true), { rate: params.rate });\n },\n async \"call.answer\"(ctx, params) {\n return ctx.calls.answer(str(params, \"call\"), { rate: params.rate });\n },\n async \"call.hangup\"(ctx, params) {\n return ctx.calls.hangup(str(params, \"call\"));\n },\n async \"call.list\"(ctx) {\n return { calls: ctx.calls.list(), autoAnswer: ctx.calls.getAuto(), available: ctx.runtime.callsUnavailable === null, ...(ctx.runtime.callsUnavailable ? { unavailable: ctx.runtime.callsUnavailable } : {}) };\n },\n async \"call.get\"(ctx, params) {\n return ctx.calls.get(str(params, \"call\"));\n },\n async \"call.flush\"(ctx, params) {\n return ctx.calls.flush(str(params, \"call\"));\n },\n async \"call.auto\"(ctx, params) {\n if (params.on === undefined) return { autoAnswer: ctx.calls.getAuto() };\n return { autoAnswer: ctx.calls.setAuto({ on: bool(params, \"on\"), from: list(params, \"from\"), rate: params.rate }) };\n },\n};\n","import type { WalletNetwork } from \"@ghostly/core\";\n/**\n * Mints a new wallet starts with, so nobody has to know what a mint is before\n * receiving sats. The first one that answers is where Lightning invoices are\n * created; ecash from any of them is accepted from contacts.\n *\n * A mint is a custodian: it holds the sats. This list is a product decision,\n * to be reviewed regularly. It was picked on 2026-09-18 from the community\n * auditor (https://audit.8333.space): mints with thousands of audited swaps,\n * the highest success rates and an OK state at the time.\n */\nexport const DEFAULT_MINTS = [\n \"https://mint.minibits.cash/Bitcoin\",\n \"https://21mint.me\",\n \"https://mint.mountainlake.io\",\n];\n\n/** Public test mint: worthless sats, invoices settle by themselves. */\nexport const TEST_MINT = \"https://testnut.cashu.space\";\n\n/**\n * Mints whose sats are worth nothing: never counted as money, never mixed with real sats.\n * Mutinynet's mint (cashu.mutinynet.com) would belong here, but on 2026-09-22 it answered with a\n * duplicated Access-Control-Allow-Origin header that every browser and WebView rejects.\n */\nexport const TEST_MINTS: readonly string[] = [TEST_MINT];\nexport const isTestMint = (url: string) => TEST_MINTS.includes(url.replace(/\\/+$/, \"\"));\n\n/** A mint on this very machine (a local test server): whatever it issues is not money anyone else holds. */\nexport function isLocalMint(url: string): boolean {\n try { return [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(new URL(url).hostname); } catch { return false; }\n}\n\n/**\n * Mints whose sats are worth nothing: the public test mints and mints on this machine. They belong to\n * the Testnet mode, and their ecash never settles a request for real sats. Only the public test mints\n * are ever added by themselves (a contact must not make this app contact a port on this machine).\n */\nexport const isWorthlessMint = (url: string) => isTestMint(url) || isLocalMint(url);\n\n/**\n * A mint whose word \"this invoice is paid\" proves nobody paid it. The public test mint runs a fake Lightning backend\n * that marks every invoice paid by itself, and a mint on this machine is a test server that may do the same. Its\n * invoices are still shown to be paid, but the wallet mints one only once a payer says it paid it (see `held` on\n * StoredQuote); asking it for test coins on purpose is `CashuWallet.testCoins`.\n */\nexport const paysItsOwnInvoices = isWorthlessMint;\n\n/**\n * Mints on this machine the operator declared test servers with a fake Lightning backend (the CLI's\n * `GHOSTLY_TEST_MINTS`, for regtest and e2e stacks). Nothing else declares one: the apps never do.\n */\nconst declaredTestMints = new Set<string>();\nconst bare = (url: string) => url.trim().replace(/\\/+$/, \"\");\nexport function declareTestMints(urls: readonly string[]): void {\n for (const url of urls) if (isLocalMint(bare(url))) declaredTestMints.add(bare(url));\n}\n\n/**\n * Whether a Testnet wallet may pay a Bitcoin (`lnbc`) invoice through this mint. Test mints issue `lnbc` invoices,\n * but so does a real node: only a mint known to fake its Lightning moves nothing real when it pays one. That is the\n * public test mint, or a mint on this machine the operator declared a test server; never any other mint on this\n * machine, which may be a self-hosted mint with real sats behind it.\n */\nexport const fakesLightning = (url: string) => isTestMint(url) || declaredTestMints.has(bare(url));\n\n/** Refused before any mint is asked: see `fakesLightning`. */\nexport const BITCOIN_INVOICE_ON_TESTNET = \"This is a Bitcoin invoice (real money): test sats pay one only through the public test mint. Nothing was sent.\";\n\n/** Real money, or test networks: each wallet has its own (see WalletNetwork in @ghostly/core). */\nexport type WalletMode = WalletNetwork;\n/** The network a Cashu mint's ecash belongs to: test mints and mints on this machine are Testnet. */\nexport const mintNetwork = (url: string): WalletNetwork => isWorthlessMint(url) ? \"testnet\" : \"mainnet\";\n","import type { DiscoveryStatus, GroupMention, GroupMessageFrame, ImageMeta, LinkPreview, PairingProgress, PaymentMethodName, RoutineCard, StatusCard, TaskCard, TypingKind, UsageCard, VideoMeta, VoiceMeta, WirePin, WireReaction } from \"@ghostly/core\";\nimport type { UsdtWalletView } from \"../engine/paymentAdapters/usdtWallet\";\nimport type { ArkWalletView } from \"../engine/paymentAdapters/arkWallet\";\nimport type { BarkWalletView } from \"../engine/paymentAdapters/barkWallet\";\nimport type { FedimintWalletView } from \"../engine/paymentAdapters/fedimintWallet\";\nimport type { SparkWalletView } from \"../engine/paymentAdapters/sparkWallet\";\nimport type { LightningView } from \"../engine/paymentAdapters/providers/lightningService\";\nimport type { LightningCardView } from \"../engine/paymentAdapters/providers/lightningCards\";\nimport type { BitcoinView } from \"../engine/paymentAdapters/providers/bitcoinService\";\nimport type { PaymentReview, PaymentTarget, VapidKeys, WakeTarget, WalletNetwork } from \"@ghostly/core\";\nimport type { ProviderDescriptorView } from \"../engine/paymentAdapters/providers/types\";\nimport type { CapsState, DeliveryMode, DhtDeliveryState, DhtDeliveryView, HoldKind } from \"@ghostly/core\";\nimport type { S3Config } from \"../backup/s3\";\nimport type { BackupReminders } from \"./backupReminder\";\nimport type { TransportCause, TransportEntry, TransportEvent } from \"../engine/transportLog\";\nimport type { PublicProfile, ProfileChoice } from '../profiles/public';\nimport type { NostrContactCache, NostrContactView, NostrSocialSettings, NostrSocialState } from \"../nostr/types\";\nimport type { ProofLedger, ProofAdapter } from \"@ghostly/core\";\nimport type { IdentityDisplay, IdentityLedger, IdentityStatus, IdentityTimelineEntry, SharedIdentity, VerifiedIdentity } from \"@ghostly/core\";\nimport type { DataLinkState, LinkStatus, LiveAttempt, ServiceAd, PairingState, NativeTransport, PairedTransport, TransportDescriptors, TransportWait } from \"@ghostly/core\";\nimport type { CommunityState, GroupByeFrame, GroupCommit, GroupRole, GroupState, GroupStatus } from \"@ghostly/core\";\n\n/** A link as stored in IndexedDB. Same fields Desktop keeps in its ChatSession. */\nexport interface StoredLink {\n /**\n * Paired 1:1 chats: how to wake the contact's closed web app, as it shared it (`wake/1`, WISP 401 § Wake-up push).\n * Absent until it does, and again once it stops sharing or its push service says the subscription is gone.\n */\n peerWake?: WakeTarget;\n /** Paired 1:1 chats: this side's token for this chat in its own wake-ups; a new one whenever the subscription changes. */\n wakeToken?: string;\n /** Paired 1:1 chats: muted here, so the contact is told to forget this side's subscription until it is unmuted. */\n wakeMuted?: boolean;\n deliveryMode?: DeliveryMode;\n dhtDeliveryState?: DhtDeliveryState;\n /** This side's layer-0 capability record and the contact's last good one (WISP 03). */\n capsState?: CapsState;\n publicProfiles?: PublicProfile[];\n profileChoice?: ProfileChoice;\n peerProofs?: ProofLedger;\n /** Identity proofs shared in this chat, both ways (WISP 300). */\n identities?: IdentityLedger;\n /** The contact's Nostr profile, follows and notes, cached under their proven key, loaded only on request. */\n nostrSocial?: Record<string, NostrContactCache>;\n profile?: \"paired-chat/1\";\n participationSeed?: string;\n transportSeeds?: Partial<Record<NativeTransport, string>>;\n peerDescriptors?: TransportDescriptors;\n peerTransports?: PairedTransport[];\n peerFallback?: boolean;\n /** Absent: no choice for this chat, the app's rule applies (\"Automatic\"). */\n preferredTransport?: PairedTransport;\n transportFallback?: boolean;\n /** The chat's connection story, newest last (see engine/transportLog.ts). Local only. */\n transportLog?: TransportEntry[];\n /** Every connection event, for the connection panel, newest last (see engine/transportLog.ts). Local only. */\n transportHistory?: TransportEvent[];\n pairedPeerKey?: string;\n /** The inviter's participation key, from a `ghostly1` code (the joiner's side): the only key that may be pinned. */\n peerParticipationKeyZ32?: string;\n /** Absent only in old releases, where first pin required an explicit code comparison. */\n peerTrust?: { version: 1; verifiedKey?: string; verifiedAt?: number };\n requireSignedSignals?: boolean;\n id: string;\n seedB64: string;\n peerPubKeyZ32: string;\n encKeyB64: string;\n createdAt: number;\n label?: string;\n /**\n * The name the contact asked to be shown by, as they last said it. `\"\"` means a paired contact said they\n * have none (removed, or not shared); absent means nothing was heard yet.\n */\n peerNick?: string;\n /** The contact's profile picture, as they last sent it (checked, small JPEG data URL). */\n peerAvatar?: string;\n /** Present on the side that created the link, until the peer shows up. */\n inviteCode?: string;\n /** Messages deleted on this device, by id, so a republished one is not stored again. */\n deletedIds?: string[];\n /**\n * Files of mine whose offer went on the DHT floor (WISP 403 § Files) and that were deleted here before they went live,\n * by wire id: the next session that carries files/3 says each is cancelled (`pf-abort`), so the contact's bubble does too.\n */\n withdrawnFiles?: string[];\n /** This side's reactions the contact has not confirmed yet (WISP 401 § Reactions), in the order of their numbers. */\n reactionsOut?: WireReaction[];\n /** The chat's pinned message (WISP 400 § Pinned message): the latest pin, either side's; `id` \"\" once unpinned. */\n pin?: StoredPin;\n /** This side's pin the contact has not confirmed yet (WISP 401 § Pinned message): said on each live session until it does. */\n pinOut?: WirePin;\n /** Ways of paying this device allows in this chat. Absent or true: allowed. */\n paymentMethods?: Partial<Record<PaymentMethodName, boolean>>;\n /**\n * For a way of paying, the networks this chat accepts it on (its cards on the Accept side). Absent for a method:\n * every network. A request, a payment or an ask of a network off here is refused, and the contact is told.\n */\n paymentNetworks?: Partial<Record<PaymentMethodName, WalletNetwork[]>>;\n /** Store-and-forward for this contact (WISP 404, `hold/1`). Absent: off, as for every chat from before it. */\n hold?: HoldState;\n /** An edge of a private group (WISP 900): the group, and the member at the other end. Not a chat. */\n group?: string;\n groupPeer?: string;\n /**\n * Not an edge but an entry session of the group's link (`group-entry/1`): `host` on the admin's\n * side, toward a joiner's member key; `guest` on the joiner's, toward the link's entry key.\n */\n groupEntry?: \"host\" | \"guest\";\n /**\n * An edge whose session is up: set when it opens, cleared when it drops while this app runs (not when the app quits).\n * Still set when the app starts, it was live when the app last ran, and is dialled at once (`resume`). Local only.\n */\n edgeLive?: boolean;\n /** When that session opened: a member's offer from before it is not answered after a restart (`resumeFloor`). Local only. */\n edgeLiveSince?: number;\n}\n\n/** What happened to a group's membership, as a line in its history. */\nexport type GroupEvent = \"created\" | \"joined\" | \"gone\" | \"admin\" | \"rotated\" | \"removed\" | \"left\" | \"forked\" | \"picture\" | \"renamed\";\n\n/** A private group as stored: an invitation not yet answered, or a group I am (or was) in. */\nexport interface StoredGroup {\n id: string;\n createdAt: number;\n /** Pending (or accepted, awaiting the welcome) invitation, on the invitee's side. */\n invitation?: {\n name: string;\n /** The inviter's member key, learned over the contact chat: the admin who must have admitted me. */\n admin: string;\n /** The contact chat it came over. */\n linkId: string;\n e: number;\n n: number;\n /** My member seed, once accepted. */\n seedB64?: string;\n /** Chain pieces that precede the welcome of a long chain. */\n pieces: { t: \"group-chain\"; g: string; commits: GroupCommit[] }[];\n /** Joining through the group's link: its entry key, the admin's side of `linkId` (an entry session, not a contact chat). */\n entry?: string;\n /**\n * Removed and invited again over a contact chat: the hash of the genesis of the chain I held. The id alone names no\n * group (anyone may sign a genesis under it): only a welcome whose chain starts there joins the history I kept.\n */\n genesis?: string;\n };\n /**\n * On the admin's side: contact chats invited and not answered yet, and when. Kept so that an accept after my app\n * started again still lets them in.\n */\n invited?: Record<string, number>;\n /** On the admin's side: the group's link is on, with this entry key seed (`group-entry/1`). */\n entry?: { seedB64: string; createdAt: number };\n state?: GroupState;\n /** Member key → the contact chat that invited them (or me): the path for courtesy notices. */\n contacts?: Record<string, string>;\n /**\n * Members whose apps said (`paired-groups` version 3, on their edge or the link that admitted them) that they take\n * rosters past eight: an admin grows the group past eight only when every other member is here (WISP 902 § Bounds).\n */\n large?: string[];\n /**\n * Removed, then invited again over a contact chat: the names my app knew members by (member key → name). The history\n * stays while the invitation waits, and still names who wrote it; once in again, the names of members who are gone\n * since go on naming what they wrote.\n */\n formerNames?: Record<string, string>;\n /** Removed, then invited again: the removed group as it was, which a Decline puts back (with the history it kept). */\n previous?: StoredGroup;\n /**\n * Past 16 members, with hubs (WISP 902 · Group Mesh § Hubs): the hubs I kept edges with (as a hub, the other hubs),\n * where my edges go when the app starts again, before the beacon is read.\n */\n hubs?: string[];\n /** Members whose edge came up without `paired-groups` version 4: their apps take no hubs, so hubs keep edges with them. */\n legacy?: string[];\n /**\n * I left: the group is gone from the list and its history from the device. What is kept is the\n * edge to the admin, until the admin's commit removing me arrives or `at` is a week old, so\n * a leave said while the admin was away still reaches it. With hubs, the edges to my hubs too, and my\n * signed leave (`bye`) that they carry to the admin.\n */\n left?: { at: number; admin: string; hubs?: string[]; bye?: GroupByeFrame };\n /**\n * I declined an invitation while its inviter's chat (`linkId`) was down: the answer is said when that chat is up, or\n * dropped once `at` is a week old. Alone when nothing else of the group is kept; beside `state` when the invitation\n * was to a group I was removed from, which stays.\n */\n declined?: { at: number; linkId: string };\n /**\n * On the admin's side: members I removed while they could not be told (their edge was down, and no contact chat\n * carried the notice), by member key. `e`: the epoch of the commit that removed them; `at`: when. The edge to each\n * stays until it opens and that commit goes over it (`told`, the first time), or `at` is a week old: a member removed\n * while its app was closed otherwise never learns it, since every member closes the edge to someone out of the\n * roster. `opened`: when the edge's connection first opened; it closes a while after, told or not.\n */\n farewells?: Record<string, { at: number; e: number; told?: number; answered?: number; opened?: number }>;\n /** The group's pinned message (WISP 400 § Pinned message): the latest pin; `id` \"\" once unpinned. */\n pin?: StoredPin;\n /**\n * When the latest message of another member (`peer`) and the latest one that names me (`mention`) reached this\n * device, where that is later than the message's own time (`cameAt`): what makes the group unread survives a restart.\n */\n came?: { peer?: number; mention?: number };\n /** A community group (`group-community/1`) I am in: its session state. Mesh groups use `state`. */\n community?: CommunityState;\n /**\n * Joining a community group through its link (`group2/…`), or again after my admission lost a\n * race: my member seed, the entry session, and what arrived of the welcome.\n */\n joining?: { g: string; host: string; seedB64: string; linkId: string; name: string; inviter: string; invitedAt?: number; pieces: unknown[]; since: number; check?: boolean };\n}\n\nexport interface GroupMemberView {\n key: string;\n role: GroupRole;\n me: boolean;\n nick?: string;\n /** The pairwise edge to this member is open, or a hub I am connected to reaches them (always true for me). */\n online: boolean;\n /** Reached through a hub, not an edge of mine (WISP 902 · Group Mesh § Hubs). */\n viaHub?: boolean;\n /** A hub of the group now (listed in its beacon), and what the admin said: pinned as a hub, or never one. */\n hub?: boolean;\n hubRole?: \"pin\" | \"exclude\";\n /** Messages of the current epoch known to be missing from this member. */\n missing: number;\n /** The pairwise edge to this member as the engine sees it: absent for me, and until the edge exists. */\n edge?: GroupEdgeView;\n}\n\n/** One edge of a group's mesh (WISP 902): a paired link toward one member, which carries the group to and from them. */\nexport interface GroupEdgeView {\n linkId: string;\n /**\n * `open`: frames flow (the member is reachable). `connecting`: the member answered and the channel is being set up.\n * `waiting`: nothing heard from the member's app yet. `error`: the last attempt failed (`error` says why).\n */\n state: \"open\" | \"connecting\" | \"waiting\" | \"error\";\n /** The transport carrying the edge while it is open: WebRTC, or Iroh or HyperDHT where one side has none. */\n transport?: PairedTransport;\n /**\n * Waiting because this device has no free native slot for it (a member reached over Iroh or HyperDHT, and the\n * device's native listeners held by chats and other group links): it is tried again as one frees up.\n */\n noSlot?: true;\n /** When this device last heard from the member on this edge, in ms (0: never). */\n lastSeenAt: number;\n /** The engine's own words for why the last attempt failed (English, for logs and details); `cause` sums it up. */\n error?: string;\n /**\n * What `error` comes down to, for a short status: `relays`, its packets could not go out or be read (the Pkarr relays\n * failed); `session`, a connection opened and dropped in a way that says nothing about the member (it is dialled\n * again); `other`, anything else (a refused key, no common transport...).\n */\n cause?: GroupEdgeCause;\n}\n\nexport type GroupEdgeCause = \"relays\" | \"session\" | \"other\";\n\n/**\n * How far a join through a group's link got, as the joiner can know it: the knock is being left,\n * it is there for the admin's app to read, that app opened the entry session, it let me in. Or a member's app\n * answered that there is no room yet (`group-full`, WISP 902 § A full group): `full`, or `blocked` while the group\n * waits for every member's app to take more than eight; the joiner goes on knocking.\n */\nexport type GroupJoinStage = \"knocking\" | \"knocked\" | \"answered\" | \"admitted\" | \"full\" | \"blocked\";\n\nexport interface GroupView {\n id: string;\n name: string;\n createdAt: number;\n /** Which kind of group: a private mesh of up to 32 (`group-mesh/1`) or a community (`group-community/1`). */\n profile: \"mesh\" | \"community\";\n /** Absent while it is only an invitation. */\n status?: GroupStatus | \"lost\";\n statusReason?: string;\n epoch?: number;\n myKey?: string;\n isAdmin: boolean;\n /**\n * Admin work is off for this group on this device (WISP 06 § Forced takeover): no commit is signed and no door duty\n * taken until the person turns on \"Manage groups from this device\" (`setGroupManage`).\n */\n adminOff?: true;\n members: GroupMemberView[];\n /** On the invitee's side, until the welcome arrives. */\n /**\n * `waiting`: joining a community through its link, how many others were knocking with me when I last knocked.\n * `full`: with stage `full` or `blocked`, the members the group has and the most it holds, as the answer said.\n * `unanswered`: accepted over a contact chat, said again as often as it is, and the inviter's app answered nothing\n * while that chat was up: the answer is offered again.\n */\n invitation?: { linkId: string; contact: string; admin: string; members: number; accepted: boolean; unanswered?: boolean; viaLink?: boolean; stage?: GroupJoinStage; waiting?: number; full?: { n: number; max: number } };\n /** The group's link while it is on (`group1/<id>/<entry key>`); only the admin who made it sees it. */\n entryLink?: string;\n /** Contacts (by chat id) invited by me and not yet in. */\n invited: string[];\n /** Contact chats that are members, by chat id → member key. */\n memberLinks: Record<string, string>;\n lastMessageAt: number;\n /** When the latest message from another member arrived (what makes the group unread; mine and membership lines do not); absent for none. */\n lastPeerMessageAt?: number;\n /** When the latest message that names me arrived; absent for none. */\n lastMentionAt?: number;\n /** The latest reaction in the group, for the chat list. */\n lastReaction?: ReactionNote;\n /** The group's pin (WISP 400 § Pinned message): `id` \"\" once unpinned; absent when nothing was ever pinned. */\n pin?: PinView;\n canSend: boolean;\n /** The group's picture (a JPEG data URL the engine checked), set by its admin; absent for none. */\n picture?: string;\n /**\n * A private group past 16 members that runs on hubs (WISP 902 · Group Mesh § Hubs): `hub` when I am one. Absent for\n * the full mesh.\n */\n hubs?: { hub: boolean };\n /** Community groups: how this device is connected (a hub for others, or through hubs). */\n community?: { hub: boolean; hubs: number; connected: number };\n /** Private groups: the members typing now, in the order they started (WISP 902 · Group Mesh § Typing). Never stored. */\n typing?: GroupTypingView[];\n /** Private groups: this side's wake-up tokens, one per member it shared its subscription with (the push worker's table). */\n wakeTokens?: string[];\n /** Private groups: its members were told not to wake this side while the group is muted (`setWakeMuted`). */\n wakeMuted?: boolean;\n /**\n * The names of authors no longer in the roster (removed, left, or back with a new member key), by\n * member key, so their earlier messages still say who wrote them. Absent when there are none.\n */\n formerNames?: Record<string, string>;\n}\n\n/** A member typing in a group: `kind` when it is not plain typing, and a bot's status line, sanitized as in a 1:1 chat. */\nexport interface GroupTypingView { key: string; kind?: Exclude<TypingKind, \"typing\">; status?: string }\n\n/** One item held in this device's storage for the contact, or on its way there. */\nexport interface HeldEntry {\n seq: number;\n /** The id on the wire: the text's wire id, the file's wire id or the payment id. */\n id: string;\n /** The chat message this item is, for its delivery state. */\n messageId: string;\n kind: Exclude<HoldKind, \"manifest\">;\n /** What to rebuild the bundle from: the file's local id or the payment id; text is on the message. */\n ref?: string;\n /** The object's name in storage, chosen before the first upload and kept across retries. */\n name: string;\n bytes: number;\n ts: number;\n expires: number;\n state: \"queued\" | \"held\" | \"failed\";\n error?: string;\n}\n\n/** Everything store-and-forward keeps per chat: the switch, what the contact said, sequences and the outbox. */\nexport interface HoldState {\n /** This device offers `hold/1` in this chat: it accepts held items, and holds items for the contact when it can. */\n enabled: boolean;\n /** The contact's app accepts held items, as it last said (handshake or session). */\n peerAllows?: boolean;\n /** This device's mailbox folder for the contact in its storage, chosen once. */\n mailbox?: string;\n /** Sequence of the last item held for the contact. */\n outSeq: number;\n /** Highest sequence received from the contact and stored. */\n inSeq: number;\n /** Highest sequence the contact said it received of what was held for it. */\n peerAck: number;\n /** This device's pointer revision, and the contact's last seen one. */\n pointerRev: number;\n peerPointerRev: number;\n /** When the manifest and its addresses were last signed (they live seven days). */\n manifestSignedAt?: number;\n outbox: HeldEntry[];\n /** Items from the contact refused on the way in (changed, oversized, not for this chat). */\n refused: number;\n /** Their sequences, the last 32, told to the contact on this device's pointer. */\n refusedSeqs?: number[];\n /** Ways of paying the contact allowed at the last session, for requests held while it is away. */\n peerPaymentMethods?: PaymentMethodName[];\n}\n\n/** A file attached to a message. The bytes live in the `files` store under `id`. */\nexport interface MessageFile {\n id: string;\n name: string;\n size: number;\n mime: string;\n /** A voice message: its length and the shape of its sound. */\n voice?: VoiceMeta;\n /** A video: its length, size and a small first frame, when the sender sent them. */\n video?: VideoMeta;\n /** A picture: its size as it is shown, when the sender sent it. */\n image?: ImageMeta;\n}\n\nexport interface FileTransferView {\n state: \"transferring\" | \"done\" | \"failed\";\n transferred: number;\n size: number;\n error?: string;\n /**\n * files/3 (WISP 501 rev 0.3), while `transferring`: where it stands when it is not moving. `waiting`: no live\n * connection (it goes on by itself); `asking`: the receiver's person has not decided (on the receiving side,\n * this person); `queued`: accepted, waiting its turn; `paused`; `verifying`: every byte there, checking the digest.\n */\n stage?: \"preparing\" | \"waiting\" | \"asking\" | \"queued\" | \"paused\" | \"verifying\";\n /** files/3: the transfer can be paused, resumed and cancelled from here. */\n direction?: \"in\" | \"out\";\n pausedBy?: \"me\" | \"peer\";\n /** Bytes per second lately, while moving. */\n rate?: number;\n /** Receiving, `asking`: bytes this device can still take for files, when it says. */\n room?: number | null;\n /** Failed but the sender can offer it again (files/3). */\n retry?: boolean; /**\n * files/3, unfinished and not moving for a while (or not since the app started): the sender can send it again\n * (`resend`), the receiver ask for it again (`request`). Either goes on from what the receiver holds.\n */\n stalled?: boolean;\n /**\n * A group file (WISP 503) waiting, `waiting`: why. `nobody`: nobody this device is connected to has it; `busy`: its\n * holders answered busy; `damaged`: what came failed the author's digest (`stalled`: its person can ask again).\n */\n wait?: \"nobody\" | \"busy\" | \"damaged\";\n /** The same, in a few words (\"Nobody you are connected to has this file yet\"). */\n note?: string;\n}\n\n/** Ecash held by this peer. One row per proof; `reserved` while an operation is using it. */\nexport interface StoredProof {\n mint: string;\n id: string;\n amount: number;\n secret: string;\n C: string;\n dleq?: unknown;\n /** Handed to the mint for a Lightning payment that has not settled: not spendable, not yet gone. */\n reserved?: boolean;\n /**\n * Brought back by a profile restore: a copy of ecash as it was when the backup was made, which the profile it was\n * copied from (or this one, before the backup was restored) may have spent since. The mint is asked once, and a\n * proof it reads spent is dropped (see `CashuWallet.checkRestored`).\n */\n unchecked?: boolean;\n}\n\n/** A Lightning payment the mint has not settled yet. Its proofs stay in the wallet, reserved, until it does. */\nexport interface PendingMelt {\n /** The melt quote id. */\n quote: string;\n mint: string;\n /** The invoice, so it is not paid a second time while this one is in flight. */\n request: string;\n /** What the invoice pays. */\n amount: number;\n /** The reserved proofs handed to the mint. */\n secrets: string[];\n /** Everything that left the balance for this payment: the proofs above plus any swap fee. */\n outlay: number;\n /** NUT-08 blank outputs (`OutputData.serialize`), to unblind the fee change once the mint returns it. */\n outputs: unknown[];\n note?: string;\n /** Set when the invoice pays a contact's payment request. */\n paymentId?: string;\n createdAt: number;\n}\n\n/**\n * A swap at a mint, written down before the mint is asked, with everything that finishes it: the mint signs the\n * outputs of `swap` and nothing else, so its signatures can be asked for again (NUT-09) for as long as this is kept.\n * Deleted in the transaction that stores what the swap brought, or once the mint says it never happened.\n */\nexport interface PendingSwap {\n id: string;\n mint: string;\n /** `receive`: a token redeemed into this wallet. `send`: this wallet's ecash split, for a token or a Lightning payment. */\n kind: \"receive\" | \"send\";\n /** cashu-ts' `SerializedSwapPreview`: the inputs and the blinded outputs with their secrets. As secret as the ecash itself. */\n swap: unknown;\n createdAt: number;\n /** When the request to the mint ended with no answer. The mint may still act on it for a moment after that. */\n attemptEndedAt?: number;\n /**\n * The inputs were given back (the swap was overdue with nothing signed), and it holds nothing any more. Still asked\n * about for a while: a request that reached the mint late is recovered from what is written here.\n */\n released?: boolean;\n /** What it brought is stored; only the chat of its payment is still to be told (the app closed in between). */\n finished?: boolean;\n /**\n * Its inputs read spent at a mint that cannot say what it signed (no NUT-09): how many times that was the answer.\n * Kept, since the outputs are the only way back to that ecash, and asked about less and less often (`askAt`).\n */\n stuck?: number;\n /** Not asked about before this time. */\n askAt?: number;\n /** Came with a restored copy of the profile: what it brings is as old as the copy, and is checked like the copy's ecash. */\n restored?: boolean;\n /** `receive`: the token being redeemed. Redeeming it again while this is kept finishes this swap, and makes no other. */\n token?: string;\n /** `receive`: the history line of the ecash, written with it. */\n tx?: WalletTx;\n /** `receive`: the payment record written with the ecash (a contact's payment received, or one of ours taken back). */\n payment?: StoredPayment;\n}\n\n/** A Lightning invoice the mint issued for us; paid invoices turn into ecash. */\nexport interface StoredQuote {\n quote: string;\n mint: string;\n amount: number;\n invoice: string;\n createdAt: number;\n expiresAt: number | null;\n /** Set when the invoice belongs to a payment request sent to a peer. */\n paymentId?: string;\n /**\n * The mint says it issued this quote's ecash, but the wallet never stored it. Kept, not polled: it is the\n * only record of sats the user paid for.\n */\n issuedUnclaimed?: boolean;\n /** The mint said this invoice is paid, and its ecash is not claimed yet (the claim is tried again every round). */\n paid?: boolean;\n /**\n * The mint marks its invoices paid by itself (a test mint, `paysItsOwnInvoices`): its \"paid\" says nothing about a\n * payer. Not polled, never minted by itself: a payer saying it paid (`CashuWallet.vouch`) lets it be minted.\n */\n held?: boolean;\n /** Asked for on purpose, as test coins from a test mint (\"Get test coins\"): minted as soon as the mint says paid. */\n testCoins?: boolean;\n /**\n * The blinded outputs the mint is asked to sign for this invoice (`OutputData.serialize`), written down before it\n * is asked: the ecash it issues is for these, and its signatures for them can be asked for again (NUT-09).\n */\n outputs?: unknown[];\n}\n\nexport type PaymentState =\n | \"pending\" // sent, no word from the peer yet / request waiting to be paid\n | \"settled\"\n | \"failed\"\n | \"reclaimed\"; // the peer never took the ecash and it is back in the wallet\n\n/** A payment or payment request in a chat, by id. */\nexport interface StoredPayment {\n /** A request made because the contact asked to pay (its `pay-ask` id), or, on the payer's side, the ask it answers. */\n ask?: string;\n target?: PaymentTarget;\n txid?: string;\n id: string;\n linkId: string;\n kind: \"payment\" | \"request\";\n direction: \"in\" | \"out\";\n amount: number;\n unit: string;\n memo?: string;\n state: PaymentState;\n error?: string;\n createdAt: number;\n mint?: string;\n /** Outgoing ecash, kept until the peer confirms so it can be reclaimed. */\n token?: string;\n /** Requests: how the payer can pay. */\n invoice?: string;\n mints?: string[];\n /**\n * Real money or test coins: only a wallet of this network pays a request, or made a payment. Absent on records\n * from before wallets had their own network: its target, mints or invoice say (see `paymentNetwork`).\n */\n network?: WalletNetwork;\n /** Fedimint requests: the federations the payee takes ecash of (their ids). */\n federations?: string[];\n /** Fedimint payments: the federation of the notes, and the client operation that spent (or redeems) them. */\n federation?: string;\n fedimintOp?: string;\n requestId?: string;\n /** Requests we pay: a Lightning payment is in flight at the mint, so paying again would pay twice. */\n lightningPending?: boolean;\n /** Requests we pay: this device paid it over Lightning (the payee's wallet cannot say who paid an invoice). */\n paidHere?: boolean;\n /**\n * A request to a whole group (WISP 902 § Payments): the group's id. Its `linkId` is `group:<id>`, it went to\n * every member over their edges, and the first member whose payment settles it pays it; later ones are refused.\n */\n group?: string;\n /**\n * A request closed by the one who made it (their wallet for it was removed): it can no longer be paid, and a\n * payment still made to what it named is lost. Set on both sides, with `state` \"failed\".\n */\n closed?: boolean;\n}\n\n/** What pages see of a payment: everything but the token. */\nexport type PaymentView = Omit<StoredPayment, \"token\">;\n\nexport interface MintView {\n url: string;\n name: string;\n balance: number;\n /** Null until the mint answered once. */\n info: MintInfoView | null;\n /** What it still waits for (see `mintAwaiting`), when it does: removing it asks about these first. */\n awaiting?: WalletAwaitingView[];\n}\n\n/** What a mint says about itself and what it charges. */\n/** What a pasted piece of ecash says about itself. Mirrors `CashuInspection` in the UI's platform contract. */\nexport type CashuInspection =\n | { kind: \"token\"; amount: number; unit: string; mint: string; memo?: string; accepted: boolean }\n | { kind: \"request\"; amount: number | null; unit: string; mints: string[]; description?: string };\n\nexport interface MintInfoView {\n version?: string;\n motd?: string;\n /**\n * Fee for spending ecash, in sats per thousand proofs (NUT-02). A swap,\n * a redeem or a Lightning payment spends a handful of proofs and is charged\n * the sum, rounded up. 0 means spending ecash is free.\n */\n inputFeePpk: number;\n /** Lightning in and out: allowed amounts in sats, null when the mint sets no bound. */\n receive: { min: number | null; max: number | null } | null;\n send: { min: number | null; max: number | null } | null;\n}\n\nexport type WalletTxKind =\n | \"lightning-in\"\n | \"lightning-out\"\n | \"ecash-in\" // from a contact, or a pasted token\n | \"ecash-out\"\n | \"reclaimed\"\n | \"fee\"; // a Lightning payment failed, and the mint kept its fee for preparing it (`amount` 0)\n\n/** One movement of the wallet. `fee` is exact: what left the balance beyond `amount`, or what a redeem cost. */\nexport interface WalletTx {\n id: string;\n timestamp: number;\n mint: string;\n kind: WalletTxKind;\n /** Sats that moved, always positive; the kind gives the direction. */\n amount: number;\n fee: number;\n note?: string;\n}\n\n/** The kinds of wallet a profile can have: each is one card of the deck, on one network. */\nexport type WalletType = \"cashu\" | \"lightning\" | \"arkade\" | \"bark\" | \"spark\" | \"bitcoin\" | \"fedimint\" | \"usdt\";\nexport const WALLET_TYPES: readonly WalletType[] = [\"cashu\", \"lightning\", \"arkade\", \"bark\", \"spark\", \"bitcoin\", \"fedimint\", \"usdt\"];\n\n/** One network's wallets, whole: what `WalletView` shows of the network in use, for either network. */\nexport interface NetworkWalletsView {\n ark?: ArkWalletView;\n bark?: BarkWalletView;\n fedimint?: FedimintWalletView;\n spark?: SparkWalletView;\n usdt?: UsdtWalletView;\n /** The network's default Lightning card for receiving (what a caller from before cards reads as its Lightning). */\n lightning?: LightningView;\n /** Every Lightning card of the network, in the order they were added: each its own source, balance and history. */\n lightnings?: LightningCardView[];\n bitcoin?: BitcoinView;\n /** This network's Cashu mints (test mints and mints on this machine are Testnet's). */\n mints: MintView[];\n balance: number;\n /** Cashu: sats held for a payment or a swap the mints have not settled yet. Not in `balance`, and not gone. */\n setAside?: number;\n /** Cashu: swaps at these mints that are not finished yet, and the sats in them. Removing the wallet names them. */\n openSwaps?: number;\n swapsAmount?: number;\n /** Cashu: sats in swaps whose inputs read spent at a mint that cannot say what it gave for them. Not in `setAside`. */\n unconfirmed?: number;\n /** This network's history, newest first. */\n history: WalletTx[];\n feesPaid: number;\n /** What this network's wallets still wait for: removing one reads it (see `walletRemoval`). Always set by the engine. */\n awaiting?: WalletAwaitingView[];\n}\n\n/**\n * Something one wallet still waits for. `request`: a request of ours still open in a chat that only this wallet can\n * be paid through; `invoice`: an invoice of its own, not paid and not expired; `paid`: an invoice the mint says is\n * paid whose ecash is not claimed yet; `unclaimed`: ecash the mint says it issued for an invoice of ours that this\n * wallet never received; `sent`: ecash sent from it that the contact has not taken yet. Removing the wallet loses\n * what is paid to the first four; `sent` Cashu comes back once its mint is added again.\n */\nexport interface WalletAwaitingView {\n /** The wallet it goes through (Lightning through the Cashu mints is the Cashu wallet's). */\n type: WalletType;\n /** `swap`: sats in an exchange with a Cashu mint that the mint has not settled yet (`PendingSwap`). */\n kind: \"request\" | \"invoice\" | \"paid\" | \"unclaimed\" | \"sent\" | \"swap\";\n /** In the wallet's base unit: sats, or the token's smallest unit for USDT. */\n amount: number;\n /** The chat payment it belongs to, when it does. */\n paymentId?: string;\n /** Lightning: the card it goes through. */\n card?: string;\n}\n\n/**\n * A wallet the profile has: one type on one network, one of each but Lightning, which a network can have several\n * cards of. `id` is `<type>:<network>`, and `lightning:<network>:<card>` for a Lightning card. Its seed and secrets\n * stay in the engine; `config` holds only what can be shown (a server, a source).\n */\nexport interface WalletInstanceView {\n id: string;\n type: WalletType;\n network: WalletNetwork;\n config: Record<string, string>;\n /** Lightning: the card's id on its network, its name, and whether it is the network's default for receiving. */\n card?: string;\n name?: string;\n receive?: boolean;\n /**\n * At home on another device (WISP 06 § Wallets that stay home): that device's name (\"\" when this device does not know\n * it), and when its coins expire (Ark, Bark). It is never opened here, and takes no payment here.\n */\n home?: { device: string; expiresAt?: number };\n}\n\n/**\n * What New can make: one type on one network. `available` false: `reason` says why (Mainnet not validated yet,\n * one already there). `needs`: the one thing it asks for, when it cannot be made in one click.\n */\nexport interface WalletOffer {\n type: WalletType;\n network: WalletNetwork;\n available: boolean;\n reason?: string;\n /** Already made: its card is in the deck. */\n exists?: boolean;\n /** A network can have several of it (Lightning): New adds one more, whatever `exists` says. */\n several?: boolean;\n needs?: \"invite\" | \"provider\" | \"apiKey\";\n /** Lightning and on-chain: the sources that can be picked on this network. */\n providers?: ProviderDescriptorView[];\n}\n\n/** What New asks the engine to make (see `walletCreate`). */\nexport interface WalletCreate {\n type: WalletType;\n network: WalletNetwork;\n /** Lightning and on-chain: the source, and the values of its form (blank ones take the network's default). */\n providerId?: string;\n values?: Record<string, string>;\n /** Fedimint: the federation's invite code. */\n invite?: string;\n /** Spark on Mainnet: the person's Breez API key. */\n apiKey?: string;\n}\n\n/** What Remove asks the engine to take away (see `walletRemove`). */\nexport interface WalletRemove {\n type: WalletType;\n network: WalletNetwork;\n /** Lightning: the card to remove; absent, the network's default for receiving. */\n card?: string;\n /** The person confirmed that what it holds on this device becomes unreachable without its backup. */\n acceptLoss?: boolean;\n}\n\n/** \"Get test coins\" on a Testnet wallet: which one asks its faucet. */\nexport interface WalletTestCoins {\n type: WalletType;\n network: WalletNetwork;\n /** Lightning: the card asking (only the Cashu mints' card has a faucet); absent, the default one. */\n card?: string;\n}\n\n/** What the faucet gave: `amount` in the wallet's own unit (\"test sats\", \"TEST-USDT\"). */\nexport interface TestCoinsResult {\n amount: number;\n unit: string;\n /** Asked, and on its way (a transaction the chain has not confirmed): it shows up in a few seconds. */\n pending?: boolean;\n}\n\n/**\n * The first-run wallet setup of a new profile: the Mainnet kinds still to make, and why the last try at each failed.\n * Kept in the settings. Absent on a profile from before it, which is never set up by itself.\n */\nexport interface WalletSetupRecord {\n /** Kinds not made yet, tried again at each start. A kind with no Mainnet wallet here yet (on-chain) waits quietly. */\n left: WalletType[];\n /** Why the last try failed, per kind still left. */\n failed?: Partial<Record<WalletType, string>>;\n}\n\n/** What the Wallet page shows of the first-run setup: whether it runs now, and each kind it could not make. */\nexport interface WalletSetupView {\n running: boolean;\n failed: { type: WalletType; network: WalletNetwork; reason: string }[];\n}\n\nexport interface WalletView {\n /** The first-run setup, while it runs or has a kind it could not make; absent otherwise. */\n setup?: WalletSetupView;\n /** Both networks' wallets, open side by side. The flat fields below are Mainnet's, for a caller naming no network. */\n networks?: Record<WalletNetwork, NetworkWalletsView>;\n /** The wallets this profile has, in the deck's order. */\n wallets?: WalletInstanceView[];\n /** What New can make on each network. */\n offers?: WalletOffer[];\n ark?: ArkWalletView;\n /** Second's Ark (Bark): a different Ark server from `ark`, not interchangeable with it. */\n bark?: BarkWalletView;\n /** Federations joined with an invite code, and their ecash. */\n fedimint?: FedimintWalletView;\n /** Spark, wallet to wallet (the Breez SDK's Spark wallet; it can also be the Breez Lightning source). */\n spark?: SparkWalletView;\n usdt?: UsdtWalletView;\n /** The Lightning source of this mode (the Cashu mints by default) and its latest operations. */\n lightning?: LightningView;\n lightnings?: LightningCardView[];\n /** The on-chain Bitcoin source of this mode, if one is set up. */\n bitcoin?: BitcoinView;\n intents?: PaymentReview[];\n mints: MintView[];\n balance: number;\n /** Cashu: see `NetworkWalletsView`. */\n setAside?: number;\n openSwaps?: number;\n swapsAmount?: number;\n unconfirmed?: number;\n /** Newest first. */\n history: WalletTx[];\n feesPaid: number;\n /** Each Mainnet wallet's backup reminder, by wallet id: the page works out which asks now (`backupDue`). */\n backupReminders?: BackupReminders;\n}\n\nexport interface StoredMessage {\n wireId?: string;\n /**\n * `held`: in this device's storage, waiting for the contact to come back (WISP 404).\n * `queued`: unconfirmed, and sent again by itself under the same id once the chat can carry it.\n * `waiting`: not sent yet; it goes by itself when the chat can carry it (live, or the DHT text before it\n * confirmed), and can be cancelled meanwhile (\"Sends when live\", WISP 400).\n */\n delivery?: \"sending\" | \"sent\" | \"queued\" | \"waiting\" | \"held\" | \"delivered\" | \"failed\";\n deliveryError?: string;\n /** Until when a `queued` message is sent again by itself; after that it waits for Retry. */\n resendUntil?: number;\n /**\n * A file of mine that waits for the live link, said on the DHT floor meanwhile (WISP 403 § Files): `sent` its offer\n * went, `seen` the contact's app took it (its bubble is in place there), `unfit` it cannot go there (it waits as before).\n */\n fileOnFloor?: \"sent\" | \"seen\" | \"unfit\";\n linkId: string;\n id: string;\n text: string;\n sender: \"me\" | \"peer\";\n /**\n * The row's place in its history, which is sorted by it (WISP 400, requirement 10). Mine: when I sent it. Received:\n * when it was first stored on this device (`arrivalKey`), never the sender's clock, so a conversation reads in the\n * order things happened here. A row received before `sentAt` existed keeps the sender's time it was stored under.\n */\n timestamp: number;\n /** Received: when the sender says it sent it, by its own clock. Shown (`shownTime`), never sorted by. */\n sentAt?: number;\n /** `hold`: through the sender's storage while the other side was away (WISP 404). */\n via: \"pkarr\" | \"datalink\" | \"hold\";\n nick?: string;\n file?: MessageFile;\n paymentId?: string;\n /** Group messages: the member key of the sender. */\n member?: string;\n /** Group history lines that are not messages. */\n event?: GroupEvent;\n /** Group history: a payment between members, as the group knows it (WISP 902 § Payments). */\n groupPay?: GroupPayNote;\n /** Group messages: the places of the text that name members, by member key (WISP 902 § Mentions). */\n mentions?: GroupMention[];\n /** A group message someone else sent that names me (or everyone). */\n mentioned?: true;\n /**\n * A private group's message from another member: the frame it came in (sealed, signed by its author), kept on the\n * group's newest rows to hand on to members who were away (`GROUP_LIMITS.relay`, WISP 902 § Catch-up). The engine's\n * own: a row goes to a page without it (`withoutFrame`).\n */\n frame?: GroupMessageFrame;\n /** How this message travelled, as the engine saw it go or come (the message's details view). */\n details?: MessageDetails;\n /** A link preview that came with the text (WISP 401 § Link previews): made by the sender's app, never fetched here. */\n preview?: LinkPreview;\n /** The message this one answers (WISP 400 § Replies). */\n replyTo?: MessageReply;\n /**\n * Reactions to it (WISP 400 § Reactions), one per person: `me`, `peer` (a 1:1 chat's contact) or a group member's\n * key. One taken back stays with an empty emoji, so an older reaction arriving late does not bring it back.\n */\n reactions?: Record<string, MessageReaction>;\n /** A text edited after it was sent (WISP 400 § Edits): `text` is the latest version. */\n edit?: MessageEdit;\n /**\n * A forwarded text or file (WISP 400 § Forwards): how many times it has been forwarded, this hop included. Only the\n * count: never who wrote it first, nor where it came from.\n */\n forwarded?: number;\n /**\n * A bot's status card (WISP 405 · Status Cards): a task or a routine shown instead of `text`, which is its fallback.\n * It belongs to the version it came with: an edit brings its own, or leaves the message a text.\n */\n card?: StatusCard;\n /**\n * A reply that presses a button of this side's own message (WISP 406 · Message Buttons), as the author's engine took\n * it: set only when the button was open for this person. What `button.pressed` reports.\n */\n press?: MessagePress;\n /**\n * A card of mine (WISP 405 · Status Cards, WISP 406 · Message Buttons) whose text went on the DHT floor or into a hold,\n * which carry text alone: `due` until the card goes again live, as an edit of the card alone; `sent` once it did, or\n * once it was given up (too old).\n */\n cardRestore?: \"due\" | \"sent\";\n /** What `cardRestore` was named before it covered every kind of card (buttons only): read for rows kept since. */\n buttonsRestore?: \"due\" | \"sent\";\n}\n\n/**\n * A message that carries a task, a routine or a usage card, as the Tasks board and the chat list read it across every\n * chat and group (`statusCardIndex`): where it is (`linkId`: a chat's id, or `group:<id>`), its message, who sent it (`member`: a group\n * member's key, absent for my own), when it was sent and when its last edit was made. Nothing else of the message.\n */\nexport interface CardIndexRow {\n linkId: string;\n id: string;\n card: TaskCard | RoutineCard | UsageCard;\n sender: \"me\" | \"peer\";\n member?: string;\n timestamp: number;\n editedAt?: number;\n}\n\n/** A page of a chat's history, oldest first, and whether older messages remain (`messagePage`). */\nexport interface MessagePage { messages: StoredMessage[]; more: boolean }\n\n/**\n * A chat's pin as kept (WISP 400 § Pinned message): the message's id both sides know (`id`, \"\" unpinned), the pinner's\n * number (the highest wins), who pinned (`me`, `peer`, or a member's key) and when it came here. `messageId`: the row\n * here, when it was here then. A private group's keeps the pinner's signature (`k`, `sig`), to pass it on.\n */\nexport interface StoredPin extends WirePin { by: string; at: number; messageId?: string; k?: string; sig?: string }\n\n/** A chat's pin, as the pages and the CLI see it. `id`: the id both sides know the message by (\"\" once unpinned); `messageId`: the row here. */\nexport interface PinView { id: string; by: string; at: number; messageId?: string }\n\n/** One person's reaction to a message: the emoji (\"\" once taken back), their number (the highest wins), when it came. */\nexport interface MessageReaction { e: string; n: number; at: number }\n\n/** The latest reaction in a chat, as the chat list says it: \"Ana reacted ❤️ to \"…\"\". */\nexport interface ReactionNote {\n at: number;\n /** Who reacted: `me`, `peer`, or a group member's key. */\n by: string;\n emoji: string;\n /** A line of the message it went to. */\n snippet: string;\n /** The message was mine. */\n mine: boolean;\n /** The message's id here: an edit of it changes `snippet`. */\n message?: string;\n}\n\n/** How a message was edited (WISP 400 § Edits). */\nexport interface MessageEdit {\n /** The edit shown: its number (1 for the first; the highest one wins) and when its author made it. */\n seq: number;\n at: number;\n /** Earlier versions, oldest first (the newest `EDIT_HISTORY_KEEP`): when each was written, and its text. */\n history: MessageVersion[];\n /** Mine: the contact has not confirmed this edit yet. It goes by itself once the chat is live and both sides offer edit/1. */\n pending?: true;\n /**\n * Mine: the engine's own edit that sends a question's buttons again, the text unchanged (WISP 406 · Message Buttons),\n * never one a person or a bot made. The bot's event stream reports none for it; a later edit makes a new one without it.\n */\n restore?: true;\n}\n\nexport interface MessageVersion { at: number; text: string }\n\n/**\n * The message a reply answers, as kept with the reply (WISP 400 § Replies). What the replier's app saw of it goes\n * with the reply; the receiver keeps its own view of the original when it has it (`messageId`), and the line and the\n * author then come from there, not from the wire.\n */\n/** A button press on this side's message (WISP 406 · Message Buttons). */\nexport interface MessagePress {\n /** The message with the buttons, its id here. */\n messageId: string;\n button: string;\n label: string;\n /** Named by its label or id in the text, not by the reply (the DHT floor, or an app without buttons). */\n inferred?: true;\n}\n\nexport interface MessageReply {\n /** The original's id in this chat as both sides know it: a paired chat's wire, file or payment id; a group message id. */\n id: string;\n /** A line of the original, plain and short (`replySnippet`); empty when only its id came (over the DHT). */\n snippet: string;\n /** Who wrote the original; absent when nothing says (only its id came, and it is not here). */\n from?: \"me\" | \"peer\";\n /** A group's original: its author's member key. */\n member?: string;\n /** A button press (WISP 406 · Message Buttons): the original's button this reply presses, its text the label. */\n button?: string;\n /**\n * The original here, when it was found in this chat as the reply was kept (always, for a reply sent here). Without\n * it the line is only what the replier's app said: shown, but marked as not checked.\n */\n messageId?: string;\n}\n\n/**\n * The way a message went, as the engine saw it at that moment: a live transport, the DHT floor (a paired chat's\n * `dht-text/1` envelope), the sender's storage (`hold/1`), or a chat from before pairing (`legacy-*`).\n */\nexport type MessagePath = PairedTransport | \"dht\" | \"hold\" | \"legacy-datalink\" | \"legacy-dht\";\n\n/** One send of a message: when, over what, and how it ended. */\nexport interface MessageSend {\n at: number;\n path: MessagePath;\n /** The live session crossed a relay (WISP 100, \"Relayed\"); the relays' hosts. */\n relayed?: boolean;\n relays?: string[];\n /** The round trip measured on the session at the time. */\n rttMs?: number;\n /** Absent while the send is under way. */\n result?: \"sent\" | \"failed\";\n error?: string;\n}\n\n/**\n * What the engine remembers about one message's travels, for its details view (WISP 400 § Message details): facts\n * as they were when the message went or came, that nothing else keeps. Kept on the message row, bounded by\n * `MESSAGE_DETAILS_MAX_BYTES`. Never a key, a seed or anything else that opens the chat.\n */\nexport interface MessageDetails {\n /** Sends of this message, oldest first: the first and the last ones (`MESSAGE_DETAILS_MAX_SENDS`). */\n sends?: MessageSend[];\n /** Sends there were in all, when more than `sends` keeps. */\n attempts?: number;\n /** A received message: when it came and over what. */\n received?: Omit<MessageSend, \"result\" | \"error\">;\n /** The last successful send was handed to the transport. */\n sentAt?: number;\n /** The contact's receipt came (the message is `delivered`). */\n receiptAt?: number;\n /** Stored for an away contact, sealed for them. */\n heldAt?: number;\n /** A file: its last byte arrived here, or the contact confirmed it. */\n completedAt?: number;\n /** The frame the message travelled in. `wireBytes`: as sent on the channel (a JSON frame, a sealed record, a bundle). */\n wire?: { frame: string; protocol: string; plaintextBytes?: number; wireBytes?: number; chunks?: number; chunkBytes?: number };\n /** The DHT floor: the envelope's sequence and times, the sealed packet's size and nonce, the record read. */\n dht?: { seq?: number; issued?: number; expires?: number; packetBytes?: number; nonce?: string; recordKey?: string; records?: string[] };\n /** Store-and-forward: the item's sequence in the mailbox, its sealed size and expiry. */\n hold?: { seq?: number; bytes?: number; expires?: number; mailbox?: string };\n}\n\n/** A details record larger than this (as JSON) is trimmed: its oldest sends go first. */\nexport const MESSAGE_DETAILS_MAX_BYTES = 2048;\n/** Sends kept on a record: the first, and the last ones. */\nexport const MESSAGE_DETAILS_MAX_SENDS = 6;\n\n/** What the details view is made of: the row, its record, and what the engine knows around it right now. */\nexport interface MessageDetailsView {\n message: Pick<StoredMessage, \"id\" | \"wireId\" | \"linkId\" | \"sender\" | \"timestamp\" | \"sentAt\" | \"via\" | \"delivery\" | \"deliveryError\" | \"resendUntil\" | \"member\" | \"nick\"> & {\n kind: \"text\" | \"file\" | \"voice\" | \"payment\" | \"event\" | \"note\";\n /** UTF-8 bytes of the text. */\n textBytes: number;\n };\n details?: MessageDetails;\n /** The chat the message is in. Keys are the public participation keys (z-base-32), never a seed. */\n link?: {\n id: string;\n profile?: \"paired-chat/1\";\n deliveryMode?: DeliveryMode;\n /** Our own participation key on this link. */\n myKey: string;\n /** The contact's, as pinned; absent before the first contact. */\n peerKey?: string;\n /** The pinned key was compared by the two people (WISP 401 § verification). */\n verified: boolean;\n /** The session carrying the chat right now, if any. */\n transportNow?: PairedTransport;\n relayedNow?: boolean;\n rttNowMs?: number;\n };\n /** The file the message carries (files/2, files/3 or a held item), as stored. */\n file?: {\n id: string; name: string; size: number; mime: string;\n /** SHA-256 of the bytes, hex, once known (the sender's own, or checked on arrival). */\n digest?: string;\n protocol?: \"files/2\" | \"files/3\" | \"hold/1\";\n state?: string;\n stage?: string;\n transferred?: number;\n /** Why a transfer that ended without the file did. */\n error?: string;\n /** files/3: bytes confirmed durably, where a restart resumes. */\n confirmed?: number;\n /** files/3: when the offer was made. */\n since?: number;\n consented?: boolean;\n /** Where the bytes are kept on this device. */\n storage?: string;\n voice?: { duration: number; peaks: number };\n };\n /** The payment or request the message stands for: everything but the money itself. */\n payment?: {\n id: string; kind: \"payment\" | \"request\"; direction: \"in\" | \"out\"; amount: number; unit: string; state: PaymentState;\n method?: string; network?: string; provider?: string; asset?: string; mint?: string; txid?: string; ask?: string;\n createdAt: number; error?: string; memo?: string; group?: string;\n /** SHA-256 of the invoice, hex, when there is one (the invoice itself carries the amount and a payment hash). */\n invoiceDigest?: string;\n lightningPending?: boolean; paidHere?: boolean;\n };\n /** A group message: which group, and the member key of the sender when it is not this device. */\n group?: { id: string; member?: string; profile?: string };\n}\n\n/** The ways of paying a group note can name. */\nexport type GroupPayRail = \"cashu\" | \"lightning\" | \"arkade\" | \"bark\" | \"bitcoin\" | \"spark\" | \"usdt\" | \"fedimint\";\n\n/**\n * A payment between two members as the whole group sees it (WISP 902 § Payments): who pays whom, how much, over\n * what, and how it stands. No money and nothing to pay with: that travels only on the edge between the two.\n */\nexport interface GroupPayNote {\n id: string;\n /** `request`: a member asks to be paid; `payment`: a member sends without a request. */\n kind: \"request\" | \"payment\";\n /** Who pays: a member key, or `*` for a request any member may pay once. */\n from: string;\n /** Who is paid. */\n to: string;\n /** In the unit's smallest denomination (sats, token base units). */\n amount: string;\n unit: string;\n decimals: number;\n rail: GroupPayRail;\n /** Test money (test mints, test networks): worth nothing. */\n test?: boolean;\n memo?: string;\n /** The request answers the payer's own ask (Ark, Bark, on-chain, USDT): the payer started it. */\n ask?: boolean;\n createdAt: number;\n /** `sent`: the payer says it paid, the payee has not confirmed. `closed`: failed, refused or taken back. */\n state: \"open\" | \"sent\" | \"paid\" | \"closed\";\n /** Who paid it: the payee's word, or, when its wallet cannot tell (an invoice), the only member who said so. */\n by?: string;\n /** Members who said they paid it. */\n claims?: string[];\n /** The last note this device said about it, sent again when an edge opens. */\n mine?: GroupPayFrame;\n}\n\n/** `group-pay` on an edge (WISP 902 § Payments). Old apps drop it. */\nexport interface GroupPayFrame {\n t: \"group-pay\";\n g: string;\n id: string;\n k: \"req\" | \"pay\";\n f: string;\n to: string;\n v: string;\n u: string;\n d: number;\n r: GroupPayRail;\n x?: 1;\n m?: string;\n a?: 1;\n ts: number;\n st: GroupPayNote[\"state\"];\n by?: string;\n}\n\n/** A local web application the user chose to share. */\nexport interface StoredService {\n id: string;\n name: string;\n /** Normalized loopback URL, e.g. `http://localhost:3400` */\n target: string;\n enabled: boolean;\n createdAt: number;\n /**\n * The contacts allowed to reach this service, by participation key. Absent or\n * empty means nobody: a service is granted per contact, never to everyone at\n * once, and a service from before this existed starts closed rather than\n * keeping an exposure its owner never chose.\n */\n sharedWith?: string[];\n}\n\nexport interface IceServerSetting {\n urls: string;\n username?: string;\n credential?: string;\n}\n\nexport interface Settings {\n online: boolean;\n nick: string;\n /** This profile's picture, shown to paired contacts: a small square JPEG data URL. */\n avatar?: string;\n /**\n * Whether contacts are told this profile's name and picture (WISP 401 § name and picture). Absent means\n * yes; off, every chat and group member is told there is none, and the join notice names nobody.\n */\n shareProfile?: boolean;\n /** The Pkarr relays. Browsers read and write through them; the Desktop writes to them (so browser contacts see its packets). */\n relays: string[];\n /**\n * Where the DHT is reached directly (the Desktop): whether reads may use the relays too, \"Also use Pkarr relays\"\n * in Settings, Network. Absent means no: reads go to the DHT alone.\n */\n readRelays?: boolean;\n /** Extra ICE servers (typically TURN) on top of the built-in STUN set. */\n iceServers: IceServerSetting[];\n /**\n * The Iroh relays a browser build homes on (WISP 102, relay only): a page cannot send UDP, so every Iroh\n * packet goes through one. Absent or empty means the defaults (n0's public relays, as the desktop app's).\n */\n irohRelays?: string[];\n /**\n * The HyperDHT relay (wss://) this browser reaches the HyperDHT through, so paired chats can use HyperDHT\n * (WISP 103) beside WebRTC. Empty: none. Absent means the default (shared/hyperdhtRelay.ts). The Desktop\n * runs HyperDHT itself and ignores it.\n */\n hyperdhtRelay?: string;\n /** Cashu mints this peer holds ecash at and accepts ecash from. The first is where invoices are created. */\n mints: string[];\n /** False until the default mints were put in place once; after that the list is the user's. */\n mintsInitialized: boolean;\n /** The Nostr social layer: relays, automatic profile loading, publication. Absent means the defaults, everything off. */\n nostr?: NostrSocialSettings;\n /**\n * Identity cards show the public profile (picture, name, bio, counts) of a verified identity, read from its\n * network's public host when the card is on screen (PUBLIC-PROFILES.md). Absent means on; off drops what was kept.\n */\n publicProfiles?: boolean;\n /**\n * Whether paired contacts are told when this profile is typing (WISP 401 § Typing). Absent means yes; off,\n * nothing is said, and a contact's typing is still shown.\n */\n sendTyping?: boolean;\n /**\n * Whether files are downloaded without asking where the rules allow it (WISP 503: a group's voice messages, and its\n * files up to 8 MiB within 256 MiB a group). Absent means on; off, each waits for a Download.\n */\n autoDownloads?: boolean;\n /**\n * Whether this device sends the group files it keeps to members who ask (WISP 503 § Serving limits). Absent means\n * on; off, it answers that it is busy and does not say it holds them.\n */\n serveFiles?: boolean;\n /**\n * Where items are held for away contacts (WISP 404): the profile's S3 storage and its random space\n * (WISP 1000/1002), as set up under Profile → Backups. Kept here for the peer, which may run outside the\n * page; never copied into a backup.\n */\n holdStorage?: { s3: S3Config; space: string } | null;\n /** The first-run wallet setup (see `WalletSetupRecord`). Only the engine writes it: a settings patch cannot. */\n walletSetup?: WalletSetupRecord;\n /**\n * This profile's push subscription (the installed web app, WISP 401 § Wake-up push): shared with each paired\n * contact whose app offers `wake/1`, so it can wake this app while it is closed. Absent: not woken.\n */\n wake?: WakeSubscription;\n /**\n * A contact who held `wake` can no longer be told to forget it (its chat was deleted, or muted: a contact may ignore\n * `w: null`). The app replaces the subscription (a new endpoint and VAPID key pair, new tokens for the others) and\n * `setWakeSubscription` clears this. Set by the engine only.\n */\n wakeRotate?: boolean;\n /**\n * Private groups muted here (WISP 902 · Group Mesh § Wake-up push): their members are told to forget this profile's\n * subscription, as a muted chat's contact is. Only through `setWakeMuted` with the group's `group:<id>`.\n */\n wakeMutedGroups?: string[];\n /**\n * Private groups → members that were given a token on an edge a group on hubs later dropped: they still hold the\n * subscription with no edge to close, so their leaving the group (or this profile leaving it, or muting it) replaces\n * it. Cleared with every new subscription. Set by the engine only.\n */\n wakeHeldBy?: Record<string, string[]>;\n /**\n * In a profile on several devices (WISP 06 § Push and the phone): devices asked for a new subscription over their\n * device link, by signing key, with the endpoint they are to replace. A device that shares another endpoint (or none)\n * has done it. Set by the engine only.\n */\n wakeRenew?: Record<string, string>;\n /**\n * A push relay (https) this app hands a finished wake-up to when it may not post to the contact's push service\n * itself (a browser page: the services answer without CORS). Empty or absent: none; nobody runs one by default.\n */\n pushRelay?: string;\n /** The backup reminder of each Mainnet wallet (see `shared/backupReminder`). Only the engine writes it: a settings patch cannot. */\n backupReminders?: BackupReminders;\n}\n\n/** A browser push subscription and the VAPID key pair it was made with. */\nexport interface WakeSubscription {\n endpoint: string;\n p256dh: string;\n auth: string;\n vapid: VapidKeys;\n /**\n * In a profile on several devices (WISP 06 § Push and the phone): the signing key of the device whose subscription\n * this is. It moves with the profile, so a device with none of its own (a desktop) goes on giving contacts the\n * phone's. Set by the engine only; absent in a profile on one device, and for a subscription made before.\n */\n device?: string;\n}\n\n/**\n * What `updateSettings` takes: any settings, and of the Nostr ones only those that change. The rest of the\n * Nostr settings stay as stored, so a switch flipped from a page that has not seen the last save yet does not\n * put back what that save replaced.\n */\nexport type SettingsPatch = Partial<Omit<Settings, \"nostr\">> & { nostr?: Partial<NostrSocialSettings> };\n\n/** What pages see of store-and-forward in one chat. */\nexport interface LinkHoldView {\n /** The switch of this chat on this device. */\n enabled: boolean;\n /** The contact's app accepts held items, as it last said; undefined until it said anything. */\n peerAllows?: boolean;\n /** This device has storage to hold items in (Profile → Backups → S3 storage). */\n storage: boolean;\n /** Items can be held for the contact right now: switch on, contact allows it, storage set up, contact pinned. */\n canHold: boolean;\n /** Items held for the contact and not yet picked up. */\n outstanding: number;\n bytes: number;\n maxBytes: number;\n maxItems: number;\n ttlMs: number;\n /** Items from the contact refused on the way in. */\n refused: number;\n /** The last problem holding or picking up, for people. */\n error?: string;\n}\n\n/** A proof of this profile (Identities). */\nexport interface IdentityProofView {\n id: string;\n provider: string;\n subject: string;\n /** The proof key the statement authorizes. */\n key: string;\n verified: VerifiedIdentity;\n issuedAt: number;\n /** Seconds: the earlier of the statement's and the evidence's expiry. */\n expiresAt: number;\n createdAt: number;\n /** Chats it is currently shared in (or queued for). */\n sharedWith: number;\n /** The identity as a URI the profile's public DID can list (`alsoKnownAs`); absent when it has none. */\n publicUri?: string;\n /** What the provider checked (public: contacts receive it too). Removal uses it to take down a published proof. */\n evidence?: unknown;\n /** The identity's public profile on its network, once asked for (PUBLIC-PROFILES.md); absent while not asked or turned off. */\n publicProfile?: PublicProfileView;\n}\n\n/**\n * An identity's public profile, as its network publishes it (docs/wisps/PUBLIC-PROFILES.md): self-described by the\n * account, never part of the proof. Only for an identity with a current verified proof, with Settings → Load public\n * profiles on; kept on this device and asked again after a day.\n */\nexport interface PublicProfileView {\n /** False when the network has no profile for this identity (or none was read yet). */\n found: boolean;\n name?: string;\n /** How the network writes the account: \"@alice.bsky.social\", a Nostr `name`. */\n handle?: string;\n /** A short bio, plain text. */\n about?: string;\n /** The website the account names: an https address, shown as a plain link. */\n website?: string;\n /** A sanitized `data:image/jpeg;base64,…` URL, never a remote one. */\n avatar?: string;\n /** The profile names a picture that is not shown: which rule refused it, in words (\"it is a GIF; …\"). */\n avatarMiss?: string;\n followers?: number;\n following?: number;\n /** The hosts it was read from, which saw this device's IP address: \"nexus.pubky.app\". */\n hosts: string[];\n /** Seconds; 0 when it was never read. */\n fetchedAt: number;\n loading?: boolean;\n /** The last attempt failed (a copy read before, if any, is still shown). */\n error?: string;\n}\n\n/**\n * A verified identity's recent posts (docs/wisps/PUBLIC-PROFILES.md, \"Posts and follows\"): `loadPublicPosts`, asked\n * when its card is chosen in a contact's identities panel. Plain text, bounded, never markup.\n */\nexport interface PublicPostView {\n id: string;\n /** Seconds. */\n createdAt: number;\n text: string;\n /** Opens the post in its network's app or site. */\n url?: string;\n reply?: boolean;\n /** Its pictures, loaded only on a tap (`loadPublicPostImage` with the index here). `host`: who is asked. */\n images: { host?: string; alt?: string }[];\n}\n\nexport interface PublicPostsView {\n posts: PublicPostView[];\n /** More can be asked for (`more: true`). */\n more: boolean;\n /** The hosts asked, which saw this device's IP address. */\n hosts: string[];\n /** Seconds. */\n fetchedAt: number;\n /** Notes the person's own Nostr mute list hides. */\n hidden?: number;\n /** The identity's profile in its network's app or site. */\n profileUrl?: string;\n}\n\n/**\n * Who a verified identity follows and who follows it, only as far as it touches people the reader knows: the reader's\n * own identities and the identities contacts shared. Computed on this device from lists read from the same hosts as\n * the profile; the lists themselves are never shown or stored.\n */\nexport interface PublicGraphView {\n /** False when there was nobody to compare with (no identity of the reader's, no contact, on this network): nothing was read. */\n compared: boolean;\n /** Its follow list names one of the reader's identities. */\n followsYou: boolean;\n /** One of the reader's identities follows it (their followers list, or the reader's own follow list). */\n youFollow: boolean;\n /** Contacts whose shared identity it follows (`follows`) or who follow it (`followedBy`). */\n contacts: { linkId: string; follows: boolean; followedBy: boolean }[];\n /** A list was longer than this app reads: someone may be missing, never added. */\n partial?: boolean;\n hosts: string[];\n /** Seconds; 0 when nothing was read. */\n fetchedAt: number;\n}\n\n/** A post's picture, on the reader's tap: a re-encoded `data:image/jpeg` URL, or why there is none. */\nexport interface PublicPostImageView { src?: string; miss?: string; hosts: string[]; width?: number; height?: number }\n\n/** The profile's did:dht (WISP 310-did-dht): its own key, public to everyone, never tied to a chat. */\nexport interface ProfileDidView {\n /** `did:dht:…` */\n id: string;\n /** Identity proof ids the person listed as public, in order (some may not be publishable right now). */\n listed: string[];\n /** What the document says the subject is also known as: the listed identities that are verified and current. */\n alsoKnownAs: string[];\n /** The last packet put on Pkarr: when, and its version (the BEP44 sequence number). */\n published?: { at: number; versionId: string };\n /** The document out there is the current one. */\n upToDate: boolean;\n /** Why the last publish failed. */\n error?: string;\n}\n\n/** A contact's proof, as this app checked it. */\nexport interface ReceivedIdentityView {\n id: string;\n provider: string;\n subject: string;\n verified: VerifiedIdentity;\n /** A public name/picture looked up on request. */\n display?: IdentityDisplay;\n status: IdentityStatus;\n verifiedAt: number;\n checkedAt: number;\n expiresAt: number;\n error?: string;\n /** The provider's checks can go stale and the last one is old enough to repeat. */\n recheckDue: boolean;\n /** Its public profile, while the proof is verified and current (PUBLIC-PROFILES.md). */\n publicProfile?: PublicProfileView;\n}\n\nexport interface LinkIdentitiesView {\n /** Both sides offer identity proofs and the channel is open now. */\n support: boolean;\n /** Providers the contact's app said it can verify. */\n contactProviders?: string[];\n shared: SharedIdentity[];\n received: ReceivedIdentityView[];\n error?: string;\n}\n\nexport interface LinkView {\n /** Identity proofs in this chat; absent for chats that are not paired. */\n identities?: LinkIdentitiesView;\n /** The contact's Nostr data, one entry per key they proved in this chat. */\n nostr?: NostrContactView[];\n discoveryError?: string;\n publicProfiles?: PublicProfile[];\n profileChoice?: ProfileChoice;\n peerProofs?: Pick<ProofLedger, \"local\" | \"remote\">;\n peerProofSupport?: boolean;\n peerProofAdapters?: ProofAdapter[];\n participationKey?: string;\n peerParticipationKey?: string;\n proofError?: string;\n profile?: \"paired-chat/1\";\n pairing?: PairingState;\n /**\n * How far this chat's first pairing got (`@ghostly/core` `PairingProgress`): from the invite being\n * published to `live`. Absent for chats paired before this app start, DHT-only invites and legacy chats.\n */\n pairingProgress?: PairingProgress;\n peerVerified?: boolean;\n /**\n * `methods`: ways of paying both sides allow in this chat right now. `calls` / `services`: both sides offer\n * `calls/1` / `services/1` on the open session (paired chats only; they need a live connection).\n */\n capabilities?: {\n files: boolean; payments: boolean; methods?: Record<PaymentMethodName, boolean>; calls?: boolean; services?: boolean; largeFiles?: boolean;\n /**\n * The networks the contact has a wallet on, per way of paying, as it said on the open session. Absent: it said\n * none (an older app): any network may meet. A way of paying it has no wallet of is not in the map (one it has\n * but turned off here lists no network). A card is offered only where its network is in its list.\n */\n networks?: Partial<Record<PaymentMethodName, readonly WalletNetwork[]>>;\n };\n /** files/3 live in this chat: bytes the contact's device said it can still take for files, when it said. */\n peerFileRoom?: number | null;\n /** Ways of paying this device allows in this chat. */\n paymentMethods?: Record<PaymentMethodName, boolean>;\n /** For each way of paying, the networks this chat accepts it on. */\n paymentNetworks?: Partial<Record<PaymentMethodName, readonly WalletNetwork[]>>;\n /** Both sides announced private groups on the open session: this contact can be invited. */\n groups?: boolean;\n /** Paired chats: what each side offers after the handshake (`paired-capabilities`); `peer` is null until it says. */\n sessionOffers?: { mine: string[]; peer: string[] | null };\n /** Paired chats: why a call cannot be placed right now, or null when it can. */\n callsUnavailable?: string | null;\n /** Paired 1:1 chats: the contact is typing now (`typing/1` on the live session). Absent otherwise. Never stored. */\n peerTyping?: boolean;\n /** Paired 1:1 chats: this side's wake-up token for this chat (the push worker's table), when it shared its subscription. */\n wakeToken?: string;\n /** Paired 1:1 chats: the contact was told not to wake this side while the chat is muted (`setWakeMuted`). */\n wakeMuted?: boolean;\n /** Paired 1:1 chats: the contact shared how to wake its closed app, so a message sent while it is away wakes it. */\n peerWakes?: boolean;\n /** While `peerTyping`: what the contact is doing when it is not plain typing (recording a voice note, thinking). */\n peerTypingKind?: Exclude<TypingKind, \"typing\">;\n /** While `peerTyping`: the contact's (a bot's) status line, sanitized: one line, at most 40 characters, no links. */\n peerTypingStatus?: string;\n availableTransports?: PairedTransport[];\n /**\n * Every transport this app runs for the chat, started or not: DHT only releases the native ones, and an app with no\n * WebRTC (the Linux Desktop) then has none started, yet can still leave DHT only for any of these.\n */\n runnableTransports?: PairedTransport[];\n /** Transports a session with this contact would cross a relay on (a browser's HyperDHT or Iroh): a fallback, and shown as relayed. */\n relayedTransports?: PairedTransport[];\n deliveryMode?: DeliveryMode;\n dhtDelivery?: DhtDeliveryView;\n canSendText?: boolean;\n /** `hold`: the contact is away and what is sent now waits in this device's storage for it. */\n textDelivery?: \"stream\" | \"dht\" | \"hold\" | \"unavailable\";\n hold?: LinkHoldView;\n transportErrors?: Partial<Record<PairedTransport, string>>;\n preferredTransport?: PairedTransport;\n transportFallback?: boolean;\n /** No transport chosen for this chat: the app's rule applies. */\n transportAutomatic?: boolean;\n /** What the contact's app can use on this link, as it last said (a session, or its capability record); unknown before either. */\n peerTransports?: PairedTransport[];\n /**\n * The transport this chat is set to reach and is not on yet, and why (WISP 100, \"A chosen transport not reached\n * yet\"): waited for, never a failure. Absent when the chat is on it, or nothing limits where it goes.\n */\n transportWait?: TransportWait;\n /**\n * The last attempt to go live that did not, as this side saw it (WISP 100, \"Why a chat is not live\"): what it tried\n * and why each did not connect, whether this side dialled or answered, and when it dials again. Absent while live.\n */\n liveAttempt?: LiveAttempt;\n /** Which side dials this chat to go live (the other answers): paired chats. */\n liveDialer?: \"you\" | \"contact\";\n /** Round trip on the live session, once measured. */\n transportRttMs?: number;\n /**\n * The live session goes through relays, never directly (WISP 100, \"Relayed\"): a browser's Iroh. The relays'\n * hosts, this app's first. They see who talks to whom and when, not what is said.\n */\n transportRelayed?: { relays: string[] };\n /** While live: since when the current transport carries the chat, and why it was chosen (see engine/transportLog.ts). */\n transportLive?: { since: number; cause?: TransportCause };\n /** The chat's connection story, newest last; only for the chat on screen (`setActiveLink`). */\n transportLog?: TransportEntry[];\n /** Every connection event of the chat on screen, for its connection panel, newest last. */\n transportHistory?: TransportEvent[];\n /** Identities shared in this chat, both ways, as its timeline shows them, newest last; only for the chat on screen. Local only. */\n identityTimeline?: IdentityTimelineEntry[];\n /** When the contact last shared an identity here (milliseconds): the chat list's preview and order. Paired chats. */\n identitySharedAt?: number;\n /** The latest reaction in this chat, for the chat list; paired chats. */\n lastReaction?: ReactionNote;\n /** The chat's pin (WISP 400 § Pinned message): `id` \"\" once unpinned; absent when nothing was ever pinned. */\n pin?: PinView;\n id: string;\n myPubKeyZ32: string;\n peerPubKeyZ32: string;\n label?: string;\n /**\n * The name the contact asked to be shown by, as they last said it. `\"\"` means a paired contact said they\n * have none (removed, or not shared); absent means nothing was heard yet.\n */\n peerNick?: string;\n /** The contact's profile picture, as they last sent it (checked, small JPEG data URL). */\n peerAvatar?: string;\n inviteCode?: string;\n createdAt: number;\n status: LinkStatus;\n dataLink: DataLinkState;\n peerOnline: boolean;\n peerLastSeenAt: number;\n /** `null` when the peer does not advertise (offline, or a legacy client). */\n peerServices: ServiceAd[] | null;\n lastMessageAt: number;\n /** Newest of my messages the peer acknowledged through Pkarr. */\n peerAck: number;\n lastSyncAt: number;\n poll: { polling: boolean; nextAt: number; interval: number };\n}\n\nexport interface ServiceView extends StoredService {\n /** Requests served since the engine started. */\n requests: number;\n}\n\nexport interface EngineState {\n settings: Settings;\n /**\n * Limited mode (WISP 06 § When a device checks): the device could not read which device is active and was started\n * anyway. The profile is offline whatever `settings.online` says, and no wallet is open. Absent otherwise.\n */\n limited?: true;\n /**\n * Why limited mode is on, when it is a restored copy (WISP 06 § A backup restored where a device set exists): `checking`,\n * the turn is read every 30 seconds to learn whether another device runs the profile; `removed`, the read found the\n * device set moved after a removal, and only the person can start the copy as a profile of its own.\n */\n restoreCheck?: \"checking\" | \"removed\";\n /**\n * Whose push subscription `settings.wake` is, in a profile on several devices (WISP 06 § Push and the phone): `here`,\n * this device's own; `away`, another device's, given to contacts because this device has none of its own (the page\n * neither replaces nor turns off what is not its own). Absent in a profile on one device, or when nobody said.\n */\n wakeOwner?: \"here\" | \"away\";\n transport: {\n protocol: string; relays: string[];\n /** Present where Iroh runs in the browser (web app, extension): the relays it uses and the defaults. */\n iroh?: { relays: string[]; defaults: string[] };\n /** This client reaches the Mainline DHT itself (the Desktop): relays are for writes, and reads only when chosen. */\n direct?: boolean;\n /** How reads go and how each relay is doing, for the connection panel's Details. */\n discovery?: DiscoveryStatus;\n /** False where this client has no WebRTC (Ghostly Desktop on Linux: WebKitGTK has none). Absent where it has. */\n webrtc?: false;\n /**\n * This device's own WebRTC attempts say direct connections do not get through its network (a VPN, a firewall, a\n * carrier's NAT: `DirectPathWatch` in packages/core). Chats still go live through relays. Absent otherwise.\n */\n directBlocked?: true;\n /**\n * This device's clock seems to be off: this clock minus what the relays and several contacts say, in ms (positive\n * when it is ahead). From several sources agreeing, never one contact (`ClockWatch` in packages/core). Absent otherwise.\n */\n clockOffMs?: number;\n /**\n * False where a group's links have no transport at all here: no WebRTC and no native transport (WISP 902 §\n * Transports), so no member of a group can be reached from it. Absent where they have one (the Linux Desktop runs\n * Iroh and HyperDHT).\n */\n groupLinks?: false;\n };\n links: LinkView[];\n services: ServiceView[];\n /** Transfers since the peer started, by file id. */\n transfers: Record<string, FileTransferView>;\n /**\n * The kept transfers are back in `transfers`. False for the first moments after a start: a file with no transfer\n * yet may still be moving, not finished.\n */\n transfersRestored?: boolean;\n wallet: WalletView;\n payments: Record<string, PaymentView>;\n /** This profile's identity proofs. */\n identityProofs: IdentityProofView[];\n /** This profile's public DID. Absent until the engine has loaded it. */\n did?: ProfileDidView;\n /** The Nostr social layer: the person's own keys' data and the effective settings. */\n nostr: NostrSocialState;\n /** Private groups and pending invitations (WISP 900). */\n groups: GroupView[];\n /** The links of groups' edges: not chats, but payments with a member travel on them. */\n edges?: LinkView[];\n}\n","import { decodeBolt11, engineError } from \"@ghostly/core\";\nimport type { WalletMode } from \"../../../shared/mints\";\nimport type { CashuWallet } from \"../../wallet\";\nimport type { LightningInvoice, LightningPaymentRef, LightningProvider, LightningProviderDescriptor } from \"./lightning\";\nimport { NothingSpentError, PROVIDER_PLATFORMS } from \"./types\";\n\nexport const CASHU_MINT_SOURCE = \"cashu-mint\";\n\ninterface MintRef { mint: string; quote: string }\nconst parseRef = (ref: string | undefined): MintRef => {\n const parsed = JSON.parse(ref ?? \"null\") as MintRef | null;\n if (!parsed?.mint || !parsed.quote) throw new Error(\"Unknown Cashu quote\");\n return parsed;\n};\n\n/**\n * Lightning through the Cashu mints: invoices are mint quotes that turn into ecash once paid, and\n * invoices are paid by melting ecash. The default source, and what Lightning was before sources.\n *\n * The Cashu wallet already polls its quotes and pending melts, mints and books them and says so\n * (`onQuotePaid`, `onMeltResolved`): the engine forwards those, and never polls this source itself.\n */\nexport class CashuMintLightning implements LightningProvider {\n readonly capabilities = { receive: true, send: true, balance: true, lookup: true };\n readonly settlesItself = true;\n /** The melt quote shown to the person is the one paid: quoting again could change the fee. */\n private readonly quotes = new Map<string, { quote: string; mint: string; amount: number; feeReserve: number }>();\n\n constructor(private readonly wallet: CashuWallet, private readonly mode: WalletMode) {}\n\n async info() {\n // Test mints issue invoices for worthless sats (often with a mainnet prefix): the mode says which it is.\n return { network: this.mode === \"mainnet\" ? \"bitcoin\" as const : \"testnet\" as const, balance: (await this.wallet.view(this.mode)).balance };\n }\n\n async createInvoice(amount: number): Promise<LightningInvoice> {\n const quote = await this.wallet.receiveLightning(amount, undefined, this.mode);\n const decoded = decodeBolt11(quote.invoice);\n if (!decoded?.paymentHash) throw new Error(\"The mint returned an invoice that cannot be read\");\n return { invoice: quote.invoice, paymentHash: decoded.paymentHash, amount, expiresAt: quote.expiresAt ?? decoded.expiresAt * 1000, ref: JSON.stringify({ mint: quote.mint, quote: quote.quote }) };\n }\n\n async invoiceStatus(invoice: LightningInvoice) {\n const { mint, quote } = parseRef(invoice.ref);\n // A test mint's invoice reads paid by itself: until a payer says it paid, it is open (see CashuWallet.vouch).\n const state = await this.wallet.isHeld(quote) ? \"UNPAID\" : await this.wallet.mintQuoteState(mint, quote);\n if (state === \"PAID\" || state === \"ISSUED\") return { state: \"paid\" as const, amount: invoice.amount };\n return { state: invoice.expiresAt < Date.now() ? \"expired\" as const : \"open\" as const };\n }\n\n /** A real melt quote: its fee reserve is what paying may cost at most. It is kept for `payInvoice`. */\n async estimateFee(invoice: string) {\n const quote = await this.wallet.quoteInvoice(invoice, this.mode);\n this.quotes.set(invoice.trim().toLowerCase(), quote);\n return quote.feeReserve;\n }\n\n /** The quote behind a fee just shown, when there is one: `payQuote` pays exactly that. */\n quoteFor(invoice: string) {\n return this.quotes.get(invoice.trim().toLowerCase());\n }\n\n /** The melt quote `payInvoice` pays, when its fee was shown first: what the mint is asked about later. */\n paymentRef(invoice: string) {\n const quote = this.quotes.get(invoice.trim().toLowerCase());\n return quote ? JSON.stringify({ mint: quote.mint, quote: quote.quote } satisfies MintRef) : undefined;\n }\n\n /**\n * A payment the app was cut off from (the tab closed while it was being paid). A melt written down is the wallet's\n * to settle and report. With none, the mint's word on the quote decides: paid, or never melted (the melt is written\n * down before the mint is handed anything, so an unpaid quote with no melt spent nothing).\n */\n async interruptedPayment(payment: LightningPaymentRef) {\n if (await this.wallet.meltInFlight(payment.invoice)) return \"pending\" as const;\n if (!payment.ref) return \"unknown\" as const;\n const { mint, quote } = parseRef(payment.ref);\n const state = await this.wallet.meltQuoteState(mint, quote);\n // Asked again: a melt written while the mint was being asked is the wallet's to settle.\n if (await this.wallet.meltInFlight(payment.invoice)) return \"pending\" as const;\n return state === \"PAID\" ? \"paid\" as const : state === \"PENDING\" ? \"pending\" as const : \"failed\" as const;\n }\n\n async payInvoice(invoice: string, maxFee: number, note?: string) {\n const key = invoice.trim().toLowerCase();\n let quote = this.quotes.get(key);\n // Nothing reaches the mint's melt before `payQuote`, and it throws only when the sats did not leave.\n try {\n quote ??= await this.wallet.quoteInvoice(invoice, this.mode);\n if (quote.feeReserve > maxFee) throw engineError(\"lightningFeeTooHigh\", { fee: quote.feeReserve });\n } catch (error) {\n throw new NothingSpentError(error instanceof Error ? error.message : String(error));\n }\n this.quotes.delete(key);\n const ref = JSON.stringify({ mint: quote.mint, quote: quote.quote });\n let paid: boolean;\n try { paid = await this.wallet.payQuote(quote.quote, quote.mint, note); }\n catch (error) { throw new NothingSpentError(error instanceof Error ? error.message : String(error)); }\n return paid ? { state: \"paid\" as const, ref } : { state: \"pending\" as const, ref };\n }\n\n async paymentStatus(payment: LightningPaymentRef) {\n const { mint, quote } = parseRef(payment.ref);\n const state = await this.wallet.meltQuoteState(mint, quote);\n return { state: state === \"PAID\" ? \"paid\" as const : state === \"PENDING\" ? \"pending\" as const : \"failed\" as const };\n }\n\n async close() { this.quotes.clear(); }\n}\n\nexport const cashuMint: LightningProviderDescriptor = {\n id: CASHU_MINT_SOURCE,\n label: \"Cashu mints\",\n kind: \"lightning\",\n description: \"Invoices are paid into, and paid from, your Cashu balance. The mints hold the sats.\",\n networks: [\"bitcoin\", \"testnet\"],\n platforms: PROVIDER_PLATFORMS,\n fields: [],\n custodial: true,\n async create(_settings, host) { return new CashuMintLightning(host.cashu as CashuWallet, host.mode); },\n};\n","import type { WalletNetwork } from \"@ghostly/core\";\nimport type { WalletMode } from \"../../shared/mints\";\n\n/** A wallet of the mode being left stopped opening: the switch goes ahead without waiting for its network. */\nexport class ModeChanged extends Error {\n constructor() { super(\"The wallets switched mode\"); this.name = \"ModeChanged\"; }\n}\n\nexport const networkLabel = (network: WalletNetwork) => network === \"mainnet\" ? \"Mainnet\" : \"Testnet\";\n\n/**\n * What was asked of one network's wallet belongs to the other network's (a backup, a chain): nothing was changed.\n * `network` is where it belongs, so the engine can hand it to that wallet instead.\n */\nexport class WrongNetworkError extends Error {\n constructor(readonly network: WalletNetwork, message: string) { super(message); this.name = \"WrongNetworkError\"; }\n}\n\n/**\n * Opening a wallet waits on its network (an Ark server, an RPC), which can take minutes or never answer.\n * A mode switch must not wait behind that: `within` gives up on the wait as soon as the mode changes, and\n * whatever still arrives afterwards is handed to `dispose`. Nothing is saved before that wait ends, so\n * giving up leaves the profile as it was. Every wait on the network inside a wallet's queue goes through it,\n * reading a balance too, or the switch, queued behind it, waits anyway.\n */\nexport class ModeGate {\n constructor(private target: WalletMode = \"mainnet\") {}\n private controller = new AbortController();\n /** The mode its owner works in, once it says so: a wait that starts while a switch away from it is queued ends at once. */\n private current?: WalletMode;\n\n /** A switch to `mode` was asked for: waits for a wallet of another mode end now. */\n switching(mode: WalletMode) {\n if (this.target === mode) return;\n this.target = mode;\n this.controller.abort();\n this.controller = new AbortController();\n }\n\n /** The owner's switch to `mode` is applied (its queue reached it): waits are for this mode again. */\n entered(mode: WalletMode) { this.current = mode; }\n\n /**\n * A creation took too long: the waits under way end now, and so does every wait that starts until `resume`\n * (a creation still sealing its seed has not reached its server yet, and must not wait there either).\n */\n interrupt() { this.cut = true; this.controller.abort(); this.controller = new AbortController(); }\n resume() { this.cut = false; }\n private cut = false;\n\n /** The wallet stopped: every wait ends now, and any later one at once. */\n close() { this.closed = true; this.controller.abort(); }\n private closed = false;\n\n within<T>(work: Promise<T>, dispose?: (value: T) => unknown): Promise<T> {\n const signal = this.controller.signal;\n return new Promise<T>((resolve, reject) => {\n const give = () => { reject(new ModeChanged()); void work.then((value) => dispose?.(value), () => {}); };\n if (this.closed || this.cut || (this.current && this.current !== this.target)) return give();\n signal.addEventListener(\"abort\", give, { once: true });\n work.then(\n (value) => { signal.removeEventListener(\"abort\", give); if (!signal.aborted) resolve(value); },\n (error) => { signal.removeEventListener(\"abort\", give); if (!signal.aborted) reject(error); },\n );\n });\n }\n}\n","import { engineError, engineText } from \"@ghostly/core\";\nimport { STORES, openDb, store, transact, wrap } from \"../../shared/idb\";\nimport type { IntentRepository, SavedIntent } from \"./coordinator\";\n/**\n * A USDT payment is one transaction at one account nonce. Another payment from the same account and chain that is\n * submitted or of unknown outcome holds its nonce until it confirms. A review made after it was sent took the next\n * nonce and may go at once. One made before (or read from an RPC that had not seen it yet) has the same or a lower\n * nonce: sent, it would replace the earlier transaction or be dropped, so it is refused. A row with no nonce refuses.\n */\nfunction nonceTaken(review:SavedIntent[\"review\"],intents:SavedIntent[]):boolean {\n if(review.method!==\"usdt\")return false;\n const from=review.evm?.from?.toLowerCase(), nonce=review.evm?.nonce;\n return intents.some(({review:other})=>other.id!==review.id && other.method===\"usdt\" && other.chainId===review.chainId && other.evm?.from?.toLowerCase()===from && [\"submitted\",\"unknown\"].includes(other.state)\n && !(typeof nonce===\"number\" && typeof other.evm?.nonce===\"number\" && nonce>other.evm.nonce));\n}\nexport const intentRepository: IntentRepository = {\n async get(id) { return wrap<SavedIntent | undefined>((await store(STORES.intents,\"readonly\")).get(id)); },\n async list() { return wrap<SavedIntent[]>((await store(STORES.intents,\"readonly\")).getAll()); },\n async put(intent) { await transact([STORES.intents],s=>{s[STORES.intents].put(intent);}); },\n async claim(id) {\n const tx=(await openDb()).transaction(STORES.intents,\"readwrite\");\n return new Promise<SavedIntent>((resolve,reject)=>{\n let saved:SavedIntent;\n let reason=engineText(\"alreadySubmitted\");\n const request=tx.objectStore(STORES.intents).getAll();\n request.onsuccess=()=>{\n const intents:SavedIntent[]=request.result;\n saved=intents.find(item=>item.review.id===id)!;\n if(!saved || saved.review.state!==\"pending\") {tx.abort();return;}\n const duplicate=saved.review.requestId && intents.some(({review})=>review.id!==id && review.requestId===saved.review.requestId && review.linkId===saved.review.linkId && [\"submitted\",\"settled\",\"unknown\"].includes(review.state));\n if(duplicate){tx.abort();return;}\n if(nonceTaken(saved.review,intents)){reason=engineText(\"sentAfterReview\");tx.abort();return;}\n saved.review={...saved.review,state:\"submitted\",error:undefined,submittedAt:Date.now()};\n tx.objectStore(STORES.intents).put(saved);\n };\n tx.oncomplete=()=>resolve(saved);\n tx.onabort=tx.onerror=()=>reject(new Error(reason));\n });\n },\n async cancel(id) {\n const tx=(await openDb()).transaction(STORES.intents,\"readwrite\");\n return new Promise<SavedIntent>((resolve,reject)=>{\n let saved:SavedIntent;\n const request=tx.objectStore(STORES.intents).get(id);\n request.onsuccess=()=>{\n saved=request.result;\n if(!saved || saved.review.state!==\"pending\"){tx.abort();return;}\n saved.review={...saved.review,state:\"cancelled\"};\n tx.objectStore(STORES.intents).put(saved);\n };\n tx.oncomplete=()=>resolve(saved);\n tx.onabort=tx.onerror=()=>reject(engineError(\"cannotCancelSubmitted\"));\n });\n },\n};\n/** Key for a wallet that needs no password: its seed is protected like the Cashu proofs in the same database. */\nexport function newDeviceKey():string {return btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(32)))).replace(/=+$/,\"\");}\nexport interface EncryptedSeed {version:1;salt:number[];iv:number[];ciphertext:number[]}\nasync function passwordKey(password:string,salt:Uint8Array<ArrayBuffer>) {\n const material=await crypto.subtle.importKey(\"raw\",new TextEncoder().encode(password),\"PBKDF2\",false,[\"deriveKey\"]);\n return crypto.subtle.deriveKey({name:\"PBKDF2\",hash:\"SHA-256\",salt,iterations:600000},material,{name:\"AES-GCM\",length:256},false,[\"encrypt\",\"decrypt\"]);\n}\nexport async function sealSeed(seed:string,password:string):Promise<EncryptedSeed> {\n if(password.length<12)throw new Error(\"Use at least 12 characters for the wallet password\");\n const salt=crypto.getRandomValues(new Uint8Array(16)),iv=crypto.getRandomValues(new Uint8Array(12));\n const ciphertext=await crypto.subtle.encrypt({name:\"AES-GCM\",iv},await passwordKey(password,salt),new TextEncoder().encode(seed));\n return {version:1,salt:[...salt],iv:[...iv],ciphertext:[...new Uint8Array(ciphertext)]};\n}\nexport async function unsealSeed(seed:EncryptedSeed,password:string):Promise<string> {\n if(seed.version!==1)throw new Error(\"Unsupported wallet backup version\");\n try {return new TextDecoder().decode(await crypto.subtle.decrypt({name:\"AES-GCM\",iv:new Uint8Array(seed.iv)},await passwordKey(password,new Uint8Array(seed.salt)),new Uint8Array(seed.ciphertext)));}\n catch {throw new Error(\"Could not unlock the wallet. Check the password and backup.\");}\n}\n","import { engineText } from \"@ghostly/core\";\n\n/*\n * Wallets at home on another device (WISP 06 § Wallets that stay home): their database lives there, and no other\n * device opens them. The engine says which, by wallet id (`<type>:<network>`, `lightning:<network>:<card>`), before\n * any wallet opens; each wallet asks here before it opens its SDK, so no path that opens one (a start, a retry, a\n * restore, an unlock) opens a wallet that is away.\n */\n\nlet away = new Map<string, string>();\n\n/** The wallets away from this device, by wallet id, with the name of their home device (\"\" when it has none here). */\nexport function setAwayWallets(next: ReadonlyMap<string, string>): void {\n away = new Map(next);\n}\n\n/** The home device's name of a wallet away from this device, or undefined when it is here. */\nexport function awayFrom(id: string): string | undefined {\n return away.get(id);\n}\n\n/** A wallet that cannot be used here: it is at home on another device. */\nexport class WalletAwayError extends Error {\n constructor(wallet: string, device: string) {\n super(device ? engineText(\"walletAway\", { wallet, device }) : engineText(\"walletAwayUnnamed\", { wallet }));\n this.name = \"WalletAwayError\";\n }\n}\n\n/**\n * Whether a single-writer wallet (Ark, Bark, Spark and the Breez source, Fedimint) may open its SDK now: the engine\n * says, from a good turn read under 60 seconds old (WISP 06 § Wallets). Every open asks, the retries of each wallet\n * included, so a device replaced while it was offline does not reopen one on a retry.\n */\nlet turnGate: () => Promise<boolean> = async () => true;\nexport function setSingleWriterGate(gate: (() => Promise<boolean>) | null): void {\n turnGate = gate ?? (async () => true);\n}\n\n/** Throws (the wallet tries again later, as it does when its server does not answer) without a fresh good turn read. */\nexport async function requireTurn(): Promise<void> {\n if (!(await turnGate().catch(() => false))) throw new Error(\"Checking which device is active\");\n}\n\n/** Throws when the wallet `id` is at home on another device. */\nexport function refuseAway(id: string, wallet: string): void {\n const device = awayFrom(id);\n if (device !== undefined) throw new WalletAwayError(wallet, device);\n}\n","import { STORES, store, transact, wrap } from \"../../../shared/idb\";\nimport type { WalletMode } from \"../../../shared/mints\";\nimport { ModeChanged, ModeGate } from \"../modeGate\";\nimport { newDeviceKey, sealSeed, unsealSeed, type EncryptedSeed } from \"../persistence\";\nimport { requireTurn } from \"../away\";\nimport { describeProvider, networkMode, offeredIn, redact, sourceProblem, type ProviderDescriptor, type ProviderDescriptorView, type ProviderHost, type ProviderKind, type ProviderNetwork, type ProviderSettings, type SourceProblem } from \"./types\";\n\n/**\n * What is stored for a source, under `<kind>Source-<mode>` (or `<kind>Source-<mode>-<key>`, one Lightning card of\n * several) in the settings store: never in the Settings object (which the UI sees), and its secrets sealed with a\n * device key, like the Ark and USDT seeds.\n */\ninterface StoredSource {\n providerId: string;\n config: Record<string, string>;\n secrets?: EncryptedSeed;\n deviceKey?: string;\n savedAt: number;\n}\n\n/** The last balance a source read, under `<kind>SourceSeen-<mode>`: shown while it reconnects after a restart. */\ninterface SeenBalance { providerId: string; balance?: number; unconfirmed?: number; at: number }\n\nexport interface SourceView {\n mode: WalletMode;\n /** Absent: no source (on-chain has none until one is chosen). */\n providerId?: string;\n label?: string;\n /** The built-in default of this kind (the Cashu mints for Lightning). */\n isDefault?: boolean;\n custodial?: boolean;\n /** The non-secret settings, as saved. */\n config?: Record<string, string>;\n /** The names of the secret fields that are saved. Their values never leave the engine. */\n secrets?: string[];\n status: \"none\" | \"connecting\" | \"ready\" | \"error\";\n network?: ProviderNetwork;\n alias?: string;\n /** Spendable sats, when the source says. */\n balance?: number;\n /** What the source holds was read once since it connected (or it says it shows none): until then `balance` says nothing. */\n read?: true;\n /** On-chain: sats in the mempool. */\n unconfirmed?: number;\n /**\n * The balance shown is the last one read (at this time), not a fresh one: the source is not connected.\n * Absent once it reads again.\n */\n balanceAt?: number;\n /**\n * While `connecting` after a failed attempt, why it failed (it is being tried again by itself); while\n * `error`, why the source is unavailable.\n */\n error?: string;\n /** What kind of failure `error` is: no answer (tried again by itself), a setting to change, or other. */\n problem?: SourceProblem;\n /** Attempts that failed in a row, and when the next one starts (it also starts on Retry, or on waking). */\n failures?: number;\n retryAt?: number;\n /** The providers that can be picked here (this platform, this mode), in registry order. */\n offered: ProviderDescriptorView[];\n}\n\ninterface Connectable { info(): Promise<{ network: ProviderNetwork; alias?: string }>; close(): Promise<void> }\n\nexport interface SourcesOptions<P> {\n kind: ProviderKind;\n /** The network this source belongs to: Mainnet and Testnet each have their own, both connected at once. */\n network: WalletMode;\n descriptors: () => readonly ProviderDescriptor<P>[];\n /** Read each time a source connects. */\n host: () => Omit<ProviderHost, \"mode\" | \"signal\">;\n changed: () => void;\n /** Used while nothing is stored for the mode. */\n defaultId?: string;\n /** One of several sources of this kind and mode (a Lightning card): its own storage keys. Absent: the mode's one. */\n key?: string;\n /** Asked before the source of a mode changes: a refusal (money in flight through it) stops the change. */\n refuseReplacing?: (providerId: string, mode: WalletMode) => Promise<string | undefined>;\n /** Balance and the like, read after connecting and then every `refreshMs`. */\n refresh?: (provider: P) => Promise<Partial<Pick<SourceView, \"balance\" | \"unconfirmed\">>>;\n refreshMs?: number;\n /** How long to wait before the next attempt after `failures` failed ones (tests shorten it). */\n backoff?: (failures: number) => number;\n}\n\n/** Where a source is stored: the mode's one, or one of several (`key`). */\nexport const sourceKey = (kind: ProviderKind, mode: WalletMode, key?: string) => `${kind}Source-${mode}${key ? `-${key}` : \"\"}`;\nconst seenKey = (kind: ProviderKind, mode: WalletMode, key?: string) => `${kind}SourceSeen-${mode}${key ? `-${key}` : \"\"}`;\n\n/**\n * A source that does not answer is tried again by itself: 2 s, 4 s, 8 s… up to 5 minutes, ±20 % so\n * that many apps coming back online do not knock at once.\n */\nexport const RETRY_MAX_MS = 5 * 60_000;\nexport function retryDelay(failures: number, random = Math.random): number {\n const base = Math.min(RETRY_MAX_MS, 2_000 * 2 ** Math.max(0, failures - 1));\n return Math.round(base * (0.8 + 0.4 * random()));\n}\n/**\n * \"Unavailable\" only once it has failed this many times in a row for at least this long: a slow server at\n * start-up is \"Connecting…\". A wrong setting is unavailable at once (waiting does not fix it).\n */\nexport const SUSTAINED_FAILURES = 3;\nexport const SUSTAINED_MS = 30_000;\n\n/**\n * Gives up on a connection when its signal aborts (a change of settings interrupts an attempt), even if the\n * provider does not listen to it; what still arrives afterwards is closed.\n */\nfunction abortable<P extends Connectable>(work: Promise<P>, signal: AbortSignal): Promise<P> {\n return new Promise<P>((resolve, reject) => {\n const give = () => { reject(new Error(\"The connection attempt was interrupted\")); void work.then((p) => p.close().catch(() => {}), () => {}); };\n if (signal.aborted) return give();\n signal.addEventListener(\"abort\", give, { once: true });\n work.then(\n (value) => { signal.removeEventListener(\"abort\", give); if (!signal.aborted) resolve(value); },\n (error) => { signal.removeEventListener(\"abort\", give); if (!signal.aborted) reject(error); },\n );\n });\n}\n\n/**\n * The active source of one kind (Lightning, on-chain) of one network: Mainnet and Testnet each have their\n * own, both connected at once. Every change is serialized.\n */\nexport class ProviderSources<P extends Connectable> {\n private readonly mode: WalletMode;\n private stored?: StoredSource;\n private descriptor?: ProviderDescriptor<P>;\n private provider?: P;\n /** The connected source's secrets, only to blank them out of error messages. */\n private secrets: Record<string, string> = {};\n private controller = new AbortController();\n private readonly gate = new ModeGate();\n private queue: Promise<unknown> = Promise.resolve();\n private connecting?: Promise<void>;\n private timer?: ReturnType<typeof setTimeout>;\n private retry?: ReturnType<typeof setTimeout>;\n /** Failed attempts in a row, and since when: what makes a failure sustained. */\n private failures = 0;\n private failingSince?: number;\n private seen?: SeenBalance;\n private stopped = false;\n view: SourceView = { mode: \"mainnet\", status: \"none\", offered: [] };\n\n constructor(private readonly options: SourcesOptions<P>) { this.mode = options.network; this.view = { mode: options.network, status: \"none\", offered: [] }; }\n\n private serial<T>(run: () => Promise<T>): Promise<T> { const next = this.queue.then(run, run); this.queue = next.catch(() => {}); return next; }\n private find(id: string) { return this.options.descriptors().find((d) => d.id === id); }\n private offered() { return this.options.descriptors().filter((d) => offeredIn(d, this.options.host().platform, this.mode)).map((d) => describeProvider(d as ProviderDescriptor<unknown>)); }\n private async load(mode: WalletMode) {\n const settings = await store(STORES.settings, \"readonly\");\n const [stored, seen] = await Promise.all([wrap<StoredSource | undefined>(settings.get(sourceKey(this.options.kind, mode, this.options.key))), wrap<SeenBalance | undefined>(settings.get(seenKey(this.options.kind, mode, this.options.key)))]);\n this.stored = stored; this.seen = seen;\n this.failures = 0; this.failingSince = undefined;\n }\n\n /** The source of the mode (stored, else the default), not connected yet: with the last balance it read. */\n private idle(): SourceView {\n const id = this.stored?.providerId ?? this.options.defaultId;\n const descriptor = id ? this.find(id) : undefined;\n const seen = id && this.seen?.providerId === id ? this.seen : undefined;\n return {\n mode: this.mode, offered: this.offered(), providerId: id, label: descriptor?.label ?? id, custodial: descriptor?.custodial,\n isDefault: !!id && id === this.options.defaultId, config: this.stored?.config,\n secrets: descriptor?.fields.filter((f) => f.kind === \"secret\" && this.stored?.secrets).map((f) => f.name),\n status: !id ? \"none\" : descriptor ? \"connecting\" : \"error\",\n error: id && !descriptor ? `${id} is not available in this version of Ghostly` : undefined,\n ...(seen ? { balance: seen.balance, unconfirmed: seen.unconfirmed, balanceAt: seen.at } : {}),\n };\n }\n\n async start() { await this.load(this.mode); this.view = this.idle(); }\n\n /** One saved secret of the source (a handoff asks whether an LND card pins its certificate, and a Breez phrase), or undefined. */\n async secret(name: string): Promise<string | undefined> {\n const stored = this.stored;\n if (!stored?.secrets) return undefined;\n const secrets = JSON.parse(await unsealSeed(stored.secrets, stored.deviceKey ?? \"\")) as Record<string, unknown>;\n const value = secrets[name];\n return typeof value === \"string\" && value ? value : undefined;\n }\n\n /** The list of providers changed (a plugin registered): the picker is told, nothing reconnects. */\n refreshOffered() { this.view = { ...this.view, offered: this.offered() }; this.options.changed(); }\n\n /**\n * Connects the mode's source in the background, and keeps trying while it cannot be reached: \"Connecting…\"\n * (with the last balance it read) until the failure is sustained, then \"Unavailable\", still retrying with\n * backoff. `eager` (Retry, waking up): shows \"Connecting…\" again while it tries.\n */\n ensureReady(eager = false): Promise<void> {\n return this.connecting ??= this.serial(() => this.connect(eager)).finally(() => { this.connecting = undefined; });\n }\n\n /** Retry, now: the wait before the next attempt is skipped. Resolves when the attempt ends. */\n retryNow(): Promise<void> {\n if (this.provider || this.stopped) return Promise.resolve();\n clearTimeout(this.retry);\n return this.ensureReady(true);\n }\n\n /**\n * The app is back (in front, online, the computer woke): a source that is not connected is tried now, and\n * a connected one reads its balance now.\n */\n wake() {\n if (this.stopped) return;\n if (this.provider) { void this.refresh(); return; }\n if (this.failures && !this.connecting) void this.retryNow();\n }\n\n private async connect(eager = false) {\n clearTimeout(this.retry);\n if (this.stopped || this.provider) return;\n const id = this.stored?.providerId ?? this.options.defaultId;\n const descriptor = id ? this.find(id) : undefined;\n if (!id || !descriptor) { this.view = this.idle(); this.options.changed(); return; }\n if (eager && this.view.status === \"error\") { this.view = { ...this.view, status: \"connecting\", retryAt: undefined }; this.options.changed(); }\n const signal = this.controller.signal;\n let secrets: Record<string, string> = {};\n try {\n if (this.stored?.secrets) secrets = JSON.parse(await unsealSeed(this.stored.secrets, this.stored.deviceKey ?? \"\")) as Record<string, string>;\n const { provider, network, alias } = await this.open(descriptor, { config: this.stored?.config ?? {}, secrets });\n this.descriptor = descriptor; this.provider = provider; this.secrets = secrets;\n this.failures = 0; this.failingSince = undefined;\n this.view = { ...this.idle(), status: \"ready\", network, alias };\n this.options.changed();\n void this.refresh();\n } catch (error) {\n // Stopped, another mode, or interrupted by a change of settings: whoever did it connects next.\n if (this.stopped || error instanceof ModeChanged || signal.aborted) return;\n const now = Date.now();\n this.failures++;\n this.failingSince ??= now;\n const problem = sourceProblem(error);\n const sustained = problem === \"config\" || (this.failures >= SUSTAINED_FAILURES && now - this.failingSince >= SUSTAINED_MS);\n const delay = (this.options.backoff ?? retryDelay)(this.failures);\n this.view = { ...this.idle(), status: sustained ? \"error\" : \"connecting\", error: `Could not connect to ${descriptor.label}: ${redact(error, secrets)}`, problem, failures: this.failures, retryAt: now + delay };\n this.options.changed();\n this.retry = setTimeout(() => void this.ensureReady(), delay);\n }\n }\n\n /** Creates a provider and checks it is on a network of this mode before anything uses it. */\n private async open(descriptor: ProviderDescriptor<P>, settings: ProviderSettings, controller = this.controller) {\n // The Breez source shares its database with the Spark wallet of its phrase: one writer, after a fresh turn read.\n if (descriptor.id === \"breez\") await requireTurn();\n const signal = controller.signal;\n const provider = await this.gate.within(abortable(descriptor.create(settings, { ...this.options.host(), mode: this.mode, signal }), signal), (p) => p.close());\n try {\n const { network, alias } = await this.gate.within(provider.info());\n if (!descriptor.networks.includes(network)) throw new Error(`it runs on ${network}, which ${descriptor.label} does not support here`);\n if (networkMode(network) !== this.mode) throw new Error(this.mode === \"mainnet\" ? `it runs on ${network}, a test network: set it up as a Testnet wallet instead` : \"it runs on Bitcoin, real money: set it up as a Mainnet wallet instead\");\n return { provider, network, alias };\n } catch (error) { await provider.close().catch(() => {}); throw error; }\n }\n\n /**\n * Makes `providerId` this mode's source, with the values typed in its form. It is connected and its\n * network checked first; only then are its settings saved (secrets sealed) and the old source closed.\n */\n set(providerId: string, values: Record<string, string>): Promise<void> {\n const interrupted = this.interrupt();\n return this.afterChange(interrupted, this.serial(async () => {\n const { descriptor, settings } = this.settingsOf(providerId, values);\n await this.guard();\n await this.apply(descriptor, settings, false);\n }));\n }\n\n /** The form's values as a source's settings, checked: its secrets apart. Throws what to fix. */\n settingsOf(providerId: string, values: Record<string, string>): { descriptor: ProviderDescriptor<P>; settings: ProviderSettings } {\n const descriptor = this.find(providerId);\n if (!descriptor || !offeredIn(descriptor, this.options.host().platform, this.mode)) throw new Error(\"That source is not available here\");\n const settings: ProviderSettings = { config: {}, secrets: {} };\n for (const field of descriptor.fields) {\n const value = (values[field.name] ?? \"\").trim();\n if (!value && !field.optional) throw new Error(`Enter ${field.label.toLowerCase()}`);\n if (field.kind === \"select\" && value && !field.options?.some((o) => o.value === value)) throw new Error(`Choose ${field.label.toLowerCase()}`);\n if (value) (field.kind === \"secret\" ? settings.secrets : settings.config)[field.name] = value;\n }\n descriptor.validate?.(settings, this.mode);\n return { descriptor, settings };\n }\n\n /**\n * Whether the saved source is this provider with these very settings (the same wallet added twice). Its secrets are\n * unsealed here to compare, and never leave.\n */\n async holds(providerId: string, settings: ProviderSettings): Promise<boolean> {\n const stored = this.stored;\n if (!stored || stored.providerId !== providerId) return false;\n const same = (a: Record<string, string>, b: Record<string, string>) => {\n const keys = Object.keys(a).sort();\n return JSON.stringify(keys) === JSON.stringify(Object.keys(b).sort()) && keys.every((k) => a[k] === b[k]);\n };\n if (!same(stored.config, settings.config)) return false;\n const secrets = stored.secrets ? JSON.parse(await unsealSeed(stored.secrets, stored.deviceKey ?? \"\")) as Record<string, string> : {};\n return same(secrets, settings.secrets);\n }\n\n /**\n * Changes the `changeable` fields (a server address) of this mode's saved source, keeping its sealed\n * secrets and whose money it is: the wallet stays, only where it reads the chain changes. Connected and\n * checked first, like `set`; a refusal leaves the saved source as it was.\n */\n reconfigure(values: Record<string, string>): Promise<void> {\n const interrupted = this.interrupt();\n return this.afterChange(interrupted, this.serial(async () => {\n const stored = this.stored;\n const descriptor = stored ? this.find(stored.providerId) : undefined;\n if (!stored || !descriptor) throw new Error(\"There is no saved source to change\");\n const changeable = descriptor.fields.filter((f) => f.changeable);\n if (!changeable.length) throw new Error(`${descriptor.label} has no server to change`);\n const config = { ...stored.config };\n for (const field of changeable) {\n if (!(field.name in values)) continue;\n const value = (values[field.name] ?? \"\").trim();\n if (!value && !field.optional) throw new Error(`Enter ${field.label.toLowerCase()}`);\n if (value) config[field.name] = value; else delete config[field.name];\n }\n const secrets = stored.secrets ? JSON.parse(await unsealSeed(stored.secrets, stored.deviceKey ?? \"\")) as Record<string, string> : {};\n const settings: ProviderSettings = { config, secrets };\n descriptor.validate?.(settings, this.mode);\n await this.apply(descriptor, settings, true);\n }));\n }\n\n /** A change refused after interrupting an attempt: the saved source goes on connecting as before. */\n private afterChange(interrupted: boolean, change: Promise<void>): Promise<void> {\n return change.finally(() => { if (interrupted && !this.provider && !this.stopped) void this.ensureReady(); });\n }\n\n /**\n * A connection attempt under way (or waiting to try again) gives way to a change of settings, instead of\n * holding it for its time-out. Says whether there was one.\n */\n private interrupt(): boolean {\n if (this.provider || (!this.connecting && !this.failures)) return false;\n clearTimeout(this.retry);\n this.controller.abort(); this.controller = new AbortController();\n return true;\n }\n\n /** Connects `settings`, then saves them and replaces the current source. `same`: the same wallet, reconfigured. */\n private async apply(descriptor: ProviderDescriptor<P>, settings: ProviderSettings, same: boolean) {\n const providerId = descriptor.id;\n let opened: Awaited<ReturnType<typeof this.open>>;\n // Its own signal: closing the source it replaces aborts that one's, not this one's.\n const controller = new AbortController();\n try { opened = await this.open(descriptor, settings, controller); }\n catch (error) { throw error instanceof ModeChanged ? error : new Error(`Could not connect to ${descriptor.label}: ${redact(error, settings.secrets)}`); }\n const deviceKey = newDeviceKey();\n const stored: StoredSource = { providerId, config: settings.config, savedAt: Date.now(),\n ...(Object.keys(settings.secrets).length ? { secrets: await sealSeed(JSON.stringify(settings.secrets), deviceKey), deviceKey } : {}) };\n // The default with nothing to configure is what an empty mode already uses: nothing to store.\n const plain = providerId === this.options.defaultId && !Object.keys(settings.config).length && !stored.secrets;\n // Another wallet's last balance is not this one's.\n if (!same) this.seen = undefined;\n try {\n await transact([STORES.settings], (s) => {\n if (plain) s[STORES.settings].delete(sourceKey(this.options.kind, this.mode, this.options.key)); else s[STORES.settings].put(stored, sourceKey(this.options.kind, this.mode, this.options.key));\n if (!same) s[STORES.settings].delete(seenKey(this.options.kind, this.mode, this.options.key));\n });\n }\n catch (error) { await opened.provider.close().catch(() => {}); throw error; }\n await this.disconnect();\n this.controller = controller;\n this.failures = 0; this.failingSince = undefined;\n this.stored = plain ? undefined : stored; this.descriptor = descriptor; this.provider = opened.provider; this.secrets = settings.secrets;\n this.view = { ...this.idle(), status: \"ready\", network: opened.network, alias: opened.alias };\n this.options.changed();\n void this.refresh();\n }\n\n /** Forgets this mode's source (and its sealed secrets): the default, if there is one, takes over. */\n clear(): Promise<void> {\n this.interrupt();\n return this.serial(async () => {\n await this.guard();\n await transact([STORES.settings], (s) => { s[STORES.settings].delete(sourceKey(this.options.kind, this.mode, this.options.key)); s[STORES.settings].delete(seenKey(this.options.kind, this.mode, this.options.key)); });\n await this.disconnect(true);\n this.stored = undefined; this.seen = undefined;\n this.failures = 0; this.failingSince = undefined;\n this.view = this.idle(); this.options.changed();\n }).then(() => this.ensureReady());\n }\n\n /**\n * Forgets this source for good (its card is removed): its settings, sealed secrets and last balance go, it is\n * closed, and nothing takes over. Refused while a payment through it has not ended.\n */\n forget(): Promise<void> {\n this.interrupt();\n return this.serial(async () => {\n await this.guard();\n await transact([STORES.settings], (s) => { s[STORES.settings].delete(sourceKey(this.options.kind, this.mode, this.options.key)); s[STORES.settings].delete(seenKey(this.options.kind, this.mode, this.options.key)); });\n this.stopped = true;\n this.gate.close();\n await this.disconnect(true);\n this.stored = undefined; this.seen = undefined;\n this.view = { mode: this.mode, status: \"none\", offered: [] };\n });\n }\n\n private async guard() {\n const current = this.stored?.providerId ?? this.options.defaultId;\n const refusal = current ? await this.options.refuseReplacing?.(current, this.mode) : undefined;\n if (refusal) throw new Error(refusal);\n }\n\n /** The connected source, waiting for a connection under way. Throws when there is none. */\n async use(): Promise<{ provider: P; descriptor: ProviderDescriptor<P> }> {\n if (!this.provider) await this.ensureReady();\n if (!this.provider || !this.descriptor) throw new Error(this.view.status === \"none\" ? `No ${this.options.kind === \"onchain\" ? \"Bitcoin\" : \"Lightning\"} source is set up` : this.view.error ?? \"The source is still connecting\");\n return { provider: this.provider, descriptor: this.descriptor };\n }\n\n /** A change waiting on its connection gives up now (a creation that took too long); nothing is saved. */\n cutShort() { this.gate.interrupt(); }\n resume() { this.gate.resume(); }\n\n /** The connected source's id, when one is connected right now. */\n get activeId(): string | undefined { return this.provider ? this.descriptor?.id : undefined; }\n get active(): P | undefined { return this.provider; }\n\n async refresh() {\n clearTimeout(this.timer);\n const provider = this.provider;\n if (!provider) return;\n // A kind of source with nothing to read shows no balance: what it shows is read.\n if (!this.options.refresh) { if (!this.view.read) { this.view = { ...this.view, read: true }; this.options.changed(); } return; }\n try {\n const details = await this.options.refresh(provider);\n if (provider !== this.provider) return;\n this.view = { ...this.view, ...details, read: true, balanceAt: undefined, error: undefined };\n this.remember(details);\n } catch (error) {\n if (provider !== this.provider) return;\n this.view = { ...this.view, error: `Could not read the balance: ${redact(error, this.secrets)}` };\n }\n this.options.changed();\n if (this.provider) this.timer = setTimeout(() => void this.refresh(), this.options.refreshMs ?? 15_000);\n }\n\n /** Keeps the balance read, for the next start: written only when it changed. */\n private remember({ balance, unconfirmed }: Partial<Pick<SourceView, \"balance\" | \"unconfirmed\">>) {\n const providerId = this.descriptor?.id;\n // The default (the Cashu mints) connects without a network: nothing to show meanwhile.\n if (!providerId || providerId === this.options.defaultId || (balance === undefined && unconfirmed === undefined)) return;\n const was = this.seen;\n if (was?.providerId === providerId && was.balance === balance && was.unconfirmed === unconfirmed) return;\n const seen: SeenBalance = { providerId, balance, unconfirmed, at: Date.now() }, key = seenKey(this.options.kind, this.mode, this.options.key);\n this.seen = seen;\n void transact([STORES.settings], (s) => { s[STORES.settings].put(seen, key); }).catch(() => {});\n }\n\n /** `forget`: the source is removed, so what it keeps on this device goes too (a provider's own `forget`). */\n private async disconnect(forget = false) {\n clearTimeout(this.timer); clearTimeout(this.retry);\n this.controller.abort(); this.controller = new AbortController();\n const provider = this.provider as (P & { forget?: () => Promise<void> }) | undefined;\n this.provider = undefined; this.descriptor = undefined; this.secrets = {};\n await (forget && provider?.forget ? provider.forget() : provider?.close())?.catch(() => {});\n }\n\n /** Shutting down: nothing reconnects afterwards. */\n async stop() { this.stopped = true; this.gate.close(); await this.serial(() => this.disconnect()); }\n}\n","import { decodeBolt11, engineError, requestLnurlInvoice, resolveLightningDestination, type LnurlPayParams, type LnurlSuccessAction } from \"@ghostly/core\";\nimport { STORES, store, transact, wrap } from \"../../../shared/idb\";\nimport { BITCOIN_INVOICE_ON_TESTNET, type WalletMode } from \"../../../shared/mints\";\nimport { CASHU_MINT_SOURCE, CashuMintLightning } from \"./cashuMint\";\nimport type { LightningInvoice, LightningProvider, LightningProviderDescriptor } from \"./lightning\";\nimport { ProviderSources, type SourceView } from \"./sources\";\nimport { isNothingSpentError, redact, type ProviderHost } from \"./types\";\n\n/**\n * One Lightning operation, journaled under `lightningOp-<direction>-<payment hash>` in the settings store\n * BEFORE the provider is asked to spend: after a crash or a lost answer it is only ever reconciled (asked\n * about), never paid again. Incoming invoices are journaled too, so whoever paid a chat request is told\n * once the source sees it paid.\n */\nexport interface LightningOp {\n direction: \"in\" | \"out\";\n providerId: string;\n mode: WalletMode;\n paymentHash: string;\n invoice: string;\n amount: number;\n /** The chat payment request this pays, or is the invoice of. */\n paymentId?: string;\n note?: string;\n ref?: string;\n expiresAt: number;\n createdAt: number;\n /** The source books and reports this itself (the Cashu wallet): the engine does not poll it. */\n selfSettled?: boolean;\n /**\n * The Lightning card it went through (see `lightningCards.ts`). Absent on operations journaled before a network could\n * have several cards: those belong to the card that took over the network's one source (`legacyCard`).\n */\n card?: string;\n /** in: open → paid | expired. out: sending → paid | pending | failed | unknown (a lost answer). */\n state: \"open\" | \"paid\" | \"expired\" | \"sending\" | \"pending\" | \"failed\" | \"unknown\";\n maxFee?: number;\n fee?: number;\n error?: string;\n settledAt?: number;\n}\nexport type LightningOpView = Omit<LightningOp, \"ref\" | \"note\" | \"selfSettled\">;\n\n/** Options of one Lightning card's service. Without `card`, it is the network's only one (tests, older callers). */\nexport interface LightningServiceOptions {\n fetch?: typeof fetch;\n /** The card this service is: its operations carry the id, and it reads only its own. */\n card?: string;\n /** Where its source is stored: `lightningSource-<network>-<key>`; absent, the network's one key. */\n key?: string;\n /** Whether a journaled operation is this card's. Default: one carrying its id, or one carrying none. */\n owns?: (op: LightningOp) => boolean;\n}\nexport interface LightningView extends SourceView {\n capabilities?: LightningProvider[\"capabilities\"];\n /** This mode's latest operations, newest first. */\n recent: LightningOpView[];\n}\n/** `mint`: the Cashu mint that will pay, when the source is the mints; otherwise the source's id. */\nexport interface LightningQuote { quote: string; mint: string; amount: number; feeReserve: number; source: string }\n/** What a person sees of a resolved Lightning address or LNURL before choosing an amount. Amounts are whole sats. */\nexport interface LnurlView {\n id: string;\n kind: \"address\" | \"lnurl\";\n /** The address or LNURL, as normalised. */\n text: string;\n /** The host that learned of the request. */\n domain: string;\n /** The host the invoice is asked from, when it is another. */\n callbackDomain: string;\n minSat: number;\n maxSat: number;\n /** What the service says the payment is for. */\n description: string;\n /** How long a comment may be (LUD-12); 0 when none is taken. */\n commentAllowed: number;\n}\n/** Who an event is about. `mint`: the Cashu mint it went through, when it did. */\nexport interface LightningEventOp { paymentId?: string; providerId: string; mint?: string }\n\nexport interface LightningEvents {\n changed(): void;\n /** One of our invoices was paid. `paymentId`: the chat request it belongs to. */\n received(op: LightningEventOp): void;\n /** A payment left pending or unknown ended, one way or the other. */\n resolved(op: LightningEventOp, paid: boolean): void;\n}\n\nconst PREFIX = \"lightningOp-\";\nconst opKey = (direction: LightningOp[\"direction\"], hash: string) => `${PREFIX}${direction}-${hash}`;\nconst RANGE = () => IDBKeyRange.bound(PREFIX, `${PREFIX}\\uffff`);\n/** Every Lightning operation journaled, of every network and card. */\n/** Lightning payments whose call to the source has not returned, on any card of this page (WISP 06 § Payments in flight). */\nlet paying = 0;\nexport const lightningPaying = (): number => paying;\n\nexport async function readLightningJournal(): Promise<LightningOp[]> { return wrap<LightningOp[]>((await store(STORES.settings, \"readonly\")).getAll(RANGE())); }\nconst IN_POLL_MS = 4_000;\nconst OUT_POLL_MS = 15_000;\nconst RECENT = 20;\n/** Resolved Lightning addresses are kept this long for the amount to be chosen; this many at most. */\nconst LNURL_TTL_MS = 10 * 60_000;\nconst LNURL_KEPT = 20;\n/** A source that cannot tell its fee beforehand gets this ceiling, the same one chat requests are paid under. */\nexport const defaultFeeCap = (amount: number) => Math.max(10, Math.ceil(amount * 0.03));\n\n/**\n * Lightning of one network: its Lightning card, the invoice a chat request on that network carries and paying a\n * contact's invoice on it all come here, and go to this network's active source. The engine has one per network.\n */\nexport class LightningService {\n readonly sources: ProviderSources<LightningProvider>;\n private readonly quotes = new Map<string, { providerId: string; provider: LightningProvider; invoice: string; amount: number; maxFee: number; paymentHash: string }>();\n private readonly mode: WalletMode;\n private timer?: ReturnType<typeof setTimeout>;\n private passes: Promise<void> = Promise.resolve();\n private stopped = false;\n private recent: LightningOpView[] = [];\n /** Lightning addresses and LNURLs resolved, until an amount is chosen for them. */\n private readonly lnurls = new Map<string, { params: LnurlPayParams; at: number }>();\n\n constructor(readonly network: WalletMode, descriptors: () => readonly LightningProviderDescriptor[], host: () => Omit<ProviderHost, \"mode\" | \"signal\">, private readonly events: LightningEvents, defaultId?: string, private readonly options: LightningServiceOptions = {}) {\n this.mode = network;\n this.sources = new ProviderSources<LightningProvider>({\n kind: \"lightning\", network, descriptors, host, defaultId, key: options.key,\n changed: () => { this.schedule(0); events.changed(); },\n refuseReplacing: (providerId, mode) => this.refusal(providerId, mode),\n refresh: async (provider) => (provider.capabilities.balance ? { balance: (await provider.info()).balance } : {}),\n });\n }\n\n /** The card this service is, when the network has several. */\n get card(): string | undefined { return this.options.card; }\n /** Whether a journaled operation went through this card (and this network). */\n owns(op: LightningOp): boolean {\n if (op.mode !== this.mode) return false;\n return this.options.owns ? this.options.owns(op) : op.card === undefined || op.card === this.options.card;\n }\n\n get view(): LightningView {\n return { ...this.sources.view, capabilities: this.sources.active?.capabilities, recent: this.recent };\n }\n\n async start() { await this.sources.start(); await this.loadRecent(); }\n ensureReady() { return this.sources.ensureReady(); }\n async stop() { this.stopped = true; clearTimeout(this.timer); await this.sources.stop(); }\n refreshOffered() { this.sources.refreshOffered(); }\n\n // -- receiving -----------------------------------------------------------------\n\n async createInvoice(amount: number, context: { memo?: string; paymentId?: string } = {}): Promise<LightningInvoice & { source: string }> {\n const { provider, descriptor } = await this.sources.use();\n if (!provider.capabilities.receive) throw new Error(`${descriptor.label} cannot create invoices`);\n const created = await provider.createInvoice(amount, context.memo ?? \"Ghostly\");\n const decoded = decodeBolt11(created.invoice);\n // What goes to a contact must be the invoice we asked for: this amount, this hash, the right chain.\n if (!decoded || decoded.amountSat !== amount || (decoded.paymentHash && decoded.paymentHash !== created.paymentHash) || created.amount !== amount) throw new Error(`${descriptor.label} returned an invoice that does not match the request`);\n this.checkNetwork(decoded.network);\n await this.put({ direction: \"in\", providerId: descriptor.id, mode: this.mode, ...this.cardField(), paymentHash: created.paymentHash, invoice: created.invoice, amount, paymentId: context.paymentId, ref: created.ref, expiresAt: created.expiresAt, createdAt: Date.now(), selfSettled: !!provider.settlesItself, state: \"open\" });\n this.schedule(IN_POLL_MS);\n return { ...created, source: descriptor.id };\n }\n\n /** The Cashu wallet minted the ecash of one of its invoices (it polls its own quotes). */\n async reportInvoicePaid(invoice: string, context: { paymentId?: string; mint?: string } = {}) {\n const hash = decodeBolt11(invoice)?.paymentHash;\n const op = hash ? await this.get(\"in\", hash) : undefined;\n if (op && op.state !== \"paid\") await this.put({ ...op, state: \"paid\", settledAt: Date.now() });\n // Quotes made before sources carry their chat request themselves.\n this.events.received({ paymentId: op?.paymentId ?? context.paymentId, providerId: op?.providerId ?? CASHU_MINT_SOURCE, mint: context.mint });\n await this.loadRecent(); this.events.changed();\n }\n\n // -- Lightning addresses and LNURLs (LUD-16, LUD-06) -----------------------------\n\n /**\n * Fetches what a Lightning address or LNURL asks for, and keeps it until an amount is chosen. The\n * service's domain learns of the request: the caller says so before asking.\n */\n async resolveDestination(text: string): Promise<LnurlView> {\n const params = await resolveLightningDestination(text, { fetch: this.options.fetch });\n const now = Date.now();\n for (const [id, entry] of this.lnurls) if (entry.at + LNURL_TTL_MS < now) this.lnurls.delete(id);\n while (this.lnurls.size >= LNURL_KEPT) this.lnurls.delete(this.lnurls.keys().next().value!);\n const id = crypto.randomUUID();\n this.lnurls.set(id, { params, at: now });\n const { destination, callbackDomain, minSat, maxSat, description, commentAllowed } = params;\n return { id, kind: destination.kind, text: destination.text, domain: destination.domain, callbackDomain, minSat, maxSat, description, commentAllowed };\n }\n\n /**\n * The invoice for `amountSat` from a resolved destination: checked (the amount, a commitment to what\n * was shown, not expired, a network of this mode) before it is quoted and paid like any other invoice.\n */\n async destinationInvoice(id: string, amountSat: number, comment?: string): Promise<{ invoice: string; successAction?: LnurlSuccessAction; note: string }> {\n const entry = this.lnurls.get(id);\n if (!entry || entry.at + LNURL_TTL_MS < Date.now()) { this.lnurls.delete(id); throw new Error(\"That address was resolved too long ago: check it again\"); }\n const { invoice, successAction } = await requestLnurlInvoice(entry.params, amountSat, comment, { fetch: this.options.fetch });\n this.checkNetwork(invoice.network);\n return { invoice: invoice.invoice, successAction, note: entry.params.destination.text };\n }\n\n /** Whether this card resolved that address (the invoice is asked where it was resolved). */\n hasDestination(id: string) { return this.lnurls.has(id); }\n\n // -- paying --------------------------------------------------------------------\n\n /** What paying this invoice costs at most, from the active source. Nothing is spent. */\n async quote(text: string): Promise<LightningQuote> {\n const decoded = decodeBolt11(text);\n if (!decoded) throw new Error(\"That is not a Lightning invoice\");\n if (decoded.amountSat === null) throw new Error(\"Invoices without an amount are not supported\");\n if (!decoded.paymentHash) throw new Error(\"That invoice has no payment hash\");\n if (decoded.expiresAt * 1000 < Date.now()) throw engineError(\"invoiceExpired\");\n this.checkNetwork(decoded.network);\n const { provider, descriptor } = await this.sources.use();\n // A Bitcoin invoice on Testnet: only the mints can tell test sats from real ones (`fakesLightning`, checked when\n // they quote it). Any other Testnet source that pays one is a real node, and would pay it with real money.\n if (this.mode === \"testnet\" && decoded.network === \"bitcoin\" && !(provider instanceof CashuMintLightning)) throw new Error(BITCOIN_INVOICE_ON_TESTNET);\n if (!provider.capabilities.send) throw new Error(`${descriptor.label} cannot pay invoices`);\n const maxFee = provider.estimateFee ? await provider.estimateFee(decoded.invoice, decoded.amountSat) : defaultFeeCap(decoded.amountSat);\n if (!Number.isSafeInteger(maxFee) || maxFee < 0) throw new Error(`${descriptor.label} returned an invalid fee`);\n const id = crypto.randomUUID();\n this.quotes.set(id, { providerId: descriptor.id, provider, invoice: decoded.invoice, amount: decoded.amountSat, maxFee, paymentHash: decoded.paymentHash });\n const mint = provider instanceof CashuMintLightning ? provider.quoteFor(decoded.invoice)?.mint : undefined;\n return { quote: id, mint: mint ?? descriptor.id, amount: decoded.amountSat, feeReserve: maxFee, source: descriptor.id };\n }\n\n hasQuote(id: string) { return this.quotes.has(id); }\n\n /**\n * Pays a quote. True once paid; false while in flight or unknown (reconciled in the background, never\n * paid again). Throws only when nothing left the wallet.\n */\n async pay(quoteId: string, context: { note?: string; paymentId?: string } = {}): Promise<boolean> {\n const quote = this.quotes.get(quoteId);\n if (!quote) throw new Error(\"This quote is no longer valid: check the invoice again\");\n if (this.sources.active !== quote.provider) { this.quotes.delete(quoteId); throw new Error(\"The Lightning source changed: check the invoice again\"); }\n this.quotes.delete(quoteId);\n const existing = await this.get(\"out\", quote.paymentHash);\n if (existing && existing.state !== \"failed\") throw new Error(existing.state === \"paid\" ? \"This invoice is already paid\" : \"This invoice is already being paid\");\n let op: LightningOp = { direction: \"out\", providerId: quote.providerId, mode: this.mode, ...this.cardField(), paymentHash: quote.paymentHash, invoice: quote.invoice, amount: quote.amount, paymentId: context.paymentId, note: context.note?.slice(0, 140), expiresAt: 0, createdAt: Date.now(), selfSettled: !!quote.provider.settlesItself, state: \"sending\", maxFee: quote.maxFee, ref: quote.provider.paymentRef?.(quote.invoice) };\n // Written down before the provider sees it: from here on the sats may be gone.\n await this.put(op);\n paying += 1;\n try {\n const result = await quote.provider.payInvoice(quote.invoice, quote.maxFee, context.note);\n op = result.state === \"paid\" ? { ...op, state: \"paid\", fee: result.fee, ref: result.ref ?? op.ref, settledAt: Date.now() } : { ...op, state: \"pending\", ref: result.ref ?? op.ref };\n } catch (error) {\n if (isNothingSpentError(error)) { await this.put({ ...op, state: \"failed\", error: redact(error) }); throw error; }\n // A source that cannot look payments up never settles this by itself: say so rather than \"being checked\".\n op = { ...op, state: \"unknown\", error: quote.provider.capabilities.lookup ? \"No answer from the source. It is being checked; nothing is paid again.\" : \"No answer from the source, and it cannot be asked: check this payment in the wallet itself. It is never paid again.\" };\n } finally { paying -= 1; }\n await this.put(op);\n if (op.state !== \"paid\") this.schedule(OUT_POLL_MS);\n return op.state === \"paid\";\n }\n\n /** The Cashu wallet settled a melt it had left pending. */\n async reportPaymentResolved(invoice: string, paid: boolean, context: { paymentId?: string; mint?: string } = {}) {\n const hash = decodeBolt11(invoice)?.paymentHash;\n const op = hash ? await this.get(\"out\", hash) : undefined;\n if (op) await this.put({ ...op, state: paid ? \"paid\" : \"failed\", settledAt: Date.now() });\n this.events.resolved({ paymentId: op?.paymentId ?? context.paymentId, providerId: op?.providerId ?? CASHU_MINT_SOURCE, mint: context.mint }, paid);\n await this.loadRecent(); this.events.changed();\n }\n\n // -- reconciling ---------------------------------------------------------------\n\n /** Asks the active source about everything of its that is open, pending or unknown. One pass at a time. */\n reconcile(): Promise<void> {\n const pass = this.passes.then(() => this.reconcileOnce());\n this.passes = pass.catch(() => {});\n return pass;\n }\n\n private async reconcileOnce(): Promise<void> {\n if (this.stopped) return;\n let again = 0;\n try {\n const provider = this.sources.active, providerId = this.sources.activeId;\n for (const op of await this.list()) {\n if (!this.owns(op)) continue;\n if (op.selfSettled) {\n // The source settles and reports its own. One cut off before its answer was written (`recover`) it may\n // never have heard of, and would never report: the source is asked what became of it.\n if (op.direction !== \"out\" || op.state !== \"unknown\") continue;\n // No source connected yet: asked again. Another source's waits until its own is active again.\n if (!provider) { again = again || OUT_POLL_MS; continue; }\n if (op.providerId !== providerId || !provider.interruptedPayment) continue;\n try {\n const outcome = await provider.interruptedPayment({ invoice: op.invoice, paymentHash: op.paymentHash, ref: op.ref });\n if (outcome === \"pending\") again = again || OUT_POLL_MS;\n if (outcome !== \"paid\" && outcome !== \"failed\") continue;\n // Written only over the state that was asked about: the source's own report wins.\n if ((await this.get(\"out\", op.paymentHash))?.state !== \"unknown\") continue;\n await this.put({ ...op, state: outcome, error: outcome === \"failed\" ? \"The payment did not go through\" : undefined, settledAt: Date.now() });\n this.events.resolved(op, outcome === \"paid\");\n } catch { again = again || OUT_POLL_MS; }\n continue;\n }\n // `sending` is a spend under way in this process: its own answer decides. One left by a crash is\n // made `unknown` by `recover` at the next start, and only then asked about.\n const waiting = op.direction === \"in\" ? op.state === \"open\" : [\"pending\", \"unknown\"].includes(op.state);\n if (!waiting) continue;\n // Another source's operation waits until that source is active again: only it can answer.\n if (!provider || op.providerId !== providerId || !provider.capabilities.lookup) continue;\n try {\n const unchanged = async () => (await this.get(op.direction, op.paymentHash))?.state === op.state;\n if (op.direction === \"in\") {\n const status = await provider.invoiceStatus({ invoice: op.invoice, paymentHash: op.paymentHash, amount: op.amount, expiresAt: op.expiresAt, ref: op.ref });\n if (!(await unchanged())) continue;\n if (status.state === \"paid\") { await this.put({ ...op, state: \"paid\", settledAt: Date.now() }); this.events.received(op); }\n else if (status.state === \"expired\" || op.expiresAt + 60_000 < Date.now()) await this.put({ ...op, state: \"expired\" });\n else again = again ? Math.min(again, IN_POLL_MS) : IN_POLL_MS;\n } else {\n const status = await provider.paymentStatus({ invoice: op.invoice, paymentHash: op.paymentHash, ref: op.ref });\n if (status.state === \"pending\") { again = again || OUT_POLL_MS; continue; }\n // Written only over the state that was asked about: anything newer wins.\n if (!(await unchanged())) continue;\n await this.put({ ...op, state: status.state, fee: status.fee ?? op.fee, error: status.state === \"failed\" ? \"The payment did not go through\" : undefined, settledAt: Date.now() });\n this.events.resolved(op, status.state === \"paid\");\n }\n } catch {\n // The source cannot be asked right now: ask again later, nothing changes.\n again = again ? Math.min(again, op.direction === \"in\" ? IN_POLL_MS : OUT_POLL_MS) : op.direction === \"in\" ? IN_POLL_MS : OUT_POLL_MS;\n }\n }\n } finally {\n await this.loadRecent();\n this.events.changed();\n if (again) this.schedule(again);\n }\n }\n\n private schedule(ms: number) {\n if (this.stopped) return;\n clearTimeout(this.timer);\n this.timer = setTimeout(() => void this.reconcile(), ms);\n }\n\n /** Changing the source is refused while a payment through it has not ended: only it can say how. */\n private async refusal(providerId: string, mode: WalletMode) {\n const open = (await this.list()).filter((op) => op.direction === \"out\" && !op.selfSettled && op.providerId === providerId && op.mode === mode && this.owns(op) && [\"sending\", \"pending\", \"unknown\"].includes(op.state));\n return open.length ? `A Lightning payment through this source has not ended yet (${open.length}). Wait for it before changing the source.` : undefined;\n }\n\n private cardField(): Pick<LightningOp, \"card\"> { return this.options.card === undefined ? {} : { card: this.options.card }; }\n\n /** A Mainnet source refuses an invoice on a test chain. The reverse (test mints use lnbc) is `quote`'s to refuse. */\n private checkNetwork(network: string) {\n if (this.mode === \"mainnet\" && network !== \"bitcoin\") throw new Error(`That invoice is for ${network}, a test network: pay it from a Testnet wallet`);\n }\n\n private async get(direction: LightningOp[\"direction\"], hash: string) { return wrap<LightningOp | undefined>((await store(STORES.settings, \"readonly\")).get(opKey(direction, hash))); }\n private async put(op: LightningOp) { await transact([STORES.settings], (s) => { s[STORES.settings].put(op, opKey(op.direction, op.paymentHash)); }); }\n list(): Promise<LightningOp[]> { return readLightningJournal(); }\n\n /** Crash recovery: a spend journaled as `sending` never got its answer written, so its outcome is unknown. */\n async recover() {\n for (const op of await this.list()) if (op.direction === \"out\" && op.state === \"sending\" && this.owns(op)) await this.put({ ...op, state: \"unknown\", error: \"Interrupted. It is being checked; nothing is paid again.\" });\n this.schedule(0);\n }\n\n private async loadRecent() {\n this.recent = (await this.list()).filter((op) => this.owns(op)).sort((a, b) => b.createdAt - a.createdAt).slice(0, RECENT)\n .map(({ ref: _ref, note: _note, selfSettled: _self, ...view }) => view);\n }\n}\n","import { sha256 } from \"@noble/hashes/sha2.js\";\nimport { databaseName } from \"../../../shared/idb\";\n\n/**\n * Where the Breez SDK keeps a wallet on this device, and which profile it belongs to.\n *\n * The SDK keeps its IndexedDB databases under one storage name (`breezDatabaseFiles`). That name is one per **profile,\n * network and seed**: the Spark rail and a Breez Lightning card of the same phrase in one profile are one SDK instance\n * over one database (`openBreez`), two phrases are two wallets, and a profile restored as a copy on the same device\n * starts from the phrase with a database of its own. Two SDK instances over one database would each think the leaves\n * are theirs.\n *\n * Before this, the name came from the network and the seed only, so every profile of the origin holding that phrase\n * opened the same database. Such a database is kept, under its old name, for **one** profile: the first to open the\n * phrase on this device after the change claims it in a small database of the device (`ghostly-breez`), outside every\n * profile database (a backup copies a profile's database whole, so a mark in it would be the copy's too). Any other\n * profile gets a new name. The same register lists every name a profile uses, so deleting a profile deletes its Breez\n * databases without opening its sealed phrases.\n */\nexport type BreezDatabaseNetwork = \"mainnet\" | \"regtest\";\nconst netOf = (network: string): BreezDatabaseNetwork => (network === \"bitcoin\" || network === \"mainnet\" ? \"mainnet\" : \"regtest\");\nconst hex = (bytes: Uint8Array) => Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\nconst digest = (text: string) => hex(sha256(new TextEncoder().encode(text))).slice(0, 16);\n\n/** What a Breez database name looks like, old or new: nothing about the seed or the profile shows in it. */\nexport const BREEZ_DATABASE = /^ghostly-breez-(mainnet|regtest)-[0-9a-f]{16}$/;\n\n/** The name every profile of the origin used for this phrase before databases were per profile. */\nexport const breezLegacyName = (network: string, mnemonic: string) => `ghostly-breez-${netOf(network)}-${digest(`ghostly-breez:${netOf(network)}:${mnemonic}`)}`;\n\n/** The name of this phrase's Breez database in one profile (`space`: the profile's peer database, WISP 04). */\nexport const breezDatabaseName = (network: string, mnemonic: string, space: string = databaseName()) =>\n `ghostly-breez-${netOf(network)}-${digest(`ghostly-breez:${space}:${netOf(network)}:${mnemonic}`)}`;\n\n/**\n * The databases the SDK keeps under one storage name. SDK 0.26 roots them there: `<name>/<network>/<identity>` and\n * `<name>/<network>/<identity>-tree`, the identity read from the seed inside the SDK. Older builds kept `<name>`\n * itself. A browser without `indexedDB.databases()` cannot list them: only the bare names are known there.\n */\nexport async function breezDatabaseFiles(name: string): Promise<string[]> {\n const files = [name, `${name}-tree`];\n if (typeof indexedDB.databases !== \"function\") return files;\n const under = (await indexedDB.databases()).map((d) => d.name ?? \"\").filter((file) => file.startsWith(`${name}/`));\n return [...new Set([...files, ...under])];\n}\n\ninterface Entry { name: string; space: string; network: BreezDatabaseNetwork; legacy?: true }\nconst REGISTER = \"ghostly-breez\", STORE = \"databases\";\n\nconst request = <T>(r: IDBRequest<T>) => new Promise<T>((resolve, reject) => { r.onsuccess = () => resolve(r.result); r.onerror = () => reject(r.error); });\nfunction openRegister(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const open = indexedDB.open(REGISTER, 1);\n open.onupgradeneeded = () => { if (!open.result.objectStoreNames.contains(STORE)) open.result.createObjectStore(STORE); };\n open.onsuccess = () => resolve(open.result);\n open.onerror = () => reject(open.error);\n open.onblocked = () => reject(new Error(\"The Breez register is blocked\"));\n });\n}\nasync function withRegister<T>(mode: IDBTransactionMode, run: (store: IDBObjectStore) => Promise<T> | T): Promise<T> {\n const db = await openRegister();\n try {\n const tx = db.transaction(STORE, mode);\n const done = new Promise<void>((resolve, reject) => { tx.oncomplete = () => resolve(); tx.onerror = tx.onabort = () => reject(tx.error); });\n const result = await run(tx.objectStore(STORE));\n await done;\n return result;\n } finally { db.close(); }\n}\n\n/**\n * Whether the SDK keeps a wallet under this storage name on this device (`breezDatabaseFiles`). A browser that cannot\n * list databases is answered yes: the first profile then takes the old name whether or not it is there, which is\n * safe, since only one profile ever has it.\n */\nasync function exists(name: string): Promise<boolean> {\n if (typeof indexedDB.databases !== \"function\") return true;\n return (await indexedDB.databases()).some((d) => d.name === name || !!d.name?.startsWith(`${name}/`));\n}\n\n/**\n * The Breez database this profile opens for this phrase: the old shared one when this profile owns it (or is the\n * first to claim it), else the profile's own. Noted in the register either way. A device whose IndexedDB cannot be\n * read gets the profile's own name: never a database another profile may use.\n */\nexport async function breezDatabase(network: string, mnemonic: string, space: string = databaseName()): Promise<string> {\n const fresh = breezDatabaseName(network, mnemonic, space);\n if (typeof indexedDB === \"undefined\") return fresh;\n const legacy = breezLegacyName(network, mnemonic);\n try {\n const legacyHere = await exists(legacy);\n return await withRegister(\"readwrite\", async (store) => {\n // Both reads and the write in one transaction: two profiles opening at once cannot both claim the old one.\n const [owner, own] = await Promise.all([request(store.get(legacy)) as Promise<Entry | undefined>, request(store.get(fresh)) as Promise<Entry | undefined>]);\n if (owner?.space === space) return legacy;\n if (!own && !owner && legacyHere) { store.put({ name: legacy, space, network: netOf(network), legacy: true } satisfies Entry, legacy); return legacy; }\n if (!own) store.put({ name: fresh, space, network: netOf(network) } satisfies Entry, fresh);\n return fresh;\n });\n } catch { return fresh; }\n}\n\n/** The names this profile has noted for this phrase (nothing is claimed): what removing the wallet deletes. */\nexport async function breezDatabasesFor(network: string, mnemonic: string, space: string = databaseName()): Promise<string[]> {\n const names = [breezLegacyName(network, mnemonic), breezDatabaseName(network, mnemonic, space)];\n return (await breezDatabasesOf(space)).filter((name) => names.includes(name));\n}\n\n/**\n * The name this profile uses for this phrase, read without claiming anything: the one it noted (`breezDatabase`), else\n * the profile's own name. What a handoff deletes and compares (WISP 06 § Wallets): never the old shared database of\n * another profile that has not opened the phrase since the change.\n */\nexport async function breezDatabaseInUse(network: string, mnemonic: string, space: string = databaseName()): Promise<string> {\n return (await breezDatabasesFor(network, mnemonic, space))[0] ?? breezDatabaseName(network, mnemonic, space);\n}\n\n/** Every Breez database name noted for a profile of this device. */\nexport async function breezDatabasesOf(space: string): Promise<string[]> {\n if (typeof indexedDB === \"undefined\") return [];\n try { return ((await withRegister(\"readonly\", (store) => request(store.getAll()))) as Entry[]).filter((e) => e?.space === space && BREEZ_DATABASE.test(e.name)).map((e) => e.name); }\n catch { return []; }\n}\n\nconst deleteDatabase = (name: string) => new Promise<void>((resolve) => {\n try { const r = indexedDB.deleteDatabase(name); r.onsuccess = r.onerror = r.onblocked = () => resolve(); } catch { resolve(); }\n});\n\nconst deleteStrictly = (name: string) => new Promise<void>((resolve, reject) => {\n const r = indexedDB.deleteDatabase(name);\n r.onsuccess = () => resolve();\n r.onerror = () => reject(r.error ?? new Error(`${name} was not deleted`));\n r.onblocked = () => reject(new Error(`${name} is still open`));\n});\n\n/**\n * Deletes one Breez wallet's databases (all the SDK keeps under the name) and its note in the register. Anything else\n * is refused. `strict` (a handoff, WISP 06 § Wallets): a delete that is blocked or fails rejects and the note stays,\n * so the caller tries again.\n */\nexport async function dropBreezDatabase(name: string, options: { strict?: boolean } = {}): Promise<void> {\n if (!BREEZ_DATABASE.test(name) || typeof indexedDB === \"undefined\") return;\n for (const file of await breezDatabaseFiles(name).catch(() => [name, `${name}-tree`])) await (options.strict ? deleteStrictly(file) : deleteDatabase(file));\n await withRegister(\"readwrite\", (store) => { store.delete(name); }).catch(() => {});\n}\n\n/** Deletes every Breez database a profile used on this device (the profile is being deleted). */\nexport async function dropBreezDatabasesOf(space: string): Promise<void> {\n for (const name of await breezDatabasesOf(space)) await dropBreezDatabase(name);\n}\n","import { generateMnemonic, validateMnemonic } from \"@scure/bip39\";\nimport { wordlist } from \"@scure/bip39/wordlists/english.js\";\n\n/**\n * Recovery phrases (BIP 39, English) for providers whose wallet Ghostly makes: the form generates one for\n * a new wallet and checks one typed to restore. Small on purpose, so a form can use it without loading\n * the provider.\n */\nexport const newRecoveryPhrase = () => generateMnemonic(wordlist);\n/** A phrase as BIP 39 wants it, however it was typed or pasted. */\nexport const normalizePhrase = (text = \"\") => text.trim().toLowerCase().split(/\\s+/).join(\" \");\nexport const isRecoveryPhrase = (text: string) => validateMnemonic(normalizePhrase(text), wordlist);\n","import { STORES, transact } from \"../../../shared/idb\";\nimport type { WalletMode } from \"../../../shared/mints\";\nimport { CASHU_MINT_SOURCE } from \"./cashuMint\";\nimport type { LightningProviderDescriptor } from \"./lightning\";\nimport { LightningService, readLightningJournal, type LightningEvents, type LightningOp, type LightningView } from \"./lightningService\";\nimport { sourceKey } from \"./sources\";\nimport { awayFrom } from \"../away\";\nimport { breezDatabaseInUse } from \"./breezDatabases\";\nimport { normalizePhrase } from \"./recoveryPhrase\";\nimport type { ProviderHost } from \"./types\";\n\n/**\n * One Lightning card of a network, as stored under `lightningCards-<network>`. Its source (and the source's sealed\n * secrets) is stored apart, under `lightningSource-<network>-<key>`: the card that took over a network's one source\n * (`LEGACY_CARD`) has no key and keeps reading `lightningSource-<network>`, where that source always was.\n */\nexport interface StoredLightningCard {\n id: string;\n name: string;\n providerId: string;\n /** Where its source is stored. Absent: the network's one key (the card made from it). */\n key?: string;\n createdAt: number;\n}\ninterface StoredLightningCards {\n cards: StoredLightningCard[];\n /** The card a chat request's invoice and Receive come from, unless another is picked. */\n receive: string;\n}\n\n/** A Lightning card as the pages see it: its source's view, its name, and whether it is the one receiving. */\nexport interface LightningCardView extends LightningView {\n card: string;\n name: string;\n /** The network's default for receiving: chat requests and Receive use it unless another card is picked. */\n receive: boolean;\n}\n\n/** The card of Lightning through the Cashu mints: at most one per network, shown while the network has mints. */\nexport const CASHU_CARD = \"cashu\";\n/** The card made from the one source a network had before it could have several. */\nexport const LEGACY_CARD = \"main\";\nexport const cardsKey = (network: WalletMode) => `lightningCards-${network}`;\nconst NAME_MAX = 32;\n\n/** Short source names for a card's default name (\"Alby Hub (NWC)\"): the form's label otherwise. */\nconst SHORT: Record<string, string> = { nwc: \"NWC\", lnd: \"LND\", \"core-lightning\": \"Core Lightning\", breez: \"Breez\", [CASHU_MINT_SOURCE]: \"Cashu\", fedimint: \"Fedimint\", webln: \"WebLN\" };\n\n/**\n * The cards of a network, made the first time this version opens it from what an older one kept: the network's one\n * source (`lightningSource-<network>`) becomes a card reading that same key, so no secret moves and nothing asks\n * again; with no source saved, the network used the Cashu mints, and that is its card. Either becomes the default for\n * receiving. Read and written in one transaction; a second run finds the cards and changes nothing. Never reads a\n * secret: the source's record names its provider next to its sealed secrets.\n */\nexport async function loadLightningCards(network: WalletMode, label: (providerId: string) => string, now = Date.now()): Promise<StoredLightningCards> {\n let result: StoredLightningCards | undefined;\n await transact([STORES.settings], (stores) => {\n const settings = stores[STORES.settings];\n const saved = settings.get(cardsKey(network));\n saved.onsuccess = () => {\n const found = saved.result as StoredLightningCards | undefined;\n if (found?.cards?.length) { result = found; return; }\n const legacy = settings.get(sourceKey(\"lightning\", network));\n legacy.onsuccess = () => {\n const providerId = (legacy.result as { providerId?: unknown } | undefined)?.providerId;\n const card: StoredLightningCard = typeof providerId === \"string\" && providerId && providerId !== CASHU_MINT_SOURCE\n ? { id: LEGACY_CARD, name: shortName(providerId, label), providerId, createdAt: now }\n : cashuCard(now);\n result = { cards: [card], receive: card.id };\n settings.put(result, cardsKey(network));\n };\n };\n });\n return result!;\n}\n\nconst cashuCard = (now: number): StoredLightningCard => ({ id: CASHU_CARD, name: SHORT[CASHU_MINT_SOURCE], providerId: CASHU_MINT_SOURCE, key: CASHU_CARD, createdAt: now });\nconst shortName = (providerId: string, label: (providerId: string) => string) => SHORT[providerId] ?? label(providerId);\n\nexport interface LightningCardsHost {\n descriptors: () => readonly LightningProviderDescriptor[];\n host: () => Omit<ProviderHost, \"mode\" | \"signal\">;\n events: LightningEvents;\n /** Whether the network has Cashu mints: the Cashu card shows only then. */\n hasMints: () => boolean;\n fetch?: typeof fetch;\n}\n\n/**\n * The Lightning cards of one network, each with its own source, balance and journal: \"Alby Hub (NWC)\", \"Home LND\",\n * the Cashu mints. A payment goes through the card picked for it; a chat request's invoice and Receive come from the\n * default for receiving unless another is picked. Adding, removing and renaming are serialized.\n */\nexport class LightningCards {\n private stored: StoredLightningCards = { cards: [], receive: CASHU_CARD };\n private readonly services = new Map<string, LightningService>();\n private queue: Promise<unknown> = Promise.resolve();\n /** A card being added: a creation that takes too long cuts its connection short. */\n private adding?: LightningService;\n private stopped = false;\n\n constructor(readonly network: WalletMode, private readonly options: LightningCardsHost) {}\n\n private serial<T>(run: () => Promise<T>): Promise<T> { const next = this.queue.then(run, run); this.queue = next.catch(() => {}); return next; }\n private label = (providerId: string) => this.options.descriptors().find((d) => d.id === providerId)?.label ?? providerId;\n\n private make(card: StoredLightningCard): LightningService {\n return new LightningService(this.network, this.options.descriptors, this.options.host, this.options.events,\n card.providerId === CASHU_MINT_SOURCE ? CASHU_MINT_SOURCE : undefined,\n { fetch: this.options.fetch, card: card.id, key: card.key, owns: (op) => this.owner(op) === card.id });\n }\n\n private starting?: Promise<void>;\n /** Opens the cards (once: a use before the engine started opens them too). */\n start(): Promise<void> { return this.starting ??= this.open(); }\n private async open() {\n this.stored = await loadLightningCards(this.network, this.label);\n for (const card of this.stored.cards) {\n const service = this.make(card);\n this.services.set(card.id, service);\n await service.start();\n }\n }\n\n /**\n * The card an operation went through. One journaled before cards: the Cashu card's when the mints made it, else the\n * card made from the network's one source (none, when that source was replaced before cards: nobody shows it).\n */\n owner(op: LightningOp): string {\n if (op.card !== undefined) return op.card;\n return op.providerId === CASHU_MINT_SOURCE ? CASHU_CARD : LEGACY_CARD;\n }\n\n /** The cards a person sees: the Cashu card only while the network has mints. In the order they were added. */\n private shown(): StoredLightningCard[] { return this.stored.cards.filter((c) => c.id !== CASHU_CARD || this.options.hasMints()); }\n\n /** The default for receiving, or the first card shown when that one is not (the Cashu card of a network with no mints). */\n get receivingId(): string {\n const shown = this.shown();\n return shown.find((c) => c.id === this.stored.receive)?.id ?? shown[0]?.id ?? this.stored.receive;\n }\n get receiving(): LightningService { return this.services.get(this.receivingId) ?? [...this.services.values()][0]; }\n\n /** A card by its id; without one, the default for receiving. Throws when it is gone. */\n card(id?: string): LightningService {\n if (id === undefined) { if (!this.services.size) throw new Error(\"Lightning is not open yet\"); return this.receiving; }\n const service = this.services.get(id);\n if (!service) throw new Error(\"That Lightning card is gone: pick another\");\n return service;\n }\n has(id: string) { return this.services.has(id); }\n all(): LightningService[] { return [...this.services.values()]; }\n\n views(): LightningCardView[] {\n const receive = this.receivingId;\n return this.shown().filter((card) => this.services.has(card.id)).map((card) => ({ ...this.services.get(card.id)!.view, card: card.id, name: card.name, receive: card.id === receive }));\n }\n /** The default for receiving's view: what a caller from before cards reads as the network's Lightning. None before start. */\n get view(): LightningCardView | undefined {\n const card = this.stored.cards.find((c) => c.id === this.receivingId) ?? this.stored.cards[0];\n return card && this.services.has(card.id) ? { ...this.services.get(card.id)!.view, card: card.id, name: card.name, receive: true } : undefined;\n }\n\n /** The card holding this quote (each card quotes through its own source). */\n withQuote(quote: string): LightningService | undefined { return this.all().find((s) => s.hasQuote(quote)); }\n\n /**\n * Adds a card with `providerId` and the values of its form: connected and checked first, like any source; only then\n * saved. Nothing is saved when it fails. The same wallet twice (same source, same settings) is refused. A card of the\n * person's own takes receiving over from the mints' card (what a source chosen before cards did); next to another\n * card of their own, the default stays.\n */\n add(providerId: string, values: Record<string, string>): Promise<string> {\n return this.serial(async () => {\n if (providerId === CASHU_MINT_SOURCE) {\n if (this.services.has(CASHU_CARD)) throw new Error(\"Lightning through your Cashu mints is already a card\");\n if (!this.options.hasMints()) throw new Error(\"Create a Cashu wallet first: this card pays and receives through its mints\");\n const card = cashuCard(Date.now());\n const service = this.make(card);\n await service.start();\n await this.save({ ...this.stored, cards: [...this.stored.cards, card] }, card.id);\n this.services.set(card.id, service);\n void service.ensureReady();\n return card.id;\n }\n const id = `ln-${[...crypto.getRandomValues(new Uint8Array(4))].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\")}`;\n const card: StoredLightningCard = { id, name: \"\", providerId, key: id, createdAt: Date.now() };\n const service = this.make(card);\n const same = await this.findSame(providerId, values);\n if (same) throw new Error(`That wallet is already your card “${this.stored.cards.find((c) => c.id === same)?.name}”`);\n await service.start();\n this.adding = service;\n try { await service.sources.set(providerId, values); }\n catch (error) { await service.stop(); throw error; }\n finally { this.adding = undefined; }\n card.name = this.freeName(service.view.alias ? `${service.view.alias.slice(0, 20)} (${shortName(providerId, this.label)})` : shortName(providerId, this.label));\n const receive = this.receivingId === CASHU_CARD ? id : this.stored.receive;\n // The mints' card of a network with no mints is not shown and holds nothing: a card of the person's own takes its\n // place, as the one source did before cards (a Cashu wallet made later does not bring it back).\n const hidden = this.services.has(CASHU_CARD) && !this.options.hasMints();\n const cards = [...this.stored.cards.filter((c) => !hidden || c.id !== CASHU_CARD), card];\n try { await this.save({ cards, receive }, card.id); }\n catch (error) { await service.sources.forget().catch(() => {}); await service.stop(); throw error; }\n this.services.set(id, service);\n if (hidden) { const gone = this.services.get(CASHU_CARD)!; this.services.delete(CASHU_CARD); await gone.stop(); }\n return id;\n });\n }\n\n /** The card that is already this wallet (the same source with the same settings), if one is. Throws what to fix in the form. */\n async findSame(providerId: string, values: Record<string, string>): Promise<string | undefined> {\n const { settings } = this.receiving.sources.settingsOf(providerId, values);\n for (const [id, service] of this.services) if (await service.sources.holds(providerId, settings)) return id;\n return undefined;\n }\n\n /** A name no other card of the network has: \"LND\", then \"LND 2\". */\n private freeName(name: string): string {\n const taken = new Set(this.stored.cards.map((c) => c.name.toLowerCase()));\n if (!taken.has(name.toLowerCase())) return name;\n for (let n = 2; ; n++) if (!taken.has(`${name} ${n}`.toLowerCase())) return `${name} ${n}`;\n }\n\n /**\n * Removes a card: its source and sealed secrets go (refused while a payment through it has not ended). The default\n * for receiving moves to the first card of the person's own left, else the mints' card. A network left with no card gets the Cashu card back, as before cards.\n */\n remove(id: string): Promise<void> {\n return this.serial(async () => {\n const service = this.services.get(id);\n if (!service) throw new Error(\"That Lightning card is gone\");\n await service.sources.forget();\n await service.stop();\n this.services.delete(id);\n let cards = this.stored.cards.filter((c) => c.id !== id);\n let added: LightningService | undefined;\n if (!cards.length) {\n const card = cashuCard(Date.now());\n cards = [card];\n added = this.make(card);\n this.services.set(card.id, added);\n await added.start();\n }\n const shown = cards.filter((c) => c.id !== CASHU_CARD || this.options.hasMints());\n const next = shown.find((c) => c.id !== CASHU_CARD) ?? shown[0] ?? cards[0];\n const receive = this.stored.receive === id || !cards.some((c) => c.id === this.stored.receive) ? next.id : this.stored.receive;\n await this.save({ cards, receive });\n if (added) void added.ensureReady();\n });\n }\n\n /** Makes a card the network's default for receiving. */\n setReceive(id: string): Promise<void> {\n return this.serial(async () => {\n if (!this.shown().some((c) => c.id === id)) throw new Error(\"That Lightning card is gone\");\n await this.save({ ...this.stored, receive: id });\n });\n }\n\n /** Renames a card: 1 to 32 characters, unique on its network. */\n rename(id: string, name: string): Promise<void> {\n return this.serial(async () => {\n const clean = name.replace(/\\s+/g, \" \").trim();\n if (!clean) throw new Error(\"Enter a name\");\n if (clean.length > NAME_MAX) throw new Error(`At most ${NAME_MAX} characters`);\n if (!this.services.has(id)) throw new Error(\"That Lightning card is gone\");\n if (this.stored.cards.some((c) => c.id !== id && c.name.toLowerCase() === clean.toLowerCase())) throw new Error(\"Another card has that name\");\n await this.save({ ...this.stored, cards: this.stored.cards.map((c) => (c.id === id ? { ...c, name: clean } : c)) });\n });\n }\n\n private async save(next: StoredLightningCards, receiveIfNone?: string) {\n const shown = next.cards.filter((c) => c.id !== CASHU_CARD || this.options.hasMints());\n // A first card shown takes the receiving over from one that is not (the Cashu card of a network with no mints).\n const receive = receiveIfNone && !shown.some((c) => c.id === next.receive) ? receiveIfNone : next.receive;\n const stored = { cards: next.cards, receive };\n await transact([STORES.settings], (s) => { s[STORES.settings].put(stored, cardsKey(this.network)); });\n this.stored = stored;\n this.options.events.changed();\n }\n\n // -- the Cashu wallet reports what its mints settled -------------------------------\n\n private async ownerOf(direction: LightningOp[\"direction\"], invoice: string): Promise<LightningService> {\n const op = (await readLightningJournal()).find((o) => o.direction === direction && o.invoice === invoice && o.mode === this.network);\n const owner = op ? this.owner(op) : CASHU_CARD;\n return (owner && this.services.get(owner)) || this.services.get(CASHU_CARD) || this.receiving;\n }\n async reportInvoicePaid(invoice: string, context: { paymentId?: string; mint?: string } = {}) { await (await this.ownerOf(\"in\", invoice)).reportInvoicePaid(invoice, context); }\n async reportPaymentResolved(invoice: string, paid: boolean, context: { paymentId?: string; mint?: string } = {}) { await (await this.ownerOf(\"out\", invoice)).reportPaymentResolved(invoice, paid, context); }\n\n // -- Lightning addresses: resolved by one card, and the invoice asked by that same one --------\n\n resolveDestination(text: string) { return this.receiving.resolveDestination(text); }\n destinationInvoice(id: string, amountSat: number, comment?: string) { return (this.all().find((s) => s.hasDestination(id)) ?? this.receiving).destinationInvoice(id, amountSat, comment); }\n\n // -- every card at once -----------------------------------------------------------\n\n list() { return readLightningJournal(); }\n /**\n * What a handoff needs to know of the cards (WISP 06 § Wallets), from their sealed settings: the LND cards that pin a\n * certificate, and each Breez card's database on this device, by wallet id.\n */\n async handoffFacts(): Promise<{ pinned: string[]; breez: Map<string, string> }> {\n const pinned: string[] = [], breez = new Map<string, string>();\n for (const [id, service] of this.services) {\n const wallet = `lightning:${this.network}:${id}`, providerId = service.view.providerId;\n if (providerId === \"lnd\" && await service.sources.secret(\"certificate\")) pinned.push(wallet);\n if (providerId === \"breez\") {\n const mnemonic = await service.sources.secret(\"mnemonic\");\n if (mnemonic) breez.set(wallet, await breezDatabaseInUse(this.network === \"mainnet\" ? \"bitcoin\" : \"regtest\", normalizePhrase(mnemonic)));\n }\n }\n return { pinned, breez };\n }\n\n /** The cards whose source is opened on this device: not one at home on another device (WISP 06 § Wallets that stay home). */\n private here(): LightningService[] { return [...this.services].filter(([id]) => awayFrom(`lightning:${this.network}:${id}`) === undefined).map(([, s]) => s); }\n async reconcile() { await Promise.all(this.here().map((s) => s.reconcile())); }\n async recover() { for (const s of this.here()) await s.recover(); }\n async ensureReady() { await Promise.all(this.here().map((s) => s.ensureReady())); }\n refreshOffered() { for (const s of this.all()) s.refreshOffered(); }\n wake() { for (const s of this.all()) s.sources.wake(); }\n cutShort() { this.adding?.sources.cutShort(); }\n resume() { this.adding?.sources.resume(); }\n async stop() { this.stopped = true; for (const s of this.all()) await s.stop(); }\n get isStopped() { return this.stopped; }\n}\n","import { engineText } from \"@ghostly/core\";\n\n/**\n * A request's failure as a person says it, when the browser's own words say nothing: a request that timed out or was\n * given up (\"Fetch is aborted\" in Safari, \"signal timed out\" in Chromium) did not answer in time; one that never left\n * (\"Failed to fetch\", \"Load failed\") could not reach it. `host` names what was asked, when known. Anything else is\n * `null`: its own words are kept.\n */\nexport function networkReason(error: unknown, host?: string): string | null {\n const name = error instanceof Error ? error.name : \"\";\n const text = error instanceof Error ? error.message : String(error);\n if (name === \"AbortError\" || name === \"TimeoutError\" || /^(fetch is aborted|signal timed out|signal is aborted without reason|the (user|operation) (aborted a request|was aborted)\\.?|this operation was aborted|the operation timed out\\.?)$/i.test(text.trim()))\n return host ? engineText(\"hostTimedOut\", { host }) : engineText(\"networkTimedOut\");\n if (/^(failed to fetch|load failed|fetch failed|networkerror when attempting to fetch resource\\.?|network request failed)$/i.test(text.trim()))\n return host ? engineText(\"hostUnreachable\", { host }) : engineText(\"networkUnreachable\");\n return null;\n}\n\n/** A request URL's host, for a reason that names it; the URL itself when it does not parse. */\nexport function rpcHost(url: string): string {\n try { return new URL(url).host; } catch { return url; }\n}\n","import { WALLET_NETWORKS, decodeBolt11, engineText, walletNetworkOf, type PaymentMethodName, type PaymentNetworks, type WalletNetwork } from \"@ghostly/core\";\nimport { mintNetwork } from \"../../shared/mints\";\nimport { WALLET_TYPES, type NetworkWalletsView, type StoredPayment, type WalletInstanceView, type WalletType } from \"../../shared/types\";\nimport { CASHU_MINT_SOURCE } from \"./providers/cashuMint\";\nimport { CASHU_CARD, LEGACY_CARD } from \"./providers/lightningCards\";\nimport { networkLabel } from \"./modeGate\";\nimport { networkReason } from \"./networkReason\";\n\nexport const WALLET_NAMES: Record<WalletType, string> = {\n cashu: \"Cashu\", lightning: \"Lightning\", arkade: \"Ark\", bark: \"Bark\", spark: \"Spark\", bitcoin: \"Bitcoin\", fedimint: \"Fedimint\", usdt: \"USDT\",\n};\n\n/** How long a one-click creation waits on its server before it gives up, saving nothing (tests shorten it). */\nexport const createTiming = { timeoutMs: 60_000 };\n\n/**\n * The wallets a profile has, read from both networks' views: one per type and network, in the deck's order, and one\n * per Lightning card. A wallet exists once it holds something to open (a seed, a source, a mint); Lightning through\n * the Cashu mints comes with that network's Cashu wallet.\n */\nexport function walletInstances(networks: Record<WalletNetwork, NetworkWalletsView>): WalletInstanceView[] {\n const list: WalletInstanceView[] = [];\n for (const type of WALLET_TYPES) for (const network of WALLET_NETWORKS) {\n if (type === \"lightning\") { list.push(...lightningInstances(network, networks[network])); continue; }\n const config = instanceConfig(type, networks[network]);\n if (config) list.push({ id: `${type}:${network}`, type, network, config });\n }\n return list;\n}\n\n/** A network's Lightning cards (a view from before cards: its one source, as one card). */\nfunction lightningInstances(network: WalletNetwork, view: NetworkWalletsView): WalletInstanceView[] {\n const cards = view.lightnings ?? (view.lightning ? [{ ...view.lightning, card: view.lightning.providerId === CASHU_MINT_SOURCE || !view.lightning.providerId ? CASHU_CARD : LEGACY_CARD, name: \"\", receive: true }] : []);\n return cards.flatMap((ln) => {\n if (!ln.providerId || (ln.providerId === CASHU_MINT_SOURCE && !view.mints.length)) return [];\n return [{ id: `lightning:${network}:${ln.card}`, type: \"lightning\" as const, network, config: { providerId: ln.providerId, label: ln.label ?? \"\" }, card: ln.card, name: ln.name || ln.label || \"Lightning\", receive: ln.receive }];\n });\n}\n\nfunction instanceConfig(type: WalletType, view: NetworkWalletsView): Record<string, string> | undefined {\n const text = (value: unknown) => typeof value === \"string\" ? value : \"\";\n switch (type) {\n case \"cashu\": return view.mints.length ? { mint: view.mints[0].url, mints: String(view.mints.length) } : undefined;\n case \"lightning\": return undefined;\n case \"arkade\": return view.ark?.configured ? { chain: text(view.ark.network), provider: text(view.ark.provider) } : undefined;\n case \"bark\": return view.bark?.configured ? { chain: text(view.bark.network), provider: text(view.bark.provider) } : undefined;\n case \"spark\": return view.spark?.configured ? { chain: text(view.spark.network) } : undefined;\n case \"bitcoin\": return view.bitcoin?.providerId ? { providerId: view.bitcoin.providerId, label: text(view.bitcoin.label) } : undefined;\n case \"fedimint\": return view.fedimint?.federations.length ? { federations: String(view.fedimint.federations.length) } : undefined;\n case \"usdt\": return view.usdt?.configured ? { chain: text(view.usdt.network), provider: text(view.usdt.provider) } : undefined;\n }\n}\n\n/** For each way of paying, the networks this profile has a wallet on: what every chat tells its contact. */\nexport function paymentNetworksOf(wallets: readonly WalletInstanceView[]): PaymentNetworks {\n const networks: PaymentNetworks = {};\n for (const wallet of wallets) {\n const method = wallet.type as PaymentMethodName;\n networks[method] = [...new Set([...(networks[method] ?? []), wallet.network])];\n }\n return networks;\n}\n\n/**\n * The network of a payment or a request: written on it since wallets have their own, else read from what it\n * carries (a target's chain, a mint, an invoice's chain). A test mint's invoice looks like a Bitcoin one; its\n * mints say Testnet.\n */\nexport function paymentNetwork(payment: Pick<StoredPayment, \"network\" | \"target\" | \"mints\" | \"mint\" | \"invoice\">): WalletNetwork {\n if (payment.network) return payment.network;\n if (payment.target) return payment.target.method === \"cashu\" ? mintNetwork(payment.target.provider) : walletNetworkOf(payment.target.network);\n const mint = payment.mints?.[0] ?? payment.mint;\n if (mint) return mintNetwork(mint);\n const decoded = payment.invoice ? decodeBolt11(payment.invoice) : null;\n return decoded && decoded.network !== \"bitcoin\" ? \"testnet\" : \"mainnet\";\n}\n\n/** The refusal when a card of one network would pay for the other: test coins and real money never meet. */\nexport const crossNetwork = (card: WalletNetwork, asked: WalletNetwork) =>\n card === asked ? `This is a ${networkLabel(asked)} payment (${asked === \"mainnet\" ? \"real money\" : \"test coins\"}): a ${networkLabel(card)} wallet never pays it. Use a ${networkLabel(asked)} wallet.`\n : engineText(asked === \"mainnet\" ? \"mainnetPaymentOnTestnet\" : \"testnetPaymentOnMainnet\");\n\n/**\n * Real money goes out only once the person confirmed it as real money (\"Send real money\"), whatever screen asked:\n * a spend RPC on Mainnet without `confirmedReal` is refused before anything is spent. Test coins need no second step.\n */\nexport function assertConfirmedReal(network: WalletNetwork, confirmedReal: unknown): void {\n if (network === \"mainnet\" && confirmedReal !== true) throw new Error(REAL_MONEY_UNCONFIRMED);\n}\nexport const REAL_MONEY_UNCONFIRMED = engineText(\"realMoneyUnconfirmed\");\n\n/** One clear message for a creation that failed: nothing was saved, and trying again is safe. */\nexport function createFailure(label: string, error: unknown): string {\n const reason = (networkReason(error) ?? (error instanceof Error ? error.message : String(error))).replace(/\\.$/, \"\");\n return `Could not create the ${label} wallet: ${reason}. Nothing was saved; try again.`;\n}\n","import { generateMnemonic } from \"@scure/bip39\";\nimport { wordlist } from \"@scure/bip39/wordlists/english.js\";\nimport type { WalletNetwork } from \"@ghostly/core\";\nimport type { EngineState, NetworkWalletsView, PaymentView, WalletInstanceView, WalletType } from \"@ghostly/browser/shared/types\";\nimport { bool, chatOf, node, num, oneOf, sentAt, state, str, type ApiContext, type Method, type Params } from \"./apiKit\";\nimport { paymentNetwork } from \"@ghostly/browser/engine/paymentAdapters/walletInstances\";\nimport { CliError } from \"./errors\";\n\n/**\n * Wallets and payments (WISP 1100, phase 2): the app's wallet deck and a chat's payment sheet, as methods. Test\n * networks move nothing real. On Mainnet every spend passes `confirmedReal`, which the engine requires, only when the\n * call says `confirmReal: true` (the CLI's `--confirm-real`): no flag, no spend.\n */\n\nconst NETWORKS = [\"mainnet\", \"testnet\"] as const;\nconst TYPES = [\"cashu\", \"lightning\", \"arkade\", \"bark\", \"spark\", \"bitcoin\", \"fedimint\", \"usdt\"] as const;\nconst METHODS = [\"cashu\", \"arkade\", \"usdt\", \"bark\", \"bitcoin\", \"fedimint\", \"spark\"] as const;\nconst CHAT_METHODS = [\"cashu\", \"lightning\", ...METHODS.filter((m) => m !== \"cashu\")] as const;\n\n/**\n * What the app has and a headless Ghostly cannot run yet, and why (WISP 1100 § Wallet SDKs on Node). New offers\n * them as unavailable; creating one is refused before the engine is asked.\n */\nexport const NODE_GAPS: Partial<Record<WalletType, string>> = {\n bark: \"Bark's SDK ships a browser build only: its WebAssembly needs a browser window. Use it from the app.\",\n};\n\nconst network = (params: Params, fallback: WalletNetwork = \"testnet\") => oneOf(params, \"network\", NETWORKS, fallback);\n\n/** `confirmedReal` for the engine: only when the caller confirmed real money, and only then. */\nconst real = (params: Params): { confirmedReal?: true } => (bool(params, \"confirmReal\") ? { confirmedReal: true } : {});\n\nfunction netView(s: EngineState, n: WalletNetwork): NetworkWalletsView | undefined {\n return s.wallet.networks?.[n];\n}\n\n/**\n * The engine opens the federations joined before in the background as it starts. A daemon has long done so when a call\n * comes; a one-shot command waits for them (at most 30 s) before its balances are read or money moves through one.\n */\nasync function federationsOpen(ctx: ApiContext, ms = 30_000): Promise<void> {\n if (ctx.mode !== \"one-shot\") return;\n const until = Date.now() + ms;\n const connecting = () => NETWORKS.some((n) => (netView(state(ctx), n)?.fedimint?.federations ?? []).some((f) => f.status === \"connecting\"));\n while (connecting() && Date.now() < until) await new Promise((resolve) => setTimeout(resolve, 250));\n}\n\n/** One wallet of the deck with its balance, in its own unit. Never a key, a phrase or a token. */\nexport function walletJson(instance: WalletInstanceView, s: EngineState) {\n const view = netView(s, instance.network);\n let balance: number | string | null = null, unit = \"sat\", address: string | null = null, status: string | null = null, error: string | null = null;\n switch (instance.type) {\n case \"cashu\": balance = view?.balance ?? null; break;\n case \"lightning\": {\n const card = view?.lightnings?.find((c) => c.card === instance.card);\n balance = card?.balance ?? null; status = card?.status ?? null; error = card?.error ?? null;\n break;\n }\n case \"arkade\": balance = view?.ark?.balance ?? null; address = view?.ark?.address ?? null; error = view?.ark?.error ?? null; break;\n case \"bark\": balance = view?.bark?.balance ?? null; error = view?.bark?.error ?? null; break;\n case \"spark\": balance = view?.spark?.balance ?? null; address = view?.spark?.address ?? null; error = view?.spark?.error ?? null; break;\n case \"bitcoin\": balance = view?.bitcoin?.balance ?? null; address = view?.bitcoin?.address ?? null; status = view?.bitcoin?.status ?? null; error = view?.bitcoin?.error ?? null; break;\n case \"fedimint\": balance = view?.fedimint?.balance ?? null; error = view?.fedimint?.error ?? null; break;\n case \"usdt\": balance = view?.usdt?.balance ?? null; unit = \"token-base\"; address = view?.usdt?.address ?? null; error = view?.usdt?.error ?? null; break;\n }\n return {\n id: instance.id, type: instance.type, network: instance.network,\n ...(instance.card ? { card: instance.card, name: instance.name ?? null, receive: !!instance.receive } : {}),\n balance, unit, address, status, error, config: instance.config,\n };\n}\n\nexport function paymentJson(p: PaymentView) {\n return {\n id: p.id, chat: p.linkId, kind: p.kind, direction: p.direction, amount: p.amount, unit: p.unit, memo: p.memo ?? null,\n state: p.state, error: p.error ?? null, network: paymentNetwork(p), method: p.target?.method ?? (p.invoice ? \"lightning\" : p.mint || p.mints ? \"cashu\" : null),\n createdAt: p.createdAt, ...(p.group ? { group: p.group } : {}), ...(p.closed ? { closed: true } : {}), ...(p.txid ? { txid: p.txid } : {}),\n };\n}\n\n/**\n * A Lightning invoice's network by its prefix (BOLT 11), for a hint only: test mints issue `lnbc` invoices too, so\n * the paying wallet's network is always named by the caller.\n */\nexport function invoiceNetwork(invoice: string): WalletNetwork | null {\n const lower = invoice.trim().toLowerCase().replace(/^lightning:/, \"\");\n if (!lower.startsWith(\"ln\")) return null;\n if (/^lnbcrt/.test(lower)) return \"testnet\";\n if (/^lnbc\\d|^lnbc1/.test(lower)) return \"mainnet\";\n return /^ln(tb|tbs|sb)/.test(lower) ? \"testnet\" : null;\n}\n\nconst isInvoice = (text: string) => /^(lightning:)?ln(bc|tb|tbs|sb|bcrt)[0-9a-z]+$/i.test(text.trim());\nconst isLnurl = (text: string) => /^(lightning:)?lnurl[0-9a-z]+$/i.test(text.trim()) || /^[a-z0-9._+-]+@[a-z0-9.-]+\\.[a-z]{2,}$/i.test(text.trim());\n\n/**\n * The engine refusing the mint URL the person typed (not a URL, or not https): their value, so `bad_request`, as\n * `service add` says; before, it was `engine` (exit 1), which a bot reads as the engine failing and may retry. Read by\n * its shape: the engine's `EngineError` carries `engineCode`.\n */\nfunction badMintUrl(error: unknown): boolean {\n const code = error instanceof Error ? (error as { engineCode?: unknown }).engineCode : undefined;\n return code === \"invalidMintUrl\" || code === \"mintNotHttps\";\n}\n\nexport const WALLET_METHODS: Record<string, Method> = {\n async \"wallet.list\"(ctx, params) {\n await federationsOpen(ctx);\n const s = state(ctx);\n const only = params.network === undefined ? null : network(params);\n const wallets = (s.wallet.wallets ?? []).filter((w) => !only || w.network === only).map((w) => walletJson(w, s));\n const offers = (s.wallet.offers ?? []).filter((o) => !only || o.network === only).map((o) => {\n const gap = NODE_GAPS[o.type];\n return { type: o.type, network: o.network, available: o.available && !gap, reason: gap ?? o.reason ?? null, exists: !!o.exists, several: !!o.several, needs: o.needs ?? null,\n ...(o.providers ? { providers: o.providers.map((p) => ({ id: p.id, label: p.label, fields: p.fields.map((f) => ({ name: f.name, label: f.label, kind: f.kind, optional: !!f.optional })) })) } : {}) };\n });\n return { wallets, offers };\n },\n\n async \"wallet.create\"(ctx, params) {\n const type = oneOf(params, \"type\", TYPES, \"cashu\");\n const n = network(params);\n const gap = NODE_GAPS[type];\n if (gap) throw new CliError(\"unavailable\", gap);\n // A Fedimint Lightning card needs its federation open.\n await federationsOpen(ctx);\n const values: Record<string, string> = {};\n const raw = params.values ?? {};\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw) || Object.values(raw).some((v) => typeof v !== \"string\")) throw new CliError(\"bad_request\", \"values must be an object of strings\");\n Object.assign(values, raw);\n const provider = str(params, \"provider\");\n // BDK asks for a recovery phrase and makes a new wallet from one the app draws: here, one drawn now.\n if (type === \"bitcoin\" && (provider ?? \"bdk\") === \"bdk\" && !values.mnemonic) values.mnemonic = generateMnemonic(wordlist, 128);\n const made = await node(ctx).walletCreate({ type, network: n, ...(provider ? { providerId: provider } : type === \"bitcoin\" ? { providerId: \"bdk\" } : {}), ...(Object.keys(values).length ? { values } : {}), ...(str(params, \"invite\") ? { invite: str(params, \"invite\") } : {}), ...(str(params, \"apiKey\") ? { apiKey: str(params, \"apiKey\") } : {}) });\n return walletJson(made, state(ctx));\n },\n\n async \"wallet.remove\"(ctx, params) {\n const type = oneOf(params, \"type\", TYPES, \"cashu\");\n const n = network(params);\n try {\n await node(ctx).walletRemove({ type, network: n, ...(str(params, \"card\") ? { card: str(params, \"card\") } : {}), ...(bool(params, \"acceptLoss\") ? { acceptLoss: true } : {}) });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n // The engine keeps a wallet that holds money here, or that someone still pays into (#303): only the person decides.\n if (!bool(params, \"acceptLoss\") && /hold|still|awaiting|lose|backup/i.test(message)) throw new CliError(\"confirm\", `${message} (pass acceptLoss, --accept-loss, only if that is intended)`);\n throw error;\n }\n return { removed: { type, network: n, card: str(params, \"card\") ?? null } };\n },\n\n async \"wallet.faucet\"(ctx, params) {\n const type = oneOf(params, \"type\", TYPES, \"cashu\");\n if (network(params) !== \"testnet\") throw new CliError(\"refused\", \"Test coins are for Testnet wallets only\");\n return node(ctx).walletTestCoins({ type, network: \"testnet\", ...(str(params, \"card\") ? { card: str(params, \"card\") } : {}) });\n },\n\n async \"wallet.history\"(ctx, params) {\n const n = network(params);\n const limit = num(params, \"limit\", 50, { min: 1, max: 10_000, whole: true });\n await federationsOpen(ctx);\n return { network: n, history: (netView(state(ctx), n)?.history ?? []).slice(0, limit) };\n },\n\n async \"wallet.receive\"(ctx, params) {\n const amount = num(params, \"amount\", 0, { min: 1 });\n if (!amount) throw new CliError(\"bad_request\", \"amount is required\");\n const n = network(params);\n await federationsOpen(ctx);\n const invoice = await node(ctx).walletReceiveLightning({ amount, network: n, ...(str(params, \"card\") ? { card: str(params, \"card\") } : {}) });\n return { network: n, amount, invoice: invoice.invoice, expiresAt: invoice.expiresAt, paymentHash: invoice.paymentHash ?? null, source: invoice.source };\n },\n\n async \"wallet.address\"(ctx, params) {\n const type = oneOf(params, \"type\", [\"bitcoin\", \"arkade\", \"spark\", \"usdt\"] as const, \"bitcoin\");\n const n = network(params);\n if (type === \"bitcoin\") return { type, network: n, address: await node(ctx).bitcoinReceiveAddress({ network: n }) };\n const view = netView(state(ctx), n);\n const address = type === \"arkade\" ? view?.ark?.address : type === \"spark\" ? view?.spark?.address : view?.usdt?.address;\n if (!address) throw new CliError(\"unavailable\", `No ${type} wallet with an address on ${n}`);\n return { type, network: n, address };\n },\n\n async \"wallet.redeem\"(ctx, params) {\n return node(ctx).walletReceiveToken({ token: str(params, \"token\", true).trim() });\n },\n\n async \"wallet.mint.add\"(ctx, params) {\n return node(ctx).walletAddMint({ url: str(params, \"url\", true), ...(bool(params, \"primary\") ? { primary: true } : {}) }).catch((error: unknown) => {\n throw badMintUrl(error) ? new CliError(\"bad_request\", (error as Error).message) : error;\n });\n },\n\n async \"lightning.default\"(ctx, params) {\n await node(ctx).lightningSetReceive({ network: network(params), card: str(params, \"card\", true) });\n return { network: network(params), receive: str(params, \"card\", true) };\n },\n\n async \"lightning.rename\"(ctx, params) {\n await node(ctx).lightningRename({ network: network(params), card: str(params, \"card\", true), name: str(params, \"name\", true) });\n return { network: network(params), card: str(params, \"card\", true), name: str(params, \"name\", true) };\n },\n\n /** An invoice, a Lightning address or an LNURL, from the network's Lightning (a card of it, or its default). */\n async \"pay\"(ctx, params) {\n const text = str(params, \"target\", true).trim();\n const card = str(params, \"card\");\n let invoice = text.replace(/^lightning:/i, \"\");\n let n: WalletNetwork;\n let note: string | undefined;\n if (isInvoice(text)) {\n // Which wallet pays is named, never guessed from the invoice: test mints issue `lnbc` invoices too, and a\n // guess must never be what makes real money move. Testnet unless `network` says mainnet.\n n = network(params);\n } else if (isLnurl(text)) {\n n = network(params);\n const amount = num(params, \"amount\", 0, { min: 1 });\n if (!amount) throw new CliError(\"bad_request\", \"amount is required to pay a Lightning address or LNURL\");\n const view = await node(ctx).lnurlResolve({ text, network: n });\n if (amount < view.minSat || amount > view.maxSat) throw new CliError(\"refused\", `${view.domain} takes from ${view.minSat} to ${view.maxSat} sats`);\n const got = await node(ctx).lnurlInvoice({ id: view.id, amount, ...(str(params, \"comment\") ? { comment: str(params, \"comment\") } : {}), network: n });\n invoice = got.invoice; note = got.note;\n } else throw new CliError(\"bad_request\", \"Not a Lightning invoice, Lightning address or LNURL\");\n if (n === \"mainnet\" && !bool(params, \"confirmReal\")) throw new CliError(\"confirm\", \"This pays real money (Mainnet): pass --confirm-real to confirm this payment\");\n await federationsOpen(ctx);\n const quote =await node(ctx).walletQuoteInvoice({ invoice, network: n, ...(card ? { card } : {}) });\n const maxFee = params.maxFee === undefined ? null : num(params, \"maxFee\", 0);\n if (maxFee !== null && quote.feeReserve > maxFee) throw new CliError(\"refused\", `The fee may reach ${quote.feeReserve} sats, more than the ${maxFee} allowed`);\n const paid = await node(ctx).walletPayQuote({ quote: quote.quote, mint: quote.mint, ...(note ? { note } : {}), ...real(params) });\n return { network: n, amount: quote.amount, feeReserve: quote.feeReserve, source: (quote as { source?: string }).source ?? quote.mint, paid: paid.paid };\n },\n\n /** Ecash to a contact in a chat (the payment sheet's Pay). */\n async \"chat.pay\"(ctx, params) {\n const link = chatOf(ctx, params);\n const amount = num(params, \"amount\", 0, { min: 1 });\n if (!amount) throw new CliError(\"bad_request\", \"amount is required\");\n const n = network(params);\n if (n === \"mainnet\" && !bool(params, \"confirmReal\")) throw new CliError(\"confirm\", \"This pays real money (Mainnet): pass --confirm-real to confirm this payment\");\n await federationsOpen(ctx);\n const { paymentId } = await node(ctx).sendPayment({ linkId: link.id, amount, timestamp: sentAt(), network: n, ...(str(params, \"memo\") ? { memo: str(params, \"memo\") } : {}), ...real(params) });\n return { chat: link.id, paymentId };\n },\n\n /** A request in a chat (the payment sheet's Request): the contact's app pays it. */\n async \"chat.request\"(ctx, params) {\n const link = chatOf(ctx, params);\n const amount = num(params, \"amount\", 0, { min: 1 });\n if (!amount) throw new CliError(\"bad_request\", \"amount is required\");\n const method = params.method === undefined ? undefined : oneOf(params, \"method\", METHODS, \"cashu\");\n const rail = params.rail === undefined ? undefined : oneOf(params, \"rail\", [\"cashu\", \"lightning\"] as const, \"cashu\");\n await federationsOpen(ctx);\n const { paymentId } = await node(ctx).requestPayment({\n linkId: link.id, amount, timestamp: sentAt(), network: network(params),\n ...(str(params, \"memo\") ? { memo: str(params, \"memo\") } : {}), ...(method ? { method } : {}), ...(rail ? { rail } : {}), ...(str(params, \"card\") ? { card: str(params, \"card\") } : {}),\n });\n return { chat: link.id, paymentId };\n },\n\n /** Pays a request the contact made in a chat. */\n async \"chat.payRequest\"(ctx, params) {\n const link = chatOf(ctx, params);\n const paymentId = str(params, \"payment\", true);\n const request = state(ctx).payments[paymentId];\n if (!request || request.linkId !== link.id || request.kind !== \"request\") throw new CliError(\"not_found\", `No request ${paymentId} in this chat`);\n // The request says which network it is paid on: real money or test coins, never the choice of the payer.\n const n = paymentNetwork(request);\n if (n === \"mainnet\" && !bool(params, \"confirmReal\")) throw new CliError(\"confirm\", \"This request is for real money (Mainnet): pass --confirm-real to confirm this payment\", { network: n });\n const via = params.via === undefined ? undefined : oneOf(params, \"via\", [\"lightning\"] as const, \"lightning\");\n await federationsOpen(ctx);\n await node(ctx).payRequest({ linkId: link.id, paymentId, network: n, ...(via ? { via } : {}), ...(params.maxFee !== undefined ? { maxFee: num(params, \"maxFee\", 0) } : {}), ...(str(params, \"card\") ? { card: str(params, \"card\") } : {}), ...real(params) });\n return paymentJson(state(ctx).payments[paymentId] ?? request);\n },\n\n /** Which ways of paying this chat takes, per network (the chat's Accept switches). */\n async \"chat.accept\"(ctx, params) {\n const link = chatOf(ctx, params);\n const method = oneOf(params, \"method\", CHAT_METHODS, \"cashu\");\n const on = bool(params, \"on\");\n const nets = params.networks === undefined ? undefined : (() => {\n const value = params.networks;\n if (!Array.isArray(value) || !value.every((v) => v === \"mainnet\" || v === \"testnet\")) throw new CliError(\"bad_request\", \"networks must list mainnet and/or testnet\");\n return value as WalletNetwork[];\n })();\n await node(ctx).setChatPaymentMethods({ linkId: link.id, methods: { [method]: on }, ...(nets ? { networks: { [method]: nets } } : {}) });\n const now = state(ctx).links.find((l) => l.id === link.id);\n return { chat: link.id, methods: now?.paymentMethods ?? null, networks: now?.paymentNetworks ?? null };\n },\n\n async \"payment.list\"(ctx, params) {\n const chat = params.chat === undefined ? null : chatOf(ctx, params).id;\n const payments = Object.values(state(ctx).payments).filter((p) => !chat || p.linkId === chat).sort((a, b) => b.createdAt - a.createdAt).map(paymentJson);\n return { payments };\n },\n\n async \"payment.check\"(ctx, params) {\n const link = chatOf(ctx, params);\n await node(ctx).checkPayment({ linkId: link.id, paymentId: str(params, \"payment\", true) });\n return { chat: link.id, paymentId: str(params, \"payment\", true) };\n },\n\n async \"payment.reclaim\"(ctx, params) {\n await node(ctx).reclaimPayment({ paymentId: str(params, \"payment\", true) });\n const p = state(ctx).payments[str(params, \"payment\", true)];\n return p ? paymentJson(p) : { paymentId: str(params, \"payment\", true) };\n },\n};\n\nexport type { ApiContext };\n","import { groupName, inviteLink, LIMITS, MAX_GROUP_NAME_LENGTH, MENTION_EVERYONE, sanitizeTypingStatus, TYPING_KINDS, TYPING_STATUS_MAX, type GroupMention, type PairedTransport, type TypingKind } from \"@ghostly/core\";\nimport type { EngineState, GroupView, Settings, StoredMessage } from \"@ghostly/browser/shared/types\";\nimport { findSecret } from \"../../../apps/ui/src/lib/parse/secrets\";\nimport { ENGINE_METHODS, ENGINE_READS } from \"./engineMethods\";\nimport { CliError } from \"./errors\";\nimport {\n bool, chatOf, chatOrGroup, findChat, findGroup, groupOf, list, messageOf, node, num, oneOf, state, str, waitForState,\n type ApiContext, type Method, type Params,\n} from \"./apiKit\";\nimport { FILE_METHODS } from \"./files\";\nimport { waitForEdit, waitForGroupFrame, waitForMessage } from \"./waits\";\nimport { STATUS_CARD_METHODS } from \"./statusCards\";\nimport { BUTTON_METHODS, buttonsCard, pressable } from \"./buttons\";\nimport { HOLD_MAX_MINUTES, holdChat, holdOf, releaseHold } from \"./holds\";\nimport { endTyping, keepTyping, sayTyping } from \"./typing\";\nimport { GROUP_ADMIN_METHODS } from \"./groupAdmin\";\nimport { withStoredIds } from \"./turnRefs\";\nimport { IDENTITY_METHODS } from \"./identities\";\nimport { SERVICE_METHODS } from \"./services\";\nimport { BACKUP_METHODS } from \"./backup\";\nimport { CALL_METHODS } from \"./calls/api\";\nimport { WALLET_METHODS } from \"./wallets\";\nimport { chatDetailsJson, chatJson, chatMessageJson, groupJson, groupMessageJson, isLive, messageJson, type MessageJson } from \"./views\";\n\n/**\n * A text past what a chat or a group carries (16 KiB of UTF-8, as the engine counts it: trimmed) is refused before the\n * engine is asked, with a code that says it never goes as it is, not \"unavailable\", which reads as \"try later\".\n */\nfunction checkTextSize(text: string): void {\n const bytes = new TextEncoder().encode(text.trim()).length;\n if (bytes > LIMITS.maxChatMessageBytes) throw new CliError(\"bad_request\", `Message exceeds ${LIMITS.maxChatMessageBytes} UTF-8 bytes (${bytes}): shorten it, or send it as a file`, { bytes, max: LIMITS.maxChatMessageBytes });\n}\n\n// ---------- settings ----------\n\n/**\n * The engine's state as `engine getState` prints it without `--show-secret`: the settings' credentials, a group's\n * entry link (anyone who reads it can join) and a pending chat's invite code (whoever reads it can take the chat)\n * are `<hidden>`.\n */\nexport function hideStateSecrets<T extends { settings: Settings; groups?: { entryLink?: string }[]; links?: { inviteCode?: string }[] }>(state: T) {\n return {\n ...state,\n settings: redactSettings(state.settings),\n ...(state.groups ? { groups: state.groups.map((g) => (g.entryLink ? { ...g, entryLink: \"<hidden>\" } : g)) } : {}),\n ...(state.links ? { links: state.links.map((l) => (l.inviteCode ? { ...l, inviteCode: \"<hidden>\" } : l)) } : {}),\n };\n}\n\n/** Settings as a bot may read them: storage credentials and other secrets masked unless asked for. */\nexport function redactSettings(settings: Settings, showSecret = false): Record<string, unknown> {\n if (showSecret) return settings as unknown as Record<string, unknown>;\n const mask = (value: unknown, key = \"\"): unknown => {\n if (value && typeof value === \"object\" && !Array.isArray(value)) return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, mask(v, k)]));\n if (Array.isArray(value)) return value.map((item) => mask(item, key));\n if (typeof value === \"string\" && (/secret|password|token|credential|privatekey|nsec|bunker/i.test(key) || key === \"auth\" || key === \"space\")) return \"<hidden>\";\n return value;\n };\n // The push subscription (wake-up push) is a capability: who holds it can wake this profile's web app.\n const { avatar, wake, ...rest } = settings;\n return { ...(mask(rest) as Record<string, unknown>), ...(avatar ? { avatar: \"<set>\" } : {}), ...(wake ? { wake: \"<set>\" } : {}) };\n}\n\n/** Settings a command may change; the rest are the app's to manage. */\nexport const SETTABLE: Record<string, \"strings\" | \"string\" | \"boolean\" | \"ice\"> = {\n relays: \"strings\", irohRelays: \"strings\", hyperdhtRelay: \"string\", readRelays: \"boolean\",\n iceServers: \"ice\", publicProfiles: \"boolean\", online: \"boolean\", shareProfile: \"boolean\", sendTyping: \"boolean\", nick: \"string\",\n autoDownloads: \"boolean\", serveFiles: \"boolean\",\n};\n\n// ---------- transports ----------\n\nconst TRANSPORT_NAMES: Record<string, PairedTransport | \"auto\" | \"dht\"> = {\n auto: \"auto\", dht: \"dht\", webrtc: \"webrtc/1\", iroh: \"iroh/1\", hyperdht: \"hyperdht/1\",\n \"webrtc/1\": \"webrtc/1\", \"iroh/1\": \"iroh/1\", \"hyperdht/1\": \"hyperdht/1\",\n};\n\n// ---------- mentions ----------\n\n/** Code points before `index` (UTF-16) in `text`: mentions count code points (WISP 902 § Mentions). */\nconst codePoints = (text: string, index: number) => [...text.slice(0, index)].length;\n\n/**\n * Mentions for a group message: each ref (a member key, a unique prefix of one, a member's name, or `everyone`) must\n * be written in the text as `@<name>` (or `@<key prefix>`, `@everyone`); the first such place is the mention.\n */\nexport function mentionsFor(text: string, refs: readonly string[], group: GroupView): GroupMention[] {\n const out: GroupMention[] = [];\n for (const ref of refs) {\n let key: string, spelled: string[];\n if (ref === \"everyone\" || ref === MENTION_EVERYONE) { key = MENTION_EVERYONE; spelled = [\"everyone\", \"all\"]; }\n else {\n const lower = ref.toLowerCase();\n // A member's key is that member, whatever another member calls itself.\n const byKey = group.members.filter((m) => !m.me && m.key === ref);\n const matches = byKey.length ? byKey : group.members.filter((m) => !m.me && (m.key.startsWith(ref) || m.nick?.trim().toLowerCase() === lower));\n if (matches.length !== 1) throw new CliError(matches.length ? \"bad_request\" : \"not_found\", matches.length ? `${JSON.stringify(ref)} names more than one member` : `No member ${JSON.stringify(ref)} in ${group.name}`);\n key = matches[0].key;\n spelled = [matches[0].nick?.trim(), ref, key.slice(0, 8)].filter((s): s is string => !!s);\n }\n const found = spelled.map((name) => ({ name, index: text.toLowerCase().indexOf(\"@\" + name.toLowerCase()) })).find((f) => f.index >= 0);\n if (!found) throw new CliError(\"bad_request\", `Write @${spelled[0]} in the text to mention ${ref}`);\n out.push({ k: key, o: codePoints(text, found.index), l: [...\"@\" + found.name].length });\n }\n return out;\n}\n\n/** Per host: stops watching the invite it marked as the chat on screen. */\nconst inviteShown = new WeakMap<ApiContext, () => void>();\n\n/**\n * Marks a new invite as the chat on screen (native listeners, the quick pace) only until it is paired: a paired chat\n * keeps its listeners anyway, and a daemon nobody looks at reads an away contact at the background pace, as its\n * other chats and the same chat after a restart do.\n */\nfunction showUntilPaired(ctx: ApiContext, linkId: string): void {\n inviteShown.get(ctx)?.();\n node(ctx).setActiveLink({ linkId });\n // Gone only once a state had it: one pushed before the invite was made does not end it.\n let seen = false;\n const done = (s: EngineState) => {\n const link = s.links.find((l) => l.id === linkId);\n if (link) seen = true;\n return link ? !!link.peerParticipationKey : seen;\n };\n const stop = () => { off(); if (inviteShown.get(ctx) === stop) inviteShown.delete(ctx); };\n const off = ctx.hub.onState((s) => {\n if (!done(s)) return;\n stop();\n node(ctx).setActiveLink({ linkId: null });\n });\n inviteShown.set(ctx, stop);\n}\n\n// ---------- methods ----------\n\nconst METHODS: Record<string, Method> = {\n ...WALLET_METHODS,\n ...FILE_METHODS,\n ...STATUS_CARD_METHODS,\n ...BUTTON_METHODS,\n ...GROUP_ADMIN_METHODS,\n ...IDENTITY_METHODS,\n ...SERVICE_METHODS,\n ...BACKUP_METHODS,\n ...CALL_METHODS,\n\n async status(ctx) {\n const s = state(ctx);\n return {\n version: ctx.version, profile: ctx.runtime.paths.name, mode: ctx.mode, pid: process.pid,\n online: s.settings.online, name: s.settings.nick || null, webrtc: ctx.runtime.webrtc, calls: ctx.runtime.callsUnavailable === null,\n chats: s.links.length, live: s.links.filter(isLive).length, groups: s.groups.length,\n discovery: { protocol: s.transport.protocol, relays: s.transport.relays },\n events: { lastSeq: ctx.hub.lastSeq },\n };\n },\n\n async \"profile.get\"(ctx) {\n const { settings } = state(ctx);\n return { profile: ctx.runtime.paths.name, name: settings.nick || null, shareProfile: settings.shareProfile !== false, picture: !!settings.avatar, online: settings.online };\n },\n async \"profile.set\"(ctx, params) {\n const patch: Partial<Settings> = {};\n const name = params.name;\n if (name !== undefined) {\n if (typeof name !== \"string\" || [...name].length > 64) throw new CliError(\"bad_request\", \"name must be a string of up to 64 characters\");\n patch.nick = name.trim();\n }\n if (params.shareProfile !== undefined) patch.shareProfile = bool(params, \"shareProfile\");\n await node(ctx).updateSettings({ settings: patch });\n return METHODS[\"profile.get\"](ctx, {});\n },\n\n async \"settings.get\"(ctx, params) {\n return redactSettings(state(ctx).settings, bool(params, \"showSecret\"));\n },\n async \"settings.set\"(ctx, params) {\n const patch: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(params)) {\n // The table's own keys only: every object has `constructor` and `toString`, which are no settings.\n const kind = Object.hasOwn(SETTABLE, key) ? SETTABLE[key] : undefined;\n if (!kind) throw new CliError(\"bad_request\", `${key} is not a setting the CLI changes (${Object.keys(SETTABLE).join(\", \")})`);\n if (kind === \"strings\" && !(Array.isArray(value) && value.every((v) => typeof v === \"string\"))) throw new CliError(\"bad_request\", `${key} must be a list of strings`);\n if (kind === \"string\" && typeof value !== \"string\") throw new CliError(\"bad_request\", `${key} must be a string`);\n if (kind === \"boolean\" && typeof value !== \"boolean\") throw new CliError(\"bad_request\", `${key} must be true or false`);\n if (kind === \"ice\" && !(Array.isArray(value) && value.every((v) => v && typeof v === \"object\"))) throw new CliError(\"bad_request\", `${key} must be a list of ICE servers`);\n patch[key] = value;\n }\n await node(ctx).updateSettings({ settings: patch });\n return redactSettings(state(ctx).settings);\n },\n\n async \"invite.create\"(ctx, params) {\n const label = str(params, \"label\");\n const { linkId, inviteCode } = await node(ctx).createLink();\n if (label) await node(ctx).renameLink({ linkId, label });\n // Native listeners start for the chat on screen (or a paired one): a new invite is the chat a bot waits on.\n showUntilPaired(ctx, linkId);\n // Its first records must be out before a one-shot leaves. A daemon publishes them later too: it answers at once when\n // the relays' request budget holds them back (a few invites in a minute), and says when they go.\n const progressOf = (s: EngineState) => s.links.find((l) => l.id === linkId)?.pairingProgress;\n const progress = await waitForState(ctx, (s) => { const p = progressOf(s); return p && (p.stage !== \"publishing\" || (ctx.mode === \"daemon\" && p.relayWaitUntil)) ? p : undefined; }, 20_000, \"the invite to be published\")\n .catch(() => progressOf(state(ctx)));\n const published = !!progress && progress.stage !== \"publishing\" && progress.reason !== \"publish\";\n const waitUntil = published ? undefined : progress?.relayWaitUntil;\n return { chat: linkId, invite: inviteCode, link: inviteLink(inviteCode), published, ...(waitUntil !== undefined && { retryInMs: Math.max(0, waitUntil - Date.now()) }) };\n },\n async \"invite.join\"(ctx, params) {\n let invite = str(params, \"invite\", true).trim();\n const hash = invite.indexOf(\"#\");\n if (/^https?:\\/\\//i.test(invite) && hash !== -1) invite = invite.slice(hash + 1);\n let linkId: string;\n try {\n ({ linkId } = await node(ctx).joinLink({ inviteCode: invite }));\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (/own invite/i.test(message)) throw new CliError(\"refused\", message);\n if (/does not look like/i.test(message)) throw new CliError(\"bad_request\", message);\n throw error;\n }\n const label = str(params, \"label\");\n if (label) await node(ctx).renameLink({ linkId, label });\n showUntilPaired(ctx, linkId);\n return { chat: linkId };\n },\n\n async \"chat.list\"(ctx, params) {\n const showSecret = bool(params, \"showSecret\");\n return { chats: state(ctx).links.map((link) => chatJson(link, showSecret)).sort((a, b) => b.lastMessageAt - a.lastMessageAt || b.createdAt - a.createdAt) };\n },\n async \"chat.get\"(ctx, params) {\n const link = chatOf(ctx, params);\n return { ...chatDetailsJson(link, bool(params, \"showSecret\")), heldUntil: holdOf(ctx, link.id) };\n },\n async \"chat.history\"(ctx, params) {\n const link = chatOf(ctx, params);\n return historyOf(ctx, link.id, params, (m) => chatMessageJson(m, link));\n },\n async \"chat.send\"(ctx, params) {\n const link = chatOf(ctx, params);\n const text = str(params, \"text\", true);\n checkTextSize(text);\n if (!bool(params, \"force\")) {\n const secret = findSecret(text);\n if (secret) throw new CliError(\"confirm\", `The text looks like ${secret.kind === \"cashu\" ? \"a Cashu token (money anyone who reads it can take)\" : \"a secret (a seed or a private key)\"}; send it with --force if you mean to`, { kind: secret.kind });\n }\n const wait = oneOf(params, \"wait\", [\"none\", \"sent\", \"delivered\"] as const, \"none\");\n const replyTo = str(params, \"reply\");\n // Buttons under the text (WISP 406 · Message Buttons): the text is the question, and what older apps show.\n const card = buttonsCard(params);\n // A kept `typing --for` ends with the message (the engine says stop with it).\n endTyping(ctx, { linkId: link.id }, false);\n const result = await node(ctx).sendMessage({ linkId: link.id, text, ...(replyTo ? { replyTo } : {}), ...(card ? { card } : {}) });\n if (result.error) throw new CliError(result.refused ? \"refused\" : \"unavailable\", result.error);\n if (!result.messageId) throw new CliError(\"bad_request\", \"Nothing to send\");\n let message = await node(ctx).getMessage(link.id, result.messageId);\n if (wait !== \"none\") message = await waitForMessage(ctx, link.id, result.messageId, wait, num(params, \"timeout\", 30, { min: 1, max: 3600 }) * 1000);\n return { chat: link.id, messageId: result.messageId, delivery: message?.delivery ?? null, ...(card ? { buttons: card.id, card, pressable: pressable(link) } : {}) };\n },\n /**\n * WISP 400 § Edits: the whole new text of one of my texts in a 1:1 chat. It shows here at once and reaches the contact\n * once the chat is live and both apps offer edit/1; `--wait confirmed` waits for the contact to confirm it.\n */\n async \"chat.edit\"(ctx, params) {\n const link = chatOf(ctx, params);\n const messageId = str(params, \"message\", true);\n const text = str(params, \"text\", true);\n if (!bool(params, \"force\")) {\n const secret = findSecret(text);\n if (secret) throw new CliError(\"confirm\", `The text looks like ${secret.kind === \"cashu\" ? \"a Cashu token (money anyone who reads it can take)\" : \"a secret (a seed or a private key)\"}; edit with --force if you mean to`, { kind: secret.kind });\n }\n const wait = oneOf(params, \"wait\", [\"none\", \"confirmed\"] as const, \"none\");\n const result = await node(ctx).editMessage({ linkId: link.id, messageId, text });\n if (result.error) throw editRefused(await messageOf(ctx, link.id, messageId), messageId, \"chat\", result.error, result.refused);\n const id = result.messageId ?? messageId;\n let message = await node(ctx).getMessage(link.id, id);\n if (wait === \"confirmed\" && message?.edit?.pending) message = await waitForEdit(ctx, link.id, id, num(params, \"timeout\", 30, { min: 1, max: 3600 }) * 1000);\n return { chat: link.id, messageId: id, edits: message?.edit?.seq ?? 0, confirmed: !!message && !message.edit?.pending };\n },\n /**\n * WISP 401 § Typing: said on the live session only, and a start only while `sendTyping` is on; it holds 6 s there.\n * `kind` (typing, recording, thinking) and `status` (a short line, e.g. \"Transcribing your audio…\") say what this\n * side is doing; a new one reaches the contact at once.\n * `for`: kept that many seconds (a one-shot stays that long), until a message to the chat or a stop.\n */\n async \"chat.typing\"(ctx, params) {\n const link = chatOf(ctx, params);\n const typing = !bool(params, \"stop\");\n const { kind, status } = typingWord(params, typing);\n const word = { ...(kind !== \"typing\" ? { kind } : {}), ...(status ? { status } : {}) };\n const seconds = params.for === undefined ? 0 : num(params, \"for\", 0, { min: 1, max: 600 });\n if (!typing && seconds) throw new CliError(\"bad_request\", \"for keeps typing on: not with stop\");\n endTyping(ctx, { linkId: link.id }, false);\n node(ctx).setTyping({ linkId: link.id, typing, ...word });\n const until = seconds ? Date.now() + seconds * 1000 : null;\n const kept = seconds ? keepTyping(ctx, { linkId: link.id }, seconds * 1000, word) : null;\n if (kept && ctx.mode === \"one-shot\") await kept;\n return {\n chat: link.id, typing, ...(typing ? { kind } : {}), ...(status ? { status } : {}),\n live: link.dataLink === \"open\", sendTyping: state(ctx).settings.sendTyping !== false, ...(until ? { until } : {}),\n };\n },\n /** WISP 400 § Reactions: one emoji per person per message, a new one replaces mine; `remove` takes mine back. */\n async \"chat.react\"(ctx, params) {\n const link = notAGroup(ctx, params, () => chatOf(ctx, params), \"group react\", \"<message> <emoji>\");\n return { chat: link.id, ...(await react(ctx, link.id, params)) };\n },\n /**\n * WISP 400 § Pinned message: one per chat or group, a new pin replaces it; `remove` unpins. In a community only the\n * admin pins. Answers the chat (or group) and the message now pinned (null once unpinned).\n */\n async \"chat.pin\"(ctx, params) {\n const target = chatOrGroup(ctx, str(params, \"chat\", true));\n const remove = bool(params, \"remove\"), messageId = remove ? str(params, \"message\") : str(params, \"message\", true);\n const result = await node(ctx).pinMessage({ linkId: target.linkId, ...(messageId && { messageId }), remove });\n if (result.error) throw new CliError(/not in this chat|Nothing is pinned/.test(result.error) ? \"not_found\" : /Only the admin/.test(result.error) ? \"refused\" : \"bad_request\", result.error);\n return { [target.group ? \"group\" : \"chat\"]: target.id, messageId: remove ? null : messageId, removed: remove };\n },\n /**\n * WISP 400 § Forwards: messages of a chat or a group sent on to up to 5 chats and groups, each as a new message of mine\n * that says it was forwarded (one hop more) and nothing of who wrote it. A file goes from the bytes here, never\n * fetched again; a group takes texts only. `wait: sent`: each text until it is on its way (a group's until an edge\n * took it), each file until its transfer is done. What a chat refused comes back per chat; any refusal fails the call.\n */\n async \"chat.forward\"(ctx, params) {\n const from = chatOrGroup(ctx, str(params, \"chat\", true));\n const messageIds = list(params, \"messages\");\n if (!messageIds.length) throw new CliError(\"bad_request\", \"Name the messages to forward (their ids, from history or an event)\");\n const to = list(params, \"to\").map((ref) => chatOrGroup(ctx, ref));\n if (!to.length) throw new CliError(\"bad_request\", \"Forward to which chat? Name one or more with --to\");\n // Each by its id here, one row each: never the whole history of the chat or group.\n const chosen: StoredMessage[] = [];\n for (const id of messageIds) {\n const message = await node(ctx).getMessage(from.linkId, id);\n if (!message) throw new CliError(\"not_found\", `No message ${id} in this ${from.group ? \"group\" : \"chat\"}`);\n chosen.push(message);\n }\n if (!bool(params, \"force\")) for (const message of chosen) {\n const secret = message.file ? null : findSecret(message.text);\n if (secret) throw new CliError(\"confirm\", `A message looks like ${secret.kind === \"cashu\" ? \"a Cashu token (money anyone who reads it can take)\" : \"a secret (a seed or a private key)\"}; forward it with --force if you mean to`, { kind: secret.kind, messageId: message.id });\n }\n const wait = oneOf(params, \"wait\", [\"none\", \"sent\"] as const, \"none\");\n let results;\n try { ({ results } = await node(ctx).forwardMessages({ linkId: from.linkId, messageIds, to: to.map((t) => t.linkId) })); }\n catch (error) { throw new CliError(\"refused\", error instanceof Error ? error.message : String(error)); }\n const ms = num(params, \"timeout\", 30, { min: 1, max: 3600 }) * 1000;\n const out = results.map((result, i) => ({ to: to[i]!.id, kind: to[i]!.group ? \"group\" as const : \"chat\" as const, messageIds: result.messageIds, error: result.error }));\n if (wait === \"sent\") for (const [i, result] of out.entries()) for (const messageId of result.messageIds) {\n if (to[i]!.group) { await waitForGroupFrame(ctx, to[i]!.id, messageId, undefined, ms); continue; }\n const sent = await node(ctx).getMessage(to[i]!.id, messageId);\n if (sent?.file) await FILE_METHODS[\"file.wait\"](ctx, { chat: to[i]!.id, file: sent.file.id, timeout: ms / 1000 });\n else await waitForMessage(ctx, to[i]!.id, messageId, \"sent\", ms);\n }\n const failed = out.filter((r) => r.error);\n if (failed.length) throw new CliError(failed.length === out.length ? \"refused\" : \"engine\", failed.map((r) => `${r.to}: ${r.error}`).join(\"; \"), { from: from.id, results: out });\n return { from: from.id, results: out };\n },\n async \"chat.retry\"(ctx, params) {\n const link = chatOf(ctx, params);\n const messageId = str(params, \"message\", true);\n if (!(await node(ctx).getMessage(link.id, messageId))) throw new CliError(\"not_found\", `No message ${messageId} in this chat`);\n await node(ctx).retryMessage({ linkId: link.id, messageId });\n return { chat: link.id, messageId };\n },\n async \"chat.delete\"(ctx, params) {\n const link = chatOf(ctx, params);\n const messageId = str(params, \"message\", true);\n if (!(await node(ctx).getMessage(link.id, messageId))) throw new CliError(\"not_found\", `No message ${messageId} in this chat`);\n await node(ctx).deleteMessage({ linkId: link.id, messageId });\n return { chat: link.id, deleted: messageId };\n },\n async \"chat.details\"(ctx, params) {\n const link = chatOf(ctx, params);\n const details = await node(ctx).messageDetails({ linkId: link.id, messageId: str(params, \"message\", true) });\n if (!details) throw new CliError(\"not_found\", `No message ${String(params.message)} in this chat`);\n return details;\n },\n async \"chat.rename\"(ctx, params) {\n const link = chatOf(ctx, params);\n await node(ctx).renameLink({ linkId: link.id, label: str(params, \"name\") ?? \"\" });\n return chatJson(findChat(state(ctx).links, link.id));\n },\n async \"chat.remove\"(ctx, params) {\n const link = chatOf(ctx, params);\n if (!bool(params, \"yes\")) throw new CliError(\"confirm\", \"Removing a chat deletes its keys and history on this device; pass yes (--yes) to go on\");\n await node(ctx).removeLink({ linkId: link.id });\n return { removed: link.id };\n },\n async \"chat.transport\"(ctx, params) {\n const link = chatOf(ctx, params);\n const name = str(params, \"transport\", true);\n const transport = Object.hasOwn(TRANSPORT_NAMES, name) ? TRANSPORT_NAMES[name] : undefined;\n if (!transport) throw new CliError(\"bad_request\", `transport must be one of auto, dht, webrtc, iroh, hyperdht`);\n await node(ctx).setChatTransport({ linkId: link.id, transport });\n return chatDetailsJson(findChat(state(ctx).links, link.id));\n },\n /** Reconnects now; a chat held off its direct link (`chat disconnect --hold`) leaves the hold first. */\n async \"chat.connect\"(ctx, params) {\n const link = chatOf(ctx, params);\n await releaseHold(ctx, link.id, \"connect\");\n await node(ctx).connect({ linkId: link.id });\n return chatJson(findChat(state(ctx).links, link.id));\n },\n /**\n * Closes the live session; the contact's app may dial again at once. `hold` (minutes): the chat stays off its\n * direct link that long, on the DHT (its \"DHT only\" choice, taken back after); 0 ends a hold now.\n */\n async \"chat.disconnect\"(ctx, params) {\n const link = chatOf(ctx, params);\n if (params.hold === undefined) {\n node(ctx).disconnect({ linkId: link.id });\n return chatJson(findChat(state(ctx).links, link.id));\n }\n const minutes = num(params, \"hold\", 0, { min: 0, max: HOLD_MAX_MINUTES });\n if (minutes === 0) await releaseHold(ctx, link.id, \"lifted\");\n else await holdChat(ctx, link, minutes);\n return { ...chatJson(findChat(state(ctx).links, link.id)), heldUntil: holdOf(ctx, link.id) };\n },\n async \"chat.verify\"(ctx, params) {\n const link = chatOf(ctx, params);\n const code = str(params, \"code\", true).replace(/\\s+/g, \"\").toLowerCase();\n const mine = link.pairing?.code?.replace(/\\s+/g, \"\").toLowerCase();\n if (!mine) throw new CliError(\"unavailable\", \"This chat shows no comparison code yet (it needs a live session)\");\n if (code !== mine) throw new CliError(\"refused\", \"The code does not match the one this side shows: do not trust this chat until you know why\");\n await node(ctx).confirmPair({ linkId: link.id, code: link.pairing!.code! });\n return chatJson(findChat(state(ctx).links, link.id));\n },\n async \"chat.wait\"(ctx, params) {\n const link = chatOf(ctx, params);\n const until = oneOf(params, \"until\", [\"live\", \"text\", \"paired\"] as const, \"live\");\n const ms = num(params, \"timeout\", 60, { min: 1, max: 86_400 }) * 1000;\n const view = await waitForState(ctx, (s) => {\n const now = s.links.find((l) => l.id === link.id);\n if (!now) throw new CliError(\"not_found\", `Chat ${link.id} was removed`);\n // Live: a session that exists now, never one remembered from the last run (`isLive`).\n const ok = until === \"live\" ? isLive(now)\n : until === \"text\" ? now.textDelivery === \"stream\" || now.textDelivery === \"dht\" || now.textDelivery === \"hold\"\n : !!now.pairingProgress?.peerSeen || now.pairing?.status === \"ready\" || isLive(now);\n return ok ? now : undefined;\n }, ms, `chat ${link.id} to be ${until}`);\n return chatJson(view);\n },\n\n async \"group.create\"(ctx, params) {\n // As `group rename` checks it: a name a group cannot have is said here, before anything is made.\n const name = groupName(str(params, \"name\", true));\n if (!name) throw new CliError(\"bad_request\", `A group's name is 1 to ${MAX_GROUP_NAME_LENGTH} characters on one line`);\n const profile = oneOf(params, \"profile\", [\"community\", \"mesh\"] as const, \"community\");\n const { groupId } = await node(ctx).createGroup({ name, profile });\n let link: string | null = null;\n if (profile === \"community\") ({ link } = await node(ctx).enableGroupLink({ groupId }));\n return { group: groupId, link };\n },\n async \"group.join\"(ctx, params) {\n let groupId: string;\n try {\n ({ groupId } = await node(ctx).joinGroupByLink({ link: str(params, \"link\", true) }));\n } catch (error) {\n // Not a link to a group at all: the caller's input, as `invite join` answers a word that is no invite.\n if (error instanceof Error && error.message === \"This is not a link to a group\") throw new CliError(\"bad_request\", error.message);\n throw error;\n }\n return { group: groupId };\n },\n async \"group.list\"(ctx, params) {\n const showSecret = bool(params, \"showSecret\");\n return { groups: state(ctx).groups.map((g) => groupJson(g, showSecret)).sort((a, b) => b.lastMessageAt - a.lastMessageAt) };\n },\n async \"group.get\"(ctx, params) {\n return groupJson(groupOf(ctx, params), bool(params, \"showSecret\"));\n },\n async \"group.history\"(ctx, params) {\n const group = groupOf(ctx, params);\n return historyOf(ctx, `group:${group.id}`, params, (m) => groupMessageJson(m, group));\n },\n async \"group.send\"(ctx, params) {\n const group = groupOf(ctx, params);\n const text = str(params, \"text\", true);\n checkTextSize(text);\n if (!bool(params, \"force\")) {\n const secret = findSecret(text);\n if (secret) throw new CliError(\"confirm\", \"The text looks like a secret or a Cashu token; send it with --force if you mean to\", { kind: secret.kind });\n }\n const mentions = mentionsFor(text, list(params, \"mentions\"), group);\n const replyTo = str(params, \"reply\");\n const wait = oneOf(params, \"wait\", [\"none\", \"sent\"] as const, \"none\");\n const card = buttonsCard(params);\n // A kept `group typing --for` ends with the message (the engine says stop with it).\n endTyping(ctx, { groupId: group.id }, false);\n const result = await node(ctx).sendGroupMessage({ groupId: group.id, text, ...(mentions.length ? { mentions } : {}), ...(replyTo ? { replyTo } : {}), ...(card ? { card } : {}) });\n // Not a member (removed, say): refused, and nothing was sent. Anything else by what a bot can do about it.\n if (result.error) throw result.refused ? new CliError(\"refused\", result.error) : groupSendRefused(result.error);\n const messageId = result.messageId ?? null;\n // `edges`: how many took it so far (none yet is not an error: it goes when one opens). `sent`: whether one did.\n // Before 2026-10-02 `sent` was always true, also for a message no edge had taken.\n const edges = messageId ? (wait === \"sent\" ? await waitForGroupFrame(ctx, group.id, messageId, undefined, num(params, \"timeout\", 30, { min: 1, max: 3600 }) * 1000) : node(ctx).groupTaken({ groupId: group.id, messageId })) : 0;\n return { group: group.id, messageId, sent: edges > 0, edges, ...(card ? { buttons: card.id, card } : {}) };\n },\n /**\n * WISP 902 § Edits: the whole new text of one of my messages in a group. It shows here at once and goes to the members\n * (a private group's over the edges that are up, the others when theirs open; a community's through the group).\n * `mentions`: members the new text names beyond those the message named. `sent`: no longer waiting for the pace.\n */\n async \"group.edit\"(ctx, params) {\n const group = groupOf(ctx, params);\n const messageId = str(params, \"message\", true);\n const text = str(params, \"text\", true);\n if (!bool(params, \"force\")) {\n const secret = findSecret(text);\n if (secret) throw new CliError(\"confirm\", `The text looks like ${secret.kind === \"cashu\" ? \"a Cashu token (money anyone who reads it can take)\" : \"a secret (a seed or a private key)\"}; edit with --force if you mean to`, { kind: secret.kind });\n }\n const mentions = mentionsFor(text, list(params, \"mentions\"), group);\n const wait = oneOf(params, \"wait\", [\"none\", \"sent\"] as const, \"none\");\n const linkId = `group:${group.id}`;\n const before = (await node(ctx).getMessage(linkId, messageId))?.edit?.seq ?? 0;\n const result = await node(ctx).editMessage({ linkId, messageId, text, ...(mentions.length ? { mentions } : {}) });\n if (result.error) throw editRefused(await messageOf(ctx, linkId, messageId), messageId, \"group\", result.error, result.refused);\n const message = await node(ctx).getMessage(linkId, messageId);\n const edits = message?.edit?.seq ?? 0;\n // The text the message already has: no edit was made and nothing went. It answered `sent: true` before, for a\n // message never edited, as if an edit had gone.\n if (edits === before) return { group: group.id, messageId, edits, sent: false, edges: 0, unchanged: true };\n // An edit waiting for the pace is not said yet: `--wait sent` waits for that too, then for an edge to take it.\n const edges = !edits ? 0 : wait === \"sent\" ? await waitForGroupFrame(ctx, group.id, messageId, edits, num(params, \"timeout\", 30, { min: 1, max: 3600 }) * 1000)\n : node(ctx).groupTaken({ groupId: group.id, messageId, edit: edits });\n return { group: group.id, messageId, edits, sent: edges > 0 || (!!message && !message.edit?.pending), edges };\n },\n async \"group.react\"(ctx, params) {\n const group = groupOf(ctx, params);\n return { group: group.id, ...(await react(ctx, `group:${group.id}`, params)) };\n },\n /**\n * WISP 902 · Group Mesh § Typing: `chat.typing` in a private group, said on the edges that are open (`reached`: to\n * how many members), with the same kinds, status rules, 6 s hold and `for`. A community does not carry typing yet.\n */\n async \"group.typing\"(ctx, params) {\n const group = groupOf(ctx, params);\n if (group.profile !== \"mesh\") throw new CliError(\"unavailable\", \"A community does not carry typing yet: only a private group does\");\n const typing = !bool(params, \"stop\");\n const { kind, status } = typingWord(params, typing);\n const word = { ...(kind !== \"typing\" ? { kind } : {}), ...(status ? { status } : {}) };\n const seconds = params.for === undefined ? 0 : num(params, \"for\", 0, { min: 1, max: 600 });\n if (!typing && seconds) throw new CliError(\"bad_request\", \"for keeps typing on: not with stop\");\n if (typing && group.status !== \"active\") throw new CliError(\"bad_request\", \"You are not in this group\");\n const target = { groupId: group.id };\n endTyping(ctx, target, false);\n sayTyping(ctx, target, typing, word);\n const until = seconds ? Date.now() + seconds * 1000 : null;\n const kept = seconds ? keepTyping(ctx, target, seconds * 1000, word) : null;\n if (kept && ctx.mode === \"one-shot\") await kept;\n return {\n group: group.id, typing, ...(typing ? { kind } : {}), ...(status ? { status } : {}),\n reached: group.members.filter((m) => !m.me && m.online && !m.viaHub).length, sendTyping: state(ctx).settings.sendTyping !== false, ...(until ? { until } : {}),\n };\n },\n async \"group.leave\"(ctx, params) {\n const group = groupOf(ctx, params);\n await node(ctx).leaveGroup({ groupId: group.id });\n return { left: group.id };\n },\n async \"group.forget\"(ctx, params) {\n const group = groupOf(ctx, params);\n if (!bool(params, \"yes\")) throw new CliError(\"confirm\", \"Forgetting a group deletes its history on this device; pass yes (--yes) to go on\");\n await node(ctx).forgetGroup({ groupId: group.id });\n return { forgotten: group.id };\n },\n async \"group.accept\"(ctx, params) {\n const group = groupOf(ctx, params);\n await node(ctx).acceptGroupInvitation({ groupId: group.id });\n return { group: group.id };\n },\n async \"group.decline\"(ctx, params) {\n const group = groupOf(ctx, params);\n await node(ctx).declineGroupInvitation({ groupId: group.id });\n return { group: group.id };\n },\n\n async \"events.replay\"(ctx, params) {\n return { events: ctx.hub.replay(num(params, \"since\", 0)), lastSeq: ctx.hub.lastSeq };\n },\n\n async \"daemon.stop\"(ctx) {\n if (ctx.mode !== \"daemon\" || !ctx.stop) throw new CliError(\"unavailable\", \"No daemon runs this profile\");\n setTimeout(() => ctx.stop!(), 10);\n return { stopping: true, pid: process.pid };\n },\n\n async \"engine.call\"(ctx, params) {\n const method = str(params, \"method\", true);\n if (!ENGINE_METHODS.includes(method) && !ENGINE_READS.includes(method)) throw new CliError(\"not_found\", `The engine has no call ${method}`);\n const raw = params.params;\n if (raw !== undefined && (raw === null || typeof raw !== \"object\" || Array.isArray(raw))) throw new CliError(\"bad_request\", \"params must be an object\");\n let args = raw as Params | undefined;\n // Real money moves only on an explicit confirmation of this call (WISP 1100 § Mainnet).\n if (args && \"confirmedReal\" in args && !bool(params, \"confirmReal\")) {\n throw new CliError(\"confirm\", `${method} spends real money: pass --confirm-real (confirmReal: true over the socket) to confirm it`);\n }\n if (method === \"getMessages\") return node(ctx).getMessages(str(args ?? {}, \"linkId\", true));\n if (method === \"getState\") return state(ctx);\n // Someone chose the chat on screen: an invite's mark no longer clears it.\n if (method === \"setActiveLink\") inviteShown.get(ctx)?.();\n const target = (node(ctx) as unknown as Record<string, (p?: unknown) => unknown>)[method];\n if (typeof target !== \"function\") throw new CliError(\"not_found\", `The engine has no call ${method}`);\n if (args === undefined) args = undefined;\n return (await target.call(node(ctx), args)) ?? null;\n },\n};\n\n/** Reacts to a message of a chat or a group (`group:<id>`): `emoji`, or `remove` for \"\" (WISP 400 § Reactions). */\nasync function react(ctx: ApiContext, linkId: string, params: Params): Promise<{ messageId: string; emoji: string | null; removed: boolean }> {\n const messageId = str(params, \"message\", true);\n const removed = bool(params, \"remove\");\n const emoji = removed ? \"\" : str(params, \"emoji\");\n if (!removed && !emoji) throw new CliError(\"usage\", \"Give one emoji, or --remove to take yours back\");\n const result = await node(ctx).react({ linkId, messageId, emoji: emoji ?? \"\" });\n if (result.error) throw new CliError(result.refused ? \"refused\" : /not in this chat|No reaction of yours/.test(result.error) ? \"not_found\" : \"bad_request\", result.error);\n return { messageId, emoji: emoji || null, removed };\n}\n\n/**\n * What `typing` says: a kind, and a status a bot writes. The contact's app would cut a long status and drop one with\n * a link; here that is an error instead, so the bot learns what the contact will (not) see.\n */\nfunction typingWord(params: Params, typing: boolean): { kind: TypingKind; status?: string } {\n const kind = oneOf(params, \"kind\", TYPING_KINDS, \"typing\");\n const raw = str(params, \"status\");\n if (!typing && (params.kind !== undefined || raw !== undefined)) throw new CliError(\"bad_request\", \"kind and status go with a start, not with stop\");\n if (raw === undefined) return { kind };\n const oneLine = raw.replace(/\\s+/g, \" \").trim();\n if ([...oneLine].length > TYPING_STATUS_MAX) throw new CliError(\"bad_request\", `status: at most ${TYPING_STATUS_MAX} characters`);\n const status = sanitizeTypingStatus(oneLine);\n if (!status) throw new CliError(\"bad_request\", \"status: plain text, with no link or markup\");\n return { kind, status };\n}\n\n/**\n * A group message the engine did not send, by what a bot can do about it: a `--reply` to a message this group does not\n * have is `not_found` (exit 3), as `group react`, `group edit` and `pin` answer for one; out of the group (removed,\n * left, a forked history) is `refused`: it will not go later either. Anything else may go on a later try\n * (`unavailable`): the epoch's key is still on its way. All three were `unavailable` (exit 1) before.\n */\nexport function groupSendRefused(error: string): CliError {\n if (/not in this chat|cannot be replied to/.test(error)) return new CliError(\"not_found\", error);\n if (/^You (were removed from|left|are no longer in) this group$|Membership changes are halted/.test(error)) return new CliError(\"refused\", error);\n return new CliError(\"unavailable\", error);\n}\n\n/**\n * A chat command given a group's id, prefix or name: `not_found` that says it is a group, with the group command that\n * takes it (`react` on a group, where `group react` is meant). A name no chat and no group has stays the chat's error.\n */\nfunction notAGroup<T>(ctx: ApiContext, params: Params, chat: () => T, command: string, rest: string): T {\n try { return chat(); } catch (error) {\n if (!(error instanceof CliError) || error.code !== \"not_found\") throw error;\n const ref = str(params, \"chat\", true);\n let group: GroupView;\n try { group = findGroup(state(ctx).groups, ref); } catch { throw error; }\n const shown = /^[\\w.:-]+$/.test(ref) ? ref : JSON.stringify(ref);\n throw new CliError(\"not_found\", `${JSON.stringify(ref)} is a group, not a chat: ghostly ${command} ${shown} ${rest}`, { group: group.id });\n }\n}\n\n/**\n * An edit the engine refused. A message that is not in the chat or group at all is `not_found` (exit 3), naming the id\n * to give, as `button update` and `task update` do; before, a mistyped or lost id read \"Only your own text messages\n * can be edited\" (`refused`, exit 1), as if the message were someone else's.\n */\nfunction editRefused(message: StoredMessage | undefined, ref: string, where: \"chat\" | \"group\", error: string, refused: boolean | undefined): CliError {\n if (refused && !message)\n return new CliError(\"not_found\", `No message ${JSON.stringify(ref)} in this ${where}: give the messageId ${where === \"group\" ? \"group send\" : \"send\"} printed`);\n return new CliError(refused ? \"refused\" : \"unavailable\", error);\n}\n\n/**\n * A page of a chat's or a group's history. Without `after` only that page is read from the store (the newest, or those\n * before `before`), so a long chat's is as quick as a short one's; with `after` the whole history is read and cut.\n */\nasync function historyOf(ctx: ApiContext, chat: string, params: Params, json: (message: StoredMessage) => MessageJson = messageJson) {\n if (params.after !== undefined && params.after !== null) return history(await node(ctx).getMessages(chat), params, json);\n const limit = num(params, \"limit\", 50, { min: 1, max: 10_000, whole: true });\n const before = params.before ?? undefined;\n if (before !== undefined && typeof before !== \"number\" && typeof before !== \"string\") throw new CliError(\"bad_request\", \"before must be a timestamp or a message id\");\n const page = await node(ctx).messagePage({ linkId: chat, limit, before }).catch((error: unknown) => {\n throw error instanceof Error && error.message === `No message ${before}` ? new CliError(\"not_found\", error.message) : error;\n });\n return { messages: page.messages.map((m) => json(m)), more: page.more };\n}\n\nfunction history(messages: StoredMessage[], params: Params, json: (message: StoredMessage) => MessageJson = messageJson) {\n const limit = num(params, \"limit\", 50, { min: 1, max: 10_000, whole: true });\n const before = params.before;\n const after = params.after;\n let list = messages.slice().sort((a, b) => a.timestamp - b.timestamp);\n const cut = (value: unknown, side: \"before\" | \"after\") => {\n if (value === undefined || value === null) return;\n if (typeof value === \"number\") { list = list.filter((m) => (side === \"before\" ? m.timestamp < value : m.timestamp > value)); return; }\n if (typeof value !== \"string\") throw new CliError(\"bad_request\", `${side} must be a timestamp or a message id`);\n const index = list.findIndex((m) => m.id === value);\n if (index === -1) throw new CliError(\"not_found\", `No message ${value}`);\n list = side === \"before\" ? list.slice(0, index) : list.slice(index + 1);\n };\n cut(before, \"before\");\n cut(after, \"after\");\n const more = after !== undefined && after !== null ? list.length > limit : list.length > limit;\n const page = after !== undefined && after !== null ? list.slice(0, limit) : list.slice(-limit);\n return { messages: page.map((m) => json(m)), more };\n}\n\nexport { findChat, findGroup } from \"./apiKit\";\nexport type { ApiContext } from \"./apiKit\";\nexport const API_METHODS: readonly string[] = Object.keys(METHODS);\n\n/** Runs one method of the API: one the table lists itself (`constructor`, which every object has, is none). */\nexport async function callApi(ctx: ApiContext, method: string, params: unknown = {}): Promise<unknown> {\n const run = Object.hasOwn(METHODS, method) ? METHODS[method] : undefined;\n if (!run) throw new CliError(\"not_found\", `No method ${method}`);\n if (params === null || typeof params !== \"object\" || Array.isArray(params)) throw new CliError(\"bad_request\", \"params must be an object\");\n return run(ctx, await withStoredIds(ctx, params as Params));\n}\n","import { chmodSync, lstatSync, mkdirSync } from \"node:fs\";\n\n/**\n * A folder for a socket outside the profile (when the profile's path is too long for one): made owner-only, and\n * refused when it is a link, someone else made it, or anyone else may enter it.\n */\nexport function privateFolder(path: string, what: string): void {\n mkdirSync(path, { recursive: true, mode: 0o700 });\n const info = lstatSync(path);\n if (!info.isDirectory() || (process.getuid && info.uid !== process.getuid()) || (info.mode & 0o077) !== 0) {\n throw new Error(`${path} is not a folder of this user's alone: ${what}`);\n }\n}\n\n/**\n * The profile's own folder, made owner-only again before the daemon's socket goes in it: a folder made by an older\n * release, or opened up by hand, is closed rather than trusted. Refused when it is a link or someone else's.\n */\nexport function ownFolder(path: string, what: string): void {\n const info = lstatSync(path);\n if (!info.isDirectory() || (process.getuid && info.uid !== process.getuid())) throw new Error(`${path} is not a folder of this user's: ${what}`);\n if ((info.mode & 0o077) !== 0) chmodSync(path, 0o700);\n}\n\n/** False when `path` is there but is not a socket of this user's: nothing to connect to. */\nexport function ownSocket(path: string): boolean {\n if (process.platform === \"win32\") return true;\n let info;\n try { info = lstatSync(path); } catch { return true; }\n return info.isSocket() && (!process.getuid || info.uid === process.getuid());\n}\n","import { connect, type Socket } from \"node:net\";\nimport { createInterface } from \"node:readline\";\nimport { CliError, type ErrorCode } from \"./errors\";\nimport type { GhostlyEvent } from \"./events\";\nimport { ownSocket } from \"./privateFolder\";\n\n/**\n * A connection to a profile's daemon over its socket (WISP 1100 § Local control API). `null` from `connectDaemon`\n * means no daemon answers there: a one-shot command runs the profile itself.\n */\nexport class DaemonClient {\n private nextId = 1;\n private readonly pending = new Map<number, { resolve: (value: unknown) => void; reject: (error: Error) => void }>();\n private eventListener: ((event: GhostlyEvent) => void) | null = null;\n private closedListener: (() => void) | null = null;\n\n constructor(private readonly socket: Socket) {\n createInterface({ input: socket, crlfDelay: Infinity }).on(\"line\", (line) => {\n let message: { id?: number; result?: unknown; error?: { code: ErrorCode; message: string; details?: Record<string, unknown> }; event?: GhostlyEvent };\n try { message = JSON.parse(line); } catch { return; }\n if (message.event) { this.eventListener?.(message.event); return; }\n const waiting = typeof message.id === \"number\" ? this.pending.get(message.id) : undefined;\n if (!waiting) return;\n this.pending.delete(message.id!);\n if (message.error) waiting.reject(new CliError(message.error.code, message.error.message, message.error.details));\n else waiting.resolve(message.result);\n });\n socket.on(\"close\", () => {\n for (const waiting of this.pending.values()) waiting.reject(new CliError(\"unavailable\", \"The daemon went away\"));\n this.pending.clear();\n this.closedListener?.();\n });\n socket.on(\"error\", () => {});\n }\n\n call(method: string, params: unknown = {}): Promise<unknown> {\n const id = this.nextId++;\n return new Promise((resolve, reject) => {\n this.pending.set(id, { resolve, reject });\n this.socket.write(JSON.stringify({ id, method, params }) + \"\\n\");\n });\n }\n\n /** Events after `since` (replayed from the journal), then every new one, until the connection closes. */\n async subscribe(since: number | undefined, listener: (event: GhostlyEvent) => void, onClosed?: () => void): Promise<{ lastSeq: number }> {\n this.eventListener = listener;\n this.closedListener = onClosed ?? null;\n return this.call(\"events.subscribe\", since === undefined ? {} : { since }) as Promise<{ lastSeq: number }>;\n }\n\n close(): void {\n this.socket.end();\n }\n}\n\nexport function connectDaemon(path: string, timeoutMs = 2_000): Promise<DaemonClient | null> {\n // Someone else's socket (or a link) where the daemon's should be is no daemon of this profile's.\n if (!ownSocket(path)) return Promise.resolve(null);\n return new Promise((resolve) => {\n const socket = connect(path);\n const timer = setTimeout(() => { socket.destroy(); resolve(null); }, timeoutMs);\n socket.once(\"connect\", () => { clearTimeout(timer); resolve(new DaemonClient(socket)); });\n socket.once(\"error\", () => { clearTimeout(timer); resolve(null); });\n });\n}\n","/*\n * What `ghostly app init` writes (WISP 1200, Apps · Publishing): the smallest app that does something with a contact.\n * Three files, each whole in this module, so the CLI's one built file carries them: the manifest as a publisher writes\n * it, a page that says hello to the same app on the contact's side, and a README with the way from here to a store.\n */\n\nexport interface AppTemplate { name: string; title: string }\n\nconst html = (text: string) => text.replace(/[&<>\"]/g, (c) => ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\" })[c]!);\n\n/** `ghostly-app.json`: the manifest without `publisher`, `sequence` and `files`, which `app publish` writes. */\nexport function templateManifest({ name, title }: AppTemplate): Record<string, unknown> {\n return {\n name,\n version: \"0.1.0\",\n kind: \"mini-app\",\n title,\n tagline: \"Say hello to a contact\",\n description: \"Open it in a chat and your contact gets a card to join. Each side can then say hello to the other.\",\n entry: \"index.html\",\n permissions: [\"chat\"],\n view: \"chat\",\n runtime: { host: \">=1.2\", clients: [\"web\", \"desktop\"] },\n license: \"MIT\",\n };\n}\n\n/**\n * `index.html`: the whole app in one file, as the sandbox needs it (no network, so nothing is loaded from elsewhere).\n * Its script is plain JavaScript typed with JSDoc against `@ghostlytools/sdk/app`, so it runs as written and still type\n * checks (test/appInit.test.ts does). The title is the only thing put into it, as text. Its type imports are in single\n * quotes: they are text of the page, not imports of the CLI's own build (test/packageDeps.test.ts reads those).\n */\nexport function templatePage({ title }: AppTemplate): string {\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${html(title)}</title>\n<style>\n :root { color-scheme: light dark; --bg: #ffffff; --fg: #16181d; --muted: #5b6270; --line: #d9dde3; --accent: #2f6fed; }\n :root[data-theme=\"dark\"] { --bg: #16181d; --fg: #f2f4f7; --muted: #a3abb8; --line: #343a45; --accent: #7aa5ff; }\n body { margin: 0; padding: 20px; background: var(--bg); color: var(--fg); font: 16px/1.4 system-ui, sans-serif; }\n h1 { margin: 0 0 4px; font-size: 20px; }\n #status { margin: 0 0 16px; color: var(--muted); }\n button { font: inherit; padding: 8px 16px; border: 1px solid var(--accent); border-radius: 8px; background: var(--accent); color: var(--bg); }\n button:disabled { opacity: 0.5; }\n ul { margin: 16px 0 0; padding: 0; list-style: none; }\n li { padding: 8px 0; border-top: 1px solid var(--line); }\n</style>\n</head>\n<body>\n<h1>${html(title)}</h1>\n<p id=\"status\" role=\"status\">Starting</p>\n<button id=\"hello\" type=\"button\" disabled>Say hello</button>\n<ul id=\"log\" aria-label=\"Messages\"></ul>\n<script type=\"module\">\n// The types of the API come from the SDK (npm install --save-dev @ghostlytools/sdk); nothing of it runs here.\n/** @typedef {import('@ghostlytools/sdk/app').MiniAppApi} MiniAppApi */\n/** @typedef {import('@ghostlytools/sdk/app').MiniAppJson} MiniAppJson */\n\n/** The longest text shown from a contact's frame. */\nconst MAX_TEXT = 200;\n\nconst status = /** @type {HTMLElement} */ (document.getElementById(\"status\"));\nconst button = /** @type {HTMLButtonElement} */ (document.getElementById(\"hello\"));\nconst log = /** @type {HTMLElement} */ (document.getElementById(\"log\"));\n\n/** @param {string} text */\nfunction show(text) {\n const line = document.createElement(\"li\");\n line.textContent = text;\n log.append(line);\n}\n\n/**\n * What the contact's app sent, when it is a hello. A frame comes from another device: read only what this app sends,\n * and never put it in the page as HTML.\n * @param {MiniAppJson} data\n * @returns {string | null}\n */\nfunction helloOf(data) {\n if (!data || typeof data !== \"object\" || Array.isArray(data) || data.type !== \"hello\") return null;\n return typeof data.text === \"string\" && data.text.length <= MAX_TEXT ? data.text : null;\n}\n\n/**\n * Why a send was refused, in words. The code is the rejection's message (MINI_APP_ERROR_CODES in the SDK); a later\n * client may add one.\n * @param {unknown} error\n */\nfunction refusal(error) {\n switch (error instanceof Error ? error.message : \"\") {\n case \"offline\": return \"Not sent: you are not both online\";\n case \"peer-closed\": case \"not-open\": return \"Not sent: your contact has not opened this app\";\n case \"not-allowed\": return \"Open this app in a chat to say hello\";\n default: return \"Not sent\";\n }\n}\n\n/** @param {MiniAppApi} ghostly */\nasync function start(ghostly) {\n // Listen before anything is awaited, so no frame is missed.\n ghostly.chat.on(\"message\", (data) => {\n const text = helloOf(data);\n if (text !== null) show(\"Your contact: \" + text);\n });\n ghostly.chat.on(\"peer\", (peer) => {\n status.textContent = peer.open ? \"Your contact has this app open\" : \"Waiting for your contact to open this app\";\n });\n\n const context = await ghostly.context();\n const dark = context.theme ? context.theme === \"dark\" : matchMedia(\"(prefers-color-scheme: dark)\").matches;\n document.documentElement.dataset.theme = dark ? \"dark\" : \"light\";\n status.textContent = !context.inChat ? \"Open this app in a chat to say hello\"\n : context.peer ? \"Your contact has this app open\" : \"Waiting for your contact to open this app\";\n\n button.disabled = false;\n button.addEventListener(\"click\", async () => {\n try {\n await ghostly.chat.send({ type: \"hello\", text: \"Hello!\" });\n show(\"You: Hello!\");\n } catch (error) {\n show(refusal(error));\n }\n });\n}\n\n/** Ghostly's runner gives the app \\`window.ghostly\\`; a browser tab has none. */\nconst ghostly = /** @type {{ ghostly?: MiniAppApi }} */ (window).ghostly;\nif (ghostly) void start(ghostly);\nelse status.textContent = \"This page is a Ghostly app: publish it and open it in Ghostly\";\n</script>\n</body>\n</html>\n`;\n}\n\n/** `README.md`: what the folder holds and the way from it to a store. */\nexport function templateReadme({ name, title }: AppTemplate): string {\n return `# ${title}\n\nA [Ghostly](https://github.com/MiguelMedeiros/ghostly) app, started with \\`ghostly app init\\`. Opened in a 1:1 chat, it\ngives your contact a card to join, and each side can say hello to the other.\n\nA Ghostly app ([WISP 1200](https://github.com/MiguelMedeiros/ghostly/blob/dev/docs/wisps/1200-marketplace.md)) is a\npage that runs in the client's sandbox. It has no network, and talks to Ghostly only through \\`window.ghostly\\`:\nframes of at most 32 KiB to the same app on the contact's side (\\`chat.send\\`, \\`chat.on\\`), storage per app and per\nchat, and the files of its own bundle. Apps run in Ghostly from release 1.2.\n\n| File | What it is |\n|---|---|\n| \\`ghostly-app.json\\` | The manifest, without \\`publisher\\`, \\`sequence\\` and \\`files\\`: \\`ghostly app publish\\` writes those |\n| \\`index.html\\` | The app, whole: styles and script inline |\n| \\`README.md\\` | This file |\n\n## Write it\n\n\\`index.html\\` is plain JavaScript, typed with JSDoc against \\`@ghostlytools/sdk/app\\`: \\`MiniAppApi\\` is\n\\`window.ghostly\\`, and the same module has the limits (\\`MINI_APP_LIMITS\\`) and the codes a refused call rejects with\n(\\`MINI_APP_ERROR_CODES\\`). For the types in your editor:\n\n\\`\\`\\`sh\nnpm install --save-dev @ghostlytools/sdk\n\\`\\`\\`\n\n- What a contact's app sends is untrusted: check its shape, bound its size, and show it as text, never as HTML.\n- \\`\"view\": \"chat\"\\` runs the app inside a chat with one contact. \\`\"full\"\\` is a full-screen app opened from the Apps page.\n- \\`permissions\\` lists what the person is asked for: \\`chat\\` (frames to the contact's app), \\`name\\` (the person's\n display name) and \\`internet\\`. Adding one later makes every install ask before it updates.\n\n## Build\n\nThere is nothing to build yet: the page is the app. Once it grows into several source files, or TypeScript, build it\ninto one self-contained \\`index.html\\` (scripts, styles and images inline) and put that file and \\`ghostly-app.json\\`\nalone in a folder of their own, which is the folder you publish.\n[ghostly-chess](https://github.com/MiguelMedeiros/ghostly-chess) does it with Vite and a small\n\\`scripts/stage.mjs\\`. \\`ghostly app publish\\` takes every file of the folder it is given but dot files, this README\nincluded, so do not run it on a repository with sources, \\`node_modules\\` or anything private in it.\n\n## Publish\n\n\\`\\`\\`sh\nghostly app publish . --key ~/ghostly-keys/${name}-publisher.key\nghostly app verify app.ghostlyapp\n\\`\\`\\`\n\n\\`publish\\` signs the folder into \\`app.ghostlyapp\\`. The first run makes the publisher key at the path you name:\n\n- Keep it **outside this folder** and its repository (\\`publish\\` refuses a key inside it), and never commit it.\n- Back it up. An app is updated only with the key that signed it; a lost key means a new app.\n\n\\`verify\\` checks the bundle as Ghostly will and prints its \\`ref\\` (\\`<publisher key>/${name}\\`), \\`sequence\\` and\n\\`digest\\`. For the next version, raise \\`version\\` in \\`ghostly-app.json\\` and publish again: the sequence goes up\nby one by itself.\n\n## Try it\n\nCommit \\`app.ghostlyapp\\` to a public GitHub repository, then in Ghostly open Apps, choose Add, and paste its URL\n(\\`https://raw.githubusercontent.com/<owner>/<repo>/HEAD/app.ghostlyapp\\`). Set \\`sources\\` in \\`ghostly-app.json\\`\nto that URL, in a list, so installs made from it find your updates. Open the app from a chat's + menu; your contact\ngets a card to install and open it.\n\n## List it in a store\n\nA store is a signed list of apps. The official one is a Git repository,\n[ghostly-store](https://github.com/MiguelMedeiros/ghostly-store), and apps enter it by pull request:\n\n1. Commit \\`app.ghostlyapp\\` to your public repository and note that commit.\n2. Add \\`apps/${name}.<first 16 characters of your publisher key>/listing.json\\` with the \\`ref\\`, \\`sequence\\`\n and \\`digest\\` that \\`ghostly app verify\\` printed. Its \\`urls\\` start with a jsDelivr URL pinned to the full commit\n (\\`https://cdn.jsdelivr.net/gh/<owner>/<repo>@<40-character commit>/app.ghostlyapp\\`).\n3. Open the pull request. Its check verifies the bundle; a maintainer reviews the app and the owner signs the index.\n\nThe store's [CONTRIBUTING](https://github.com/MiguelMedeiros/ghostly-store/blob/main/CONTRIBUTING.md) has the rules.\nAn app installed from a store updates only to the version that store lists, so each new version is a pull request too.\n\n## If something goes wrong\n\nIf a version is harmful, \\`ghostly app revoke . --key <your key> --up-to <sequence>\\` writes \\`ghostly-revoke.json\\`;\npublish it beside \\`app.ghostlyapp\\` and Ghostly stops those versions.\n`;\n}\n","import { createHash, createPrivateKey, generateKeyPairSync } from \"node:crypto\";\nimport { closeSync, existsSync, lstatSync, mkdirSync, openSync, readdirSync, readFileSync, renameSync, statSync, writeFileSync, writeSync } from \"node:fs\";\nimport { basename, dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport {\n APP_BUNDLE_LIMITS, APP_PREFIXES, appFingerprint, appRef, appStoreDecision, assembleAppBundle, canonicalJson, canonicalJsonBytes,\n APP_STATEMENT_LIMITS, checkAppManifest, checkAppRevokeStatement, checkAppStoreIndex, fromBase64Url, isAppUrl, readAppBundle,\n readAppRevocations, readAppStore, seedSigner, signAppObject, signAppRevocation, signAppStore, toBase64Url, toZ32, type AppBundle,\n type AppManifest, type AppRevokeStatement, type AppStoreIndex, type SignedAppRevocation, type Signer,\n} from \"@ghostly/core\";\nimport { templateManifest, templatePage, templateReadme } from \"./appTemplate\";\nimport { CliError } from \"./errors\";\n\n/*\n * The publisher tools of WISP 1200 (Apps · Publishing): `app init` writes a folder to start from, `app publish` bundles\n * and signs a folder, `app verify` checks a bundle as a client would, `store sign` signs a store index. They run here, on files: no profile, no daemon, no\n * network but the one read `app verify` makes of a URL.\n *\n * Keys. A publisher key and a store key are Ed25519 keys of their own, never a profile's (WISP 1200 · Publisher\n * identity). Each lives in a file the command names with `--key`: a PKCS #8 PEM block (what `openssl genpkey -algorithm\n * ed25519` writes, so secret scanners know it for a private key), after one line that says which kind it is. The file\n * is made owner-only (0600) when a command makes it, and refused when anyone else may read it. A key is never printed:\n * only its public key and fingerprint are.\n */\n\nexport const APP_SOURCE = \"ghostly-app.json\";\nexport const APP_BUNDLE_FILE = \"app.ghostlyapp\";\nexport const APP_REVOKE_FILE = \"ghostly-revoke.json\";\nexport const STORE_INDEX_FILE = \"ghostly-store.json\";\nexport const STORE_SIG_FILE = \"ghostly-store.sig\";\n\n/** How long `app verify` waits for a URL, and how many redirects it follows (each checked as the first). */\nconst FETCH_TIMEOUT_MS = 60_000;\nconst MAX_REDIRECTS = 5;\n\n// ---------- keys ----------\n\nexport type KeyKind = \"publisher\" | \"store\";\n\nconst KEY_HEADER: Record<KeyKind, string> = {\n publisher: \"Ghostly publisher key (WISP 1200). Secret: never share or commit it. Back it up: an app signed with it is updated only with it.\",\n store: \"Ghostly store key (WISP 1200). Secret: keep it offline, never share or commit it. Back it up: a store is its key.\",\n};\nconst PEM = /-----BEGIN PRIVATE KEY-----\\r?\\n([A-Za-z0-9+/=\\r\\n]+)-----END PRIVATE KEY-----/;\n\nexport interface LoadedKey { signer: Signer; key: string; created: boolean }\n\n/** Refuses a key file that is not a plain file of this user's, or that anyone else may read or write. */\nfunction checkKeyFile(path: string): void {\n const info = statSync(path);\n if (!info.isFile()) throw new CliError(\"bad_request\", `${path} is not a file: --key names a key file`);\n if (process.platform === \"win32\") return;\n if (process.getuid && info.uid !== process.getuid()) throw new CliError(\"refused\", `${path} is not this user's: a key file must be yours alone`);\n if ((info.mode & 0o077) !== 0) {\n throw new CliError(\"refused\", `${path} may be read by others (mode ${(info.mode & 0o777).toString(8)}): run chmod 600 ${path}, and think whether the key was seen`);\n }\n}\n\n/** Reads a key file: the PEM block, an Ed25519 key, and the kind its first line names (when it names one). */\nfunction readKey(path: string, kind: KeyKind): LoadedKey {\n checkKeyFile(path);\n const text = readFileSync(path, \"utf8\");\n const other: KeyKind = kind === \"publisher\" ? \"store\" : \"publisher\";\n if (text.startsWith(`Ghostly ${other} key`)) {\n throw new CliError(\"refused\", `${path} is a ${other} key: ${kind === \"publisher\" ? \"a store key never signs an app\" : \"a publisher key never signs a store index\"} (WISP 1200 · Stores)`);\n }\n const block = PEM.exec(text);\n let seed: Uint8Array | undefined;\n if (block) {\n try {\n const key = createPrivateKey(block[0]);\n const d = key.asymmetricKeyType === \"ed25519\" ? key.export({ format: \"jwk\" }).d : undefined;\n if (d) seed = fromBase64Url(d);\n } catch { /* said below, without the file's text */ }\n }\n if (!seed || seed.length !== 32) throw new CliError(\"bad_request\", `${path} holds no Ed25519 private key (a PEM \"PRIVATE KEY\" block, as openssl genpkey -algorithm ed25519 writes)`);\n const signer = seedSigner(seed);\n return { signer, key: toZ32(signer.publicKey), created: false };\n}\n\n/** Makes a key file: owner-only from the start, never over a file that is there. */\nfunction makeKey(path: string, kind: KeyKind): LoadedKey {\n mkdirSync(dirname(path), { recursive: true, mode: 0o700 });\n const { privateKey } = generateKeyPairSync(\"ed25519\");\n const pem = privateKey.export({ type: \"pkcs8\", format: \"pem\" }) as string;\n const fd = openSync(path, \"wx\", 0o600);\n try { writeSync(fd, `${KEY_HEADER[kind]}\\n${pem}`); } finally { closeSync(fd); }\n return { ...readKey(path, kind), created: true };\n}\n\n/** What the person is told on stderr when a key was made: where, which, and to back it up. Never the key. */\nfunction madeKeyNote(path: string, kind: KeyKind, key: string): string {\n return kind === \"publisher\"\n ? `new publisher key written to ${path}; back it up (${appFingerprint(key)}: an app is updated only with the key that signed it, and phase 1 has no recovery)`\n : `new store key written to ${path}; back it up and keep it offline (${appFingerprint(key)}: a store is its key)`;\n}\n\n// ---------- reading bundles ----------\n\nconst refused = (what: string, reading: { reason: string; detail?: string }, extra: Record<string, unknown> = {}) =>\n new CliError(\"refused\", `${what} (${reading.reason}${reading.detail ? `: ${reading.detail}` : \"\"})`, { reason: reading.reason, ...(reading.detail ? { detail: reading.detail } : {}), ...extra });\n\n/** A bundle file's bytes: its size is checked before it is read, so a huge file is refused unread. */\nfunction readBundleFile(path: string): Uint8Array {\n const info = statSync(path);\n if (info.isDirectory()) throw new CliError(\"bad_request\", `${path} is a folder: name a .ghostlyapp file`);\n if (info.size > APP_BUNDLE_LIMITS.bundleBytes) throw refused(`${path} is not a valid bundle`, { reason: \"too-large\", detail: `${info.size} bytes` });\n return new Uint8Array(readFileSync(path));\n}\n\n/**\n * A bundle at an https URL, as a client fetches one: `https:` only, no user or password, jsDelivr only at a full\n * commit (WISP 1200 · Manifest, \"Every URL\"); every redirect is held to the same rule, and the download stops past\n * 16 MiB.\n */\nasync function fetchBundle(url: string): Promise<Uint8Array> {\n let at = url;\n for (let hop = 0; ; hop++) {\n if (!isAppUrl(at)) {\n throw new CliError(hop ? \"refused\" : \"usage\", hop\n ? `${url} redirects to ${at}, which a client does not read (https only, no user or password, jsDelivr only at a full commit)`\n : `Not a URL a client reads: https only, at most ${APP_BUNDLE_LIMITS.url} characters, no user or password, and jsDelivr only as https://cdn.jsdelivr.net/gh/<owner>/<repo>@<40-hex commit>/<path>`);\n }\n let response: Response;\n try {\n response = await fetch(at, { redirect: \"manual\", signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });\n } catch (error) {\n if (error instanceof Error && error.name === \"TimeoutError\") throw new CliError(\"timeout\", `${at} did not answer in ${FETCH_TIMEOUT_MS / 1000} s`);\n throw new CliError(\"unavailable\", `Could not read ${at}: ${error instanceof Error ? error.message : String(error)}`);\n }\n if (response.status >= 300 && response.status < 400 && response.headers.get(\"location\")) {\n await response.body?.cancel();\n if (hop >= MAX_REDIRECTS) throw new CliError(\"refused\", `${url} redirects more than ${MAX_REDIRECTS} times`);\n at = new URL(response.headers.get(\"location\")!, at).href;\n continue;\n }\n if (response.status === 404 || response.status === 410) { await response.body?.cancel(); throw new CliError(\"not_found\", `${at} answered ${response.status}`); }\n if (!response.ok || !response.body) { await response.body?.cancel(); throw new CliError(\"unavailable\", `${at} answered ${response.status}`); }\n const limit = APP_BUNDLE_LIMITS.bundleBytes;\n const tooLarge = (bytes: number | string) => refused(`${url} is not a valid bundle`, { reason: \"too-large\", detail: `${bytes} bytes` });\n const length = Number(response.headers.get(\"content-length\"));\n if (Number.isFinite(length) && length > limit) { await response.body.cancel(); throw tooLarge(length); }\n const chunks: Uint8Array[] = [];\n let total = 0;\n const reader = response.body.getReader();\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n total += value.length;\n if (total > limit) { await reader.cancel(); throw tooLarge(`more than ${limit}`); }\n chunks.push(value);\n }\n } catch (error) {\n if (error instanceof CliError) throw error;\n if (error instanceof Error && error.name === \"TimeoutError\") throw new CliError(\"timeout\", `${at} did not finish in ${FETCH_TIMEOUT_MS / 1000} s`);\n throw new CliError(\"unavailable\", `Could not read ${at}: ${error instanceof Error ? error.message : String(error)}`);\n }\n const out = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) { out.set(chunk, offset); offset += chunk.length; }\n return out;\n }\n}\n\n/** What a checked bundle is, as `app verify` and `app publish` print it. */\nfunction described(bundle: AppBundle, bytes: Uint8Array) {\n const m = bundle.manifest;\n return {\n ref: appRef(m.publisher, m.name), publisher: m.publisher, fingerprint: appFingerprint(m.publisher), name: m.name,\n title: m.title, version: m.version, sequence: m.sequence, digest: bundle.digest, bytes: bytes.length,\n sha256: toBase64Url(createHash(\"sha256\").update(bytes).digest()),\n };\n}\n\n/** `app verify <bundle|url>`: every check a client makes before it stores a bundle (WISP 1200 · Reading a bundle). */\nexport async function verifyApp(source: string): Promise<Record<string, unknown>> {\n const isUrl = /^[a-z][a-z0-9+.-]*:\\/\\//i.test(source);\n const bytes = isUrl ? await fetchBundle(source) : readBundleFile(resolve(source));\n const read = readAppBundle(bytes);\n if (!read.ok) throw refused(`${source} is not a valid bundle`, read);\n return { valid: true, ...described(read.bundle, bytes), manifest: read.bundle.manifest };\n}\n\n// ---------- starting an app ----------\n\n/** A folder's name as an app's: lower case, anything else a hyphen (`My App` is `my-app`). */\nconst nameFrom = (folder: string) => folder.toLowerCase().replace(/[^a-z0-9]+/g, \"-\").replace(/^-+|-+$/g, \"\");\n/** An app's name as a title: `my-app` is `My app`. */\nconst titleFrom = (name: string) => (name[0]!.toUpperCase() + name.slice(1)).replace(/-/g, \" \");\n\n/**\n * `app init <dir> [--name <name>] [--title <title>] [--force]`: a small working app in `<dir>` (made when missing),\n * which `app publish <dir>` signs as it is: `ghostly-app.json`, an `index.html` that says hello to the contact, and a\n * README with the way to a store (./appTemplate.ts). The name is `--name`, else the folder's; the title `--title`, else\n * the name's. Nothing is written when one of the three files is there, unless `--force`, which replaces those three only.\n */\nexport async function initApp(dirArg: string, what: { name?: string; title?: string; force?: boolean } = {}): Promise<Record<string, unknown>> {\n const usage = \"ghostly app init <dir> [--name <name>] [--title <title>] [--force]\";\n const dir = resolve(dirArg);\n const name = what.name ?? nameFrom(basename(dir));\n const title = what.title ?? (name ? titleFrom(name) : \"\");\n const page = templatePage({ name, title });\n // Checked as publish will check it, under a key of zeros and the page's own entry: what is written always publishes.\n const pageBytes = new TextEncoder().encode(page);\n const checked = checkAppManifest({\n ...templateManifest({ name, title }), ghostlyApp: 1, publisher: toZ32(new Uint8Array(32)), sequence: 1,\n files: [{ path: \"index.html\", size: pageBytes.length, sha256: toBase64Url(createHash(\"sha256\").update(pageBytes).digest()) }],\n }, { strict: true });\n if (!checked.ok) {\n if (checked.detail === \"name is not valid\") {\n throw new CliError(\"usage\", what.name === undefined\n ? `The folder's name gives no app name (${JSON.stringify(basename(dir))}): give --name, a lowercase letter then up to 31 lowercase letters, digits or hyphens: ${usage}`\n : `--name is a lowercase letter then up to 31 lowercase letters, digits or hyphens, not ${JSON.stringify(what.name)}`);\n }\n if (checked.detail === \"title is not valid\") throw new CliError(\"usage\", `--title is one line of 1 to ${APP_BUNDLE_LIMITS.title} characters, not ${JSON.stringify(title)}`);\n throw refused(\"The template makes no valid manifest\", checked);\n }\n\n if (existsSync(dir) && !statSync(dir).isDirectory()) throw new CliError(\"bad_request\", `${dir} is a file: name a folder for the app`);\n const files: [string, string][] = [\n [APP_SOURCE, `${JSON.stringify(templateManifest({ name, title }), null, 2)}\\n`],\n [\"index.html\", page],\n [\"README.md\", templateReadme({ name, title })],\n ];\n const held = files.map(([path]) => path).filter((path) => existsSync(join(dir, path)));\n if (held.length && !what.force) {\n throw refused(`${dir} already holds ${held.join(\", \")}: nothing was written (--force replaces ${held.length > 1 ? \"them\" : \"it\"})`, { reason: \"exists\" }, { files: held });\n }\n mkdirSync(dir, { recursive: true });\n for (const [path, text] of files) writeWhole(join(dir, path), new TextEncoder().encode(text));\n return {\n dir, name, title, files: files.map(([path]) => path), ...(held.length ? { replaced: held } : {}),\n next: `ghostly app publish ${dirArg} --key <a key file outside ${dirArg}>`,\n };\n}\n\n// ---------- publishing ----------\n\n/** Every file of the folder, as the bundle names it; dot files and folders are left out and listed. */\nfunction collectFiles(dir: string, leaveOut: Set<string>): { files: { path: string; full: string; size: number }[]; skipped: string[] } {\n const files: { path: string; full: string; size: number }[] = [];\n const skipped: string[] = [];\n let bytes = 0;\n const walk = (folder: string) => {\n for (const name of readdirSync(folder).sort()) {\n const full = join(folder, name);\n const path = relative(dir, full).split(sep).join(\"/\");\n if (leaveOut.has(full)) continue;\n if (name.startsWith(\".\")) { skipped.push(path); continue; }\n const info = lstatSync(full);\n if (info.isSymbolicLink()) throw new CliError(\"bad_request\", `${path} is a link: a bundle holds the folder's own files (copy it in, or remove it)`);\n if (info.isDirectory()) { walk(full); continue; }\n if (!info.isFile()) throw new CliError(\"bad_request\", `${path} is not a file`);\n files.push({ path, full, size: info.size });\n bytes += info.size;\n if (files.length > APP_BUNDLE_LIMITS.files) throw refused(`${dir} holds more files than a bundle may`, { reason: \"too-many-files\", detail: `more than ${APP_BUNDLE_LIMITS.files}` });\n if (bytes > APP_BUNDLE_LIMITS.bundleBytes) throw refused(`${dir} holds more than a bundle may`, { reason: \"too-large\", detail: `more than ${APP_BUNDLE_LIMITS.bundleBytes} bytes of files` });\n }\n };\n walk(dir);\n // The manifest lists every path in code-unit order, across folders: `lib-extra.js` before `lib/x.js`, which the walk\n // reaches first (`-` and `.` sort before `/`).\n files.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));\n return { files, skipped };\n}\n\n/**\n * A private key in a file: a PEM private key block with its body (PKCS #8, OpenSSH, RSA, EC, encrypted), or a Ghostly\n * key file's first line. A library that only names the block's header in its code does not match.\n */\nconst PRIVATE_KEY = /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----\\r?\\n[A-Za-z0-9+/=\\r\\n:,-]{40,}-----END [A-Z0-9 ]*PRIVATE KEY-----|^Ghostly (publisher|store) key \\(WISP 1200\\)/;\n\n/** Whether `path` is `dir` or anything under it. */\nconst isInside = (dir: string, path: string) => { const rel = relative(dir, path); return !rel.startsWith(\"..\") && !isAbsolute(rel); };\n\n/** Fields of `ghostly-app.json` the command writes itself. */\nconst WRITTEN = [\"publisher\", \"files\"] as const;\n\nfunction readSource(path: string): Record<string, unknown> {\n if (!existsSync(path)) throw new CliError(\"not_found\", `No ${APP_SOURCE} in ${dirname(path)}: it holds the manifest's fields (name, version, title, tagline, entry, permissions, runtime, license, ...) but publisher, sequence and files, which publish writes`);\n let value: unknown;\n try { value = JSON.parse(readFileSync(path, \"utf8\")); } catch (error) { throw new CliError(\"bad_request\", `${path} is not JSON: ${error instanceof Error ? error.message : String(error)}`); }\n if (!value || typeof value !== \"object\" || Array.isArray(value)) throw new CliError(\"bad_request\", `${path} is not a JSON object`);\n const draft = value as Record<string, unknown>;\n const written = WRITTEN.filter((k) => k in draft);\n if (written.length) throw new CliError(\"bad_request\", `${path} has ${written.join(\" and \")}: publish writes ${written.length > 1 ? \"them\" : \"it\"} from the key and the folder's files`);\n if (draft.ghostlyApp !== undefined && draft.ghostlyApp !== 1) throw refused(`${path} is not a manifest this release makes`, { reason: \"unsupported-format\", detail: \"ghostlyApp is not 1\" });\n if (draft.sequence !== undefined && !(Number.isSafeInteger(draft.sequence) && (draft.sequence as number) >= 1)) {\n throw new CliError(\"bad_request\", `${path}: sequence is a whole number from 1 (or leave it out: publish raises it by itself)`);\n }\n return draft;\n}\n\n/** Writes a file whole or not at all: a reader never sees half a bundle (and a dot file left by a crash is never bundled). */\nfunction writeWhole(path: string, bytes: Uint8Array): void {\n const tmp = join(dirname(path), `.${basename(path)}.${process.pid}.tmp`);\n writeFileSync(tmp, bytes);\n renameSync(tmp, path);\n}\n\n/**\n * `app publish <dir> --key <file> [--out <file>]`: `<dir>/ghostly-app.json` (the manifest without `publisher`,\n * `sequence` and `files`) and every other file in `<dir>` become one signed bundle, by default `<dir>/app.ghostlyapp`.\n * The sequence is `--sequence` (higher than the bundle already at `--out`'s), else one more than that bundle's (or\n * the source's `sequence`, if higher, else 1). The\n * key file is made when it is missing and no earlier bundle is there to update.\n */\nexport async function publishApp(dirArg: string, keyArg: string, outArg?: string, sequenceArg?: number): Promise<Record<string, unknown>> {\n if (sequenceArg !== undefined && !(Number.isSafeInteger(sequenceArg) && sequenceArg >= 1)) throw new CliError(\"usage\", `--sequence takes a whole number from 1, not ${sequenceArg}`);\n const dir = resolve(dirArg);\n if (!statSync(dir).isDirectory()) throw new CliError(\"bad_request\", `${dir} is not a folder`);\n const keyPath = resolve(keyArg);\n // Every file of the folder is published, and the folder is often the repository the bundle is committed to.\n if (isInside(dir, keyPath)) {\n throw refused(`${keyPath} is inside ${dir}, which is published: keep the publisher key outside the app's folder (and its repository)`, { reason: \"private-key\" });\n }\n const out = resolve(outArg ?? join(dir, APP_BUNDLE_FILE));\n const sourcePath = join(dir, APP_SOURCE);\n const draft = readSource(sourcePath);\n\n // The version this one follows: the bundle already at the output, which only its own key may follow.\n let previous: AppManifest | null = null;\n if (existsSync(out)) {\n const read = readAppBundle(readBundleFile(out));\n if (!read.ok) throw refused(`${out} is there and is not a valid bundle: move it away, or name another --out`, read);\n previous = read.bundle.manifest;\n }\n\n let key: LoadedKey;\n if (existsSync(keyPath)) key = readKey(keyPath, \"publisher\");\n else if (previous) {\n throw new CliError(\"not_found\", `No key file ${keyPath}: ${out} is signed by ${appFingerprint(previous.publisher)}, and only that key publishes its next version`);\n } else key = makeKey(keyPath, \"publisher\");\n\n if (previous && previous.publisher !== key.key) {\n throw refused(`${out} is ${appRef(previous.publisher, previous.name)}, signed by another key (${appFingerprint(previous.publisher)}): a version of an app is signed by its own key`, { reason: \"other-app\" }, { publisher: previous.publisher });\n }\n if (previous && previous.name !== draft.name) {\n throw refused(`${out} is the app ${JSON.stringify(previous.name)}, not ${JSON.stringify(draft.name)}: name another --out for another app`, { reason: \"other-app\" });\n }\n\n if (sequenceArg !== undefined && previous && sequenceArg <= previous.sequence) {\n throw refused(`--sequence ${sequenceArg} is not higher than the ${previous.sequence} of ${out}: every client refuses a lower one, and the same one with other content is equivocation`, { reason: sequenceArg < previous.sequence ? \"rollback\" : \"equivocation\" }, { previous: previous.sequence });\n }\n const sequence = sequenceArg ?? Math.max(previous ? previous.sequence + 1 : 1, (draft.sequence as number | undefined) ?? 1);\n const { files, skipped } = collectFiles(dir, new Set([sourcePath, out, join(dir, APP_REVOKE_FILE)]));\n const contents = files.map((f) => ({ path: f.path, bytes: new Uint8Array(readFileSync(f.full)) }));\n // A bundle is public: a private key found in the folder (an old key, a store key, any PEM) stops the publish.\n const keyFile = contents.find((f) => PRIVATE_KEY.test(Buffer.from(f.bytes).toString(\"latin1\")));\n if (keyFile) throw refused(`${keyFile.path} in ${dir} holds a private key, and a bundle is public: move it out of the app's folder`, { reason: \"private-key\", detail: keyFile.path });\n const manifest = {\n ...draft,\n ghostlyApp: 1,\n publisher: key.key,\n sequence,\n files: contents.map((f) => ({ path: f.path, size: f.bytes.length, sha256: toBase64Url(createHash(\"sha256\").update(f.bytes).digest()) })),\n };\n const checked = checkAppManifest(JSON.parse(JSON.stringify(manifest)), { strict: true });\n if (!checked.ok) throw refused(`${sourcePath} and the folder's files make no valid manifest`, checked);\n\n const { bytes: manifestBytes, signature } = await signAppObject(APP_PREFIXES.app, checked.manifest, key.signer);\n const bundle = assembleAppBundle(manifestBytes, canonicalJsonBytes(signature), contents.map((f) => f.bytes));\n // Read back as a client would (the icon's header, the bundle's size): what is written is never a bundle it refuses.\n const read = readAppBundle(bundle);\n if (!read.ok) throw refused(\"The bundle made is not valid\", read);\n mkdirSync(dirname(out), { recursive: true });\n writeWhole(out, bundle);\n\n return {\n ...described(read.bundle, bundle),\n previous: previous?.sequence ?? null,\n out,\n files: contents.map((f) => f.path),\n ...(skipped.length ? { skipped } : {}),\n keyCreated: key.created,\n ...(key.created ? { warning: madeKeyNote(keyPath, \"publisher\", key.key) } : {}),\n };\n}\n\n// ---------- revocations ----------\n\n/**\n * `app revoke <dir> --key <file> (--digest <digest>... | --up-to <sequence>) [--reason <text>] [--bundle <file>]`: a\n * `ghostly-revoke/1` statement (WISP 1200 · Publisher keys, Revocation) for the app whose bundle is `<dir>/app.ghostlyapp`\n * (or `--bundle`), signed by that app's publisher key and added to `<dir>/ghostly-revoke.json`, which stays canonical.\n * The key is never made here: only the key that signed the app may revoke its versions.\n */\nexport async function revokeApp(dirArg: string, keyArg: string, what: { digests?: string[]; upTo?: number; reason?: string; bundle?: string }): Promise<Record<string, unknown>> {\n const dir = resolve(dirArg);\n const bundlePath = resolve(what.bundle ?? join(dir, APP_BUNDLE_FILE));\n const listPath = join(dir, APP_REVOKE_FILE);\n const usage = \"ghostly app revoke <dir> --key <file> (--digest <digest>... | --up-to <sequence>) [--reason <text>]\";\n const digests = what.digests ?? [];\n if ((digests.length > 0) === (what.upTo !== undefined)) throw new CliError(\"usage\", `Give --digest (again for each) or --up-to, one of the two: ${usage}`);\n if (what.upTo !== undefined && !(Number.isSafeInteger(what.upTo) && what.upTo >= 1)) throw new CliError(\"usage\", `--up-to takes a sequence, a whole number from 1, not ${what.upTo}`);\n\n if (!existsSync(bundlePath)) throw new CliError(\"not_found\", `No bundle ${bundlePath}: a revocation names the app of the bundle in <dir> (or --bundle)`);\n const read = readAppBundle(readBundleFile(bundlePath));\n if (!read.ok) throw refused(`${bundlePath} is not a valid bundle`, read);\n const manifest = read.bundle.manifest;\n const ref = appRef(manifest.publisher, manifest.name);\n\n const keyPath = resolve(keyArg);\n if (!existsSync(keyPath)) throw new CliError(\"not_found\", `No key file ${keyPath}: only the key that signed ${ref} (${appFingerprint(manifest.publisher)}) revokes its versions`);\n const key = readKey(keyPath, \"publisher\");\n if (key.key !== manifest.publisher) {\n throw refused(`${keyPath} is not the publisher key of ${ref} (${appFingerprint(manifest.publisher)}): a revocation by any other key is refused by every client`, { reason: \"signature-key\" });\n }\n\n const statement = (digests.length\n ? { ghostlyRevoke: 1, app: ref, digests }\n : { ghostlyRevoke: 1, app: ref, upTo: what.upTo }) as AppRevokeStatement;\n if (what.reason !== undefined) statement.reason = what.reason;\n const problem = checkAppRevokeStatement(statement);\n if (problem) throw refused(\"Not a revocation a client reads\", { reason: \"bad-revocation\", detail: problem });\n\n let list: SignedAppRevocation[] = [];\n if (existsSync(listPath)) {\n const held = readAppRevocations(new Uint8Array(readFileSync(listPath)));\n if (!held.ok) throw refused(`${listPath} is there and is not a valid ghostly-revoke.json: fix or move it first`, held);\n list = held.revocations;\n }\n const signed = await signAppRevocation(statement, key.signer);\n const same = list.some((r) => canonicalJson(r.statement) === canonicalJson(signed.statement));\n if (!same) {\n if (list.length >= APP_STATEMENT_LIMITS.revocations) throw refused(`${listPath} holds ${list.length} revocations already`, { reason: \"bad-revocation\", detail: `at most ${APP_STATEMENT_LIMITS.revocations}` });\n list = [...list, signed];\n const bytes = canonicalJsonBytes(list);\n const check = readAppRevocations(bytes);\n if (!check.ok) throw refused(\"The revocation list made is not valid\", check);\n writeWhole(listPath, bytes);\n }\n const covers = digests.length\n ? { digests, current: digests.includes(read.bundle.digest) }\n : { upTo: what.upTo, current: manifest.sequence <= what.upTo! };\n return { app: ref, ...covers, ...(what.reason !== undefined ? { reason: what.reason } : {}), added: !same, revocations: list.length, file: listPath };\n}\n\n// ---------- store indexes ----------\n\n/**\n * `store sign <index> --key <file> [--out <dir>]`: a store index written by hand (any JSON layout; `key` may be left\n * out and is then the signing key's) becomes `ghostly-store.json`, canonical, and `ghostly-store.sig`, in `--out` or\n * beside the index. It is read back as a client reads it (`expires` at most 90 days ahead), and refused when the index\n * already there, signed by the same key, has a higher `sequence`, or the same one with other content.\n */\nexport async function signStore(indexArg: string, keyArg: string, outArg?: string, now = Math.floor(Date.now() / 1000)): Promise<Record<string, unknown>> {\n const indexPath = resolve(indexArg);\n const keyPath = resolve(keyArg);\n const outDir = resolve(outArg ?? dirname(indexPath));\n let value: unknown;\n try { value = JSON.parse(readFileSync(indexPath, \"utf8\")); } catch (error) {\n if (error instanceof SyntaxError) throw new CliError(\"bad_request\", `${indexPath} is not JSON: ${error.message}`);\n throw error;\n }\n if (!value || typeof value !== \"object\" || Array.isArray(value)) throw new CliError(\"bad_request\", `${indexPath} is not a JSON object`);\n const index = value as Record<string, unknown>;\n\n let key: LoadedKey;\n if (existsSync(keyPath)) key = readKey(keyPath, \"store\");\n else if (index.key !== undefined) throw new CliError(\"not_found\", `No key file ${keyPath}: the index names the store key ${typeof index.key === \"string\" ? appFingerprint(index.key) : JSON.stringify(index.key)}`);\n else key = makeKey(keyPath, \"store\");\n if (index.key !== undefined && index.key !== key.key) {\n throw refused(`${indexPath} names another store key than ${keyPath}'s (${appFingerprint(key.key)})`, { reason: \"store-key\" });\n }\n index.key = key.key;\n\n const checked = checkAppStoreIndex(index, { strict: true });\n if (!checked.ok) throw refused(`${indexPath} is not a valid store index`, checked);\n const { indexBytes, sigBytes } = await signAppStore(index as unknown as AppStoreIndex, key.signer);\n const read = readAppStore(indexBytes, sigBytes, now, key.key);\n if (!read.ok) throw refused(`${indexPath} would be refused by a client`, read);\n const store = read.store;\n\n // The index this one follows, when the folder holds one this key signed.\n const indexOut = join(outDir, STORE_INDEX_FILE), sigOut = join(outDir, STORE_SIG_FILE);\n let previous: number | null = null;\n if (existsSync(indexOut) && existsSync(sigOut)) {\n const held = readAppStore(new Uint8Array(readFileSync(indexOut)), new Uint8Array(readFileSync(sigOut)), now);\n if (held.ok && held.store.index.key === key.key) {\n previous = held.store.index.sequence;\n const decision = appStoreDecision(\n { key: key.key, sequence: held.store.index.sequence, digest: held.store.digest },\n { key: key.key, sequence: store.index.sequence, digest: store.digest },\n );\n if (decision === \"rollback\" || decision === \"equivocation\") {\n throw refused(`${indexOut} already holds sequence ${held.store.index.sequence} of this store: ${decision === \"rollback\" ? \"a lower one is refused by every client\" : \"the same one with other content is refused as equivocation\"}; raise sequence`, { reason: decision }, { held: held.store.index.sequence });\n }\n }\n }\n\n mkdirSync(outDir, { recursive: true });\n writeWhole(indexOut, indexBytes);\n writeWhole(sigOut, sigBytes);\n const notes = [\n ...(key.created ? [madeKeyNote(keyPath, \"store\", key.key)] : []),\n ...(store.expired ? [`expires (${new Date(store.index.expires * 1000).toISOString()}) is past: clients still install from it and say the store was not updated since then`] : []),\n ];\n return {\n key: key.key, fingerprint: appFingerprint(key.key), name: store.index.name, kind: store.index.kind,\n sequence: store.index.sequence, previous, expires: store.index.expires, expired: store.expired,\n apps: store.index.apps.length, removed: store.index.removed.length, revoked: store.index.revoked.length,\n digest: store.digest, index: indexOut, sig: sigOut, keyCreated: key.created,\n ...(notes.length ? { warning: notes.join(\"; \") } : {}),\n };\n}\n","import { initApp, publishApp, revokeApp, signStore, verifyApp } from \"../apps\";\nimport { CliError } from \"../errors\";\nimport type { Command } from \"./shared\";\n\n/** The key file a command signs with: always named, never a default, never on the command line itself. */\nfunction keyFile(value: unknown, usage: string): string {\n if (typeof value !== \"string\" || !value) throw new CliError(\"usage\", `--key <file> is needed: ghostly ${usage}`);\n return value;\n}\n\nconst key = { type: \"string\", description: \"The key file (made, owner-only, when it is missing)\" } as const;\n\n/**\n * The publisher tools of WISP 1200 (Apps · Publishing): they run here, on files, with no profile or daemon. One entry\n * per command, in alphabetical order (test/commands.test.ts checks).\n */\nexport const commands: Record<string, Command> = {\n \"app init\": {\n method: \"app.init\", usage: \"app init <dir> [--name <name>] [--title <title>] [--force]\",\n summary: \"Start an app (WISP 1200): a small working one in <dir>, its ghostly-app.json, an index.html that says hello to the contact and a README, ready for app publish\",\n args: [\"dir\"],\n options: {\n name: { type: \"string\", description: \"The app's name, lowercase letters, digits and hyphens (default: the folder's)\" },\n title: { type: \"string\", description: \"The title people see, up to 40 characters (default: from the name)\" },\n force: { type: \"boolean\", description: \"Replace the three files when they are there\" },\n },\n run: ({ options }, a) => initApp(a.dir!, { name: options.name as string | undefined, title: options.title as string | undefined, force: options.force === true }),\n },\n \"app publish\": {\n method: \"app.publish\", usage: \"app publish <dir> --key <file> [--out <file>]\",\n summary: \"Bundle and sign an app (WISP 1200): <dir>/ghostly-app.json and every other file in <dir> become one .ghostlyapp, its sequence one more than the bundle at --out (default <dir>/app.ghostlyapp)\",\n args: [\"dir\"],\n options: {\n key: { ...key, description: \"The publisher key file (made, owner-only, on first use; back it up)\" },\n out: { type: \"string\", description: \"Where the bundle goes (default <dir>/app.ghostlyapp); the bundle there is the version this one follows\" },\n sequence: { type: \"number\", description: \"This version's sequence, higher than the bundle at --out's (default: one more than it)\" },\n },\n run: ({ options }, a) => publishApp(a.dir!, keyFile(options.key, \"app publish <dir> --key <file> [--out <file>] [--sequence n]\"), options.out as string | undefined, options.sequence as number | undefined),\n },\n \"app revoke\": {\n method: \"app.revoke\", usage: \"app revoke <dir> --key <file> (--digest <digest>... | --up-to <sequence>) [--reason <text>] [--bundle <file>]\",\n summary: \"Revoke versions of your app (WISP 1200 · Revocation): a ghostly-revoke/1 statement signed with its publisher key, added to <dir>/ghostly-revoke.json\",\n args: [\"dir\"],\n options: {\n key: { type: \"string\", description: \"The app's publisher key file (never made here)\" },\n digest: { type: \"list\", description: \"A version's digest, base64url as app verify prints it (again for each, up to 64)\" },\n \"up-to\": { type: \"number\", description: \"Every version with this sequence or a lower one\" },\n reason: { type: \"string\", description: \"Why, one line of up to 200 characters, shown to people\" },\n bundle: { type: \"string\", description: \"The bundle naming the app (default <dir>/app.ghostlyapp)\" },\n },\n run: ({ options }, a) => revokeApp(a.dir!, keyFile(options.key, \"app revoke <dir> --key <file> (--digest <digest>... | --up-to <sequence>) [--reason <text>]\"), {\n digests: options.digest as string[] | undefined, upTo: options[\"up-to\"] as number | undefined,\n reason: options.reason as string | undefined, bundle: options.bundle as string | undefined,\n }),\n },\n \"app verify\": {\n method: \"app.verify\", usage: \"app verify <bundle|url>\",\n summary: \"Check a .ghostlyapp as a client would, from a file or an https URL: its manifest and digest, or refused (exit 1) with the reason\",\n args: [\"bundle\"],\n run: (_, a) => verifyApp(a.bundle!),\n },\n \"store sign\": {\n method: \"store.sign\", usage: \"store sign <index> --key <file> [--out <dir>]\",\n summary: \"Sign a store index (WISP 1200 · Stores): writes ghostly-store.json, canonical, and ghostly-store.sig beside it or in --out\",\n args: [\"index\"],\n options: {\n key: { ...key, description: \"The store key file (made, owner-only, when it is missing and the index names no key; keep it offline)\" },\n out: { type: \"string\", description: \"The folder to write ghostly-store.json and ghostly-store.sig in (default: the index's)\" },\n },\n run: ({ options }, a) => signStore(a.index!, keyFile(options.key, \"store sign <index> --key <file> [--out <dir>]\"), options.out as string | undefined),\n },\n};\n","import { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { ITEM_STATES, PR_CHECKS, PR_STATES, RUN_RESULTS, STATUS_CARD_LIMITS, cardUrl } from \"@ghostly/core\";\nimport type { OptionSpec, Parsed } from \"../args\";\nimport { CliError } from \"../errors\";\n\n/**\n * The commands that are one API method each: how the command line becomes the method's parameters. Commands with\n * their own flow (profiles, the daemon, listen, send's stdin, the engine passthrough) live in main.ts.\n */\nexport interface Command {\n method: string;\n usage: string;\n summary: string;\n options?: Record<string, OptionSpec>;\n /** Positionals, in order; a trailing `...` one takes the rest joined by spaces. */\n args?: string[];\n params?: (parsed: Parsed, args: Record<string, string | undefined>) => Record<string, unknown>;\n /**\n * A command that runs here, on files, with no profile or daemon (the publisher tools of WISP 1200): its answer is\n * printed as a method's is, and `method` only names it.\n */\n run?: (parsed: Parsed, args: Record<string, string | undefined>) => Promise<Record<string, unknown>>;\n}\n\nexport const wait: Record<string, OptionSpec> = {\n wait: { type: \"string\", description: \"none, sent or delivered (default: none with a daemon, sent without)\" },\n timeout: { type: \"number\", description: \"Seconds to wait (default 30)\" },\n};\n/** A group has no receipts: `sent` waits until an edge took the frame (a member's, or one of my hubs'). */\nexport const groupWait: Record<string, OptionSpec> = {\n wait: { type: \"string\", description: \"none or sent: until an edge took it (default none)\" },\n timeout: { type: \"number\", description: \"Seconds to wait (default 30)\" },\n};\nexport const net: OptionSpec = { type: \"string\", description: \"mainnet or testnet (default testnet)\" };\nexport const card: OptionSpec = { type: \"string\", description: \"A Lightning card (its id; default: the network's default)\" };\nexport const memo: OptionSpec = { type: \"string\", description: \"What it is for (up to 140 characters)\" };\nexport const confirmReal: OptionSpec = { type: \"boolean\", description: \"Confirm a Mainnet (real money) spend\" };\n\n/** A path as this command's working directory means it: the daemon runs elsewhere. */\nexport function here(path: unknown): string | undefined {\n return typeof path === \"string\" ? resolve(path) : undefined;\n}\n\n/** A whole number of sats from the command line. */\nexport function sats(value: string | undefined): number {\n const n = Number(value);\n if (!value || !Number.isSafeInteger(n) || n <= 0) throw new CliError(\"usage\", `Not an amount in sats: ${JSON.stringify(value)}`);\n return n;\n}\n\n/** `name=value` options as an object. */\nexport function pairs(values: unknown): Record<string, string> | undefined {\n if (!Array.isArray(values) || !values.length) return undefined;\n const out: Record<string, string> = {};\n for (const item of values as string[]) {\n const eq = item.indexOf(\"=\");\n if (eq < 1) throw new CliError(\"usage\", `--value takes name=value, not ${JSON.stringify(item)}`);\n out[item.slice(0, eq)] = item.slice(eq + 1);\n }\n return out;\n}\n\nexport const rate: OptionSpec = { type: \"number\", description: \"The audio's sample rate: 48000 (default), 24000, 16000, 12000 or 8000\" };\nexport const force: OptionSpec = { type: \"boolean\", description: \"Send even if the text looks like a seed, a key or ecash\" };\nexport const reply: OptionSpec = { type: \"string\", description: \"Reply to this message (its id, from history or an event)\" };\n/** Buttons under a message (WISP 406 · Message Buttons): the text is the question. */\nexport const buttonOptions: Record<string, OptionSpec> = {\n button: { type: \"list\", description: \"A button under the message, id:Label (again for each, 1 to 6; the label may hold colons). A press comes back as button.pressed\" },\n style: { type: \"list\", description: \"A button's look, id=primary, neutral or danger\" },\n once: { type: \"boolean\", description: \"Each person answers once: after a press their app offers the buttons no more\" },\n id: { type: \"string\", description: \"The question's id (default: made up, ask-…)\" },\n};\n/** The group's entry link lets anyone join: printed only when asked for. */\nexport const showSecret: Record<string, OptionSpec> = { \"show-secret\": { type: \"boolean\", description: \"Print the group's entry link (anyone who has it can join)\" } };\n/** A pending chat's invite code lets whoever reads it take the chat: printed only when asked for. */\nexport const showInvite: Record<string, OptionSpec> = { \"show-secret\": { type: \"boolean\", description: \"Print a pending chat's invite code (whoever has it can take the chat)\" } };\nexport const secret = (options: Parsed[\"options\"]) => (options[\"show-secret\"] === true ? { showSecret: true } : {});\n\n/**\n * `--for` of the typing commands: the seconds it is kept on, 1 to 600, and never with `--stop`. A usage error (exit 2)\n * that names the flag: before, the API's own check answered (\"for must be a number from 1 to 600\", exit 1).\n */\nexport function typingFor(options: Parsed[\"options\"]): number | undefined {\n const seconds = options.for;\n if (seconds === undefined) return undefined;\n if (typeof seconds !== \"number\" || !(seconds >= 1 && seconds <= 600)) throw new CliError(\"usage\", `--for takes a number of seconds from 1 to 600, not ${JSON.stringify(seconds)}`);\n if (options.stop === true) throw new CliError(\"usage\", \"--for keeps it on: not with --stop\");\n return seconds;\n}\n\n/** A history cursor: a timestamp when it is all digits, else a message id. */\nexport function cursor(value: unknown): string | number | undefined {\n if (value === undefined) return undefined;\n const text = String(value);\n return /^\\d{10,}$/.test(text) ? Number(text) : text;\n}\n\n/**\n * A status card's fields given as JSON (WISP 405 · Status Cards): inline (`{...}`), `-` for stdin, or a file's path. The\n * flags given beside it win over it.\n */\nexport function cardJson(value: unknown): Record<string, unknown> {\n if (value === undefined) return {};\n const text = String(value).trim();\n let raw: string;\n try { raw = text.startsWith(\"{\") ? text : readFileSync(text === \"-\" ? 0 : resolve(text), \"utf8\"); }\n catch (error) { throw new CliError(\"usage\", `--json takes a card as JSON, - for stdin, or a file: ${error instanceof Error ? error.message : String(error)}`); }\n let parsed: unknown;\n try { parsed = JSON.parse(raw); } catch { throw new CliError(\"usage\", \"--json is not valid JSON\"); }\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) throw new CliError(\"usage\", \"--json is a JSON object of the card's fields\");\n return parsed as Record<string, unknown>;\n}\n\n/** Options every status card command takes. */\nexport const cardOptions: Record<string, OptionSpec> = {\n json: { type: \"string\", description: \"The card's fields as JSON: inline, - for stdin, or a file (flags win over it)\" },\n link: { type: \"list\", description: \"A link on the card: https://… or label=https://… (up to 4)\" },\n text: { type: \"string\", description: \"The text older apps show instead (default: written from the card)\" },\n force: { type: \"boolean\", description: \"Send even if the card's text looks like a seed, a key or ecash\" },\n};\n\n/** `--link label=https://…` or a bare link, as a card's links. */\nexport function cardLinks(values: unknown): { url: string; label?: string }[] | undefined {\n if (!Array.isArray(values) || !values.length) return undefined;\n return (values as string[]).map((value) => {\n const eq = value.indexOf(\"=\");\n return eq > 0 && !value.slice(0, eq).includes(\":\") ? { label: value.slice(0, eq), url: value.slice(eq + 1) } : { url: value };\n });\n}\n\nconst listed = (words: readonly string[]) => `${words.slice(0, -1).join(\", \")} or ${words.at(-1)}`;\n\n/**\n * `--item state:text` as a task's item. The word before the first colon is the state when it is one lowercase word (and\n * the colon does not start `://`): one of `ITEM_STATES`, or a usage error that lists them, so `queued:Publishing` is\n * refused rather than shown as that text. Anything else (`Step 2: build`, `Note: x`, a link, no colon) is the whole\n * text, pending; `pending:fix: tests` keeps a colon after a lowercase word.\n */\nexport function itemOf(value: string): { state: string; text: string } {\n const colon = value.indexOf(\":\");\n const word = colon > 0 ? value.slice(0, colon) : \"\";\n if (!/^[a-z]+$/.test(word) || value.startsWith(\"//\", colon + 1)) return { state: \"pending\", text: value };\n if (!(ITEM_STATES as readonly string[]).includes(word))\n throw new CliError(\"usage\", `--item takes state:text, and ${JSON.stringify(word)} is not a state: use ${listed(ITEM_STATES)} (pending:<text> keeps a colon in the text), not ${JSON.stringify(value)}`);\n return { state: word, text: value.slice(colon + 1).trim() };\n}\n\n/** `--progress`, `--left`: a percent, or a usage error that names the flag. */\nfunction percent(options: Parsed[\"options\"], flag: string): void {\n const value = options[flag];\n if (value !== undefined && !(typeof value === \"number\" && value >= 0 && value <= 100)) throw new CliError(\"usage\", `--${flag} takes a percent from 0 to 100, not ${JSON.stringify(value)}`);\n}\n\n/** The flags of a task's pull request, and the field of `pr` each one gives. */\nconst PR_FLAGS = [[\"pr-url\", \"url\"], [\"pr-number\", \"number\"], [\"additions\", \"additions\"], [\"deletions\", \"deletions\"], [\"files\", \"files\"], [\"pr-state\", \"state\"], [\"pr-checks\", \"checks\"]] as const;\n\n/**\n * A task's fields from its flags; what was not given is left out, so an update changes only what it names. What a flag\n * got wrong is a usage error (exit 2) that names the flag: before, it reached the card's own check and failed as\n * `bad_request` (exit 1) in the card's words (\"Status card: done is at most total\").\n */\nexport function taskFields(options: Parsed[\"options\"]): Record<string, unknown> {\n const fields: Record<string, unknown> = { ...cardJson(options.json) };\n const set = (key: string, value: unknown) => { if (value !== undefined) fields[key] = value; };\n set(\"id\", options.id);\n set(\"title\", options.title);\n set(\"status\", options.status);\n percent(options, \"progress\");\n if (options.progress !== undefined && options.steps !== undefined) throw new CliError(\"usage\", \"--progress or --steps, not both\");\n set(\"progress\", options.progress);\n set(\"step\", options.step);\n set(\"branch\", options.branch);\n if (options.steps !== undefined) {\n const match = /^(\\d+)\\s*\\/\\s*(\\d+)$/.exec(String(options.steps));\n if (!match) throw new CliError(\"usage\", `--steps takes done/total, like 2/5, not ${JSON.stringify(options.steps)}`);\n fields.done = Number(match[1]); fields.total = Number(match[2]);\n if (Number(match[2]) < 1 || Number(match[1]) > Number(match[2])) throw new CliError(\"usage\", `--steps takes done/total with done at most total, like 2/5, not ${JSON.stringify(options.steps)}`);\n }\n if (options[\"pr-url\"] !== undefined && !cardUrl(options[\"pr-url\"])) throw new CliError(\"usage\", `--pr-url takes an https link of at most ${STATUS_CARD_LIMITS.url} characters, not ${JSON.stringify(options[\"pr-url\"])}`);\n for (const [flag, words] of [[\"pr-state\", PR_STATES], [\"pr-checks\", PR_CHECKS]] as const)\n if (options[flag] !== undefined && !(words as readonly unknown[]).includes(options[flag])) throw new CliError(\"usage\", `--${flag} takes ${listed(words)}, not ${JSON.stringify(options[flag])}`);\n const pr: Record<string, unknown> = {};\n for (const [flag, key] of PR_FLAGS) if (options[flag] !== undefined) pr[key] = options[flag];\n if (Object.keys(pr).length) fields.pr = { ...(fields.pr && typeof fields.pr === \"object\" ? fields.pr as object : {}), ...pr };\n if (Array.isArray(options.item) && options.item.length) fields.items = (options.item as string[]).map(itemOf);\n if (Array.isArray(options.tag) && options.tag.length > STATUS_CARD_LIMITS.tags) throw new CliError(\"usage\", `--tag is given at most ${STATUS_CARD_LIMITS.tags} times, not ${options.tag.length}`);\n if (Array.isArray(options.tag) && options.tag.length) fields.tags = options.tag as string[];\n set(\"parent\", options.parent);\n const links = cardLinks(options.link);\n if (links) fields.links = links;\n return fields;\n}\n\n/**\n * The fields a new card cannot go without, given as a flag or in `--json`: a usage error (exit 2) that names the\n * flags. Before, a missing `--title` reached the card's own check and failed as `bad_request` (exit 1) with\n * \"Status card: title is text\".\n */\nexport function needs(card: Record<string, unknown>, fields: readonly (readonly [key: string, flag: string])[], usage: string): Record<string, unknown> {\n const missing = fields.filter(([key]) => typeof card[key] !== \"string\" || !(card[key] as string).trim()).map(([, flag]) => `--${flag}`);\n if (missing.length) throw new CliError(\"usage\", `${missing.join(\" and \")} ${missing.length > 1 ? \"are\" : \"is\"} needed: ghostly ${usage}`);\n return card;\n}\n\n/**\n * A new task's pull request goes by its link: `--pr-state` and the like without `--pr-url` (or `pr.url` in `--json`)\n * are a usage error that names them. An update's card may have the link already, so only a send asks.\n */\nexport function prLinked(card: Record<string, unknown>, options: Parsed[\"options\"], usage: string): Record<string, unknown> {\n const given = PR_FLAGS.filter(([flag]) => options[flag] !== undefined).map(([flag]) => `--${flag}`);\n if (!given.length || (card.pr as { url?: unknown }).url !== undefined) return card;\n throw new CliError(\"usage\", `${given.join(\" and \")} ${given.length > 1 ? \"go\" : \"goes\"} with --pr-url: ghostly ${usage}`);\n}\n\n/**\n * A usage card says how much is left: `--left`, or `--used` with `--limit`, as flags or in `--json`. The one that is\n * missing is a usage error that names the flag.\n */\nexport function quota(card: Record<string, unknown>, usage: string): Record<string, unknown> {\n const used = card.used !== undefined, limit = card.limit !== undefined;\n if (used !== limit) throw new CliError(\"usage\", `--${used ? \"used\" : \"limit\"} goes with --${used ? \"limit\" : \"used\"}: ghostly ${usage}`);\n if (card.left === undefined && !used) throw new CliError(\"usage\", `--left is needed (or --used with --limit): ghostly ${usage}`);\n return card;\n}\n\n/** A time a command names: milliseconds, or a date the JavaScript Date reads (`2026-09-30T01:00:00Z`). */\nexport function cardTime(value: unknown, flag: string): number | undefined {\n if (value === undefined) return undefined;\n const text = String(value).trim();\n const ms = /^\\d+$/.test(text) ? Number(text) : Date.parse(text);\n if (!Number.isSafeInteger(ms) || ms <= 0) throw new CliError(\"usage\", `--${flag} takes a time in milliseconds or a date like 2026-09-30T01:00:00Z, not ${JSON.stringify(value)}`);\n return ms;\n}\n\n/** `--run ok`, `--run failed:\"CI flaked\"`: a run of a routine, recorded as its last. */\nexport function runOf(value: unknown): { result: string; summary?: string } | undefined {\n if (value === undefined) return undefined;\n const text = String(value), colon = text.indexOf(\":\");\n const result = colon === -1 ? text : text.slice(0, colon), summary = colon === -1 ? \"\" : text.slice(colon + 1).trim();\n if (!(RUN_RESULTS as readonly string[]).includes(result)) throw new CliError(\"usage\", `--run takes ok, failed or skipped, and :a summary after it if you like, not ${JSON.stringify(value)}`);\n return { result, ...(summary && { summary }) };\n}\n\n/** A routine's fields from its flags; what was not given is left out. */\nexport function routineFields(options: Parsed[\"options\"]): Record<string, unknown> {\n const fields: Record<string, unknown> = { ...cardJson(options.json) };\n for (const key of [\"id\", \"name\", \"schedule\", \"cron\", \"state\"] as const) if (options[key] !== undefined) fields[key] = options[key];\n const next = cardTime(options.next, \"next\");\n if (next !== undefined) fields.nextRunAt = next;\n const links = cardLinks(options.link);\n if (links) fields.links = links;\n return fields;\n}\n\n/** Options a routine command takes, beside `cardOptions`. */\nexport const routineOptions: Record<string, OptionSpec> = {\n name: { type: \"string\", description: \"What the routine is (120 characters)\" },\n schedule: { type: \"string\", description: \"When it runs, as people say it: \\\"every day 01:00\\\" (80 characters)\" },\n cron: { type: \"string\", description: \"Its cron line, shown beside the schedule (never run)\" },\n state: { type: \"string\", description: \"active (default on send) or paused\" },\n next: { type: \"string\", description: \"Its next run: milliseconds or a date like 2026-09-30T01:00:00Z\" },\n run: { type: \"string\", description: \"Record a run now: ok, failed or skipped, with :a summary if you like (it becomes the last run)\" },\n ...cardOptions,\n};\n\n/** Options a task command takes, beside `cardOptions`. */\nexport const taskOptions: Record<string, OptionSpec> = {\n title: { type: \"string\", description: \"What the task is (120 characters)\" },\n status: { type: \"string\", description: \"queued, running (default on send), blocked, done, failed or cancelled\" },\n progress: { type: \"number\", description: \"Percent done, 0 to 100\" },\n steps: { type: \"string\", description: \"Steps done of total, like 2/5 (progress is worked out from them)\" },\n step: { type: \"string\", description: \"What it is doing now (200 characters)\" },\n item: { type: \"list\", description: \"A step or log line, state:text with state pending, running, done, failed or skipped (text alone: pending). A lowercase word before the first colon must be one of them; pending:fix: x keeps the colon. The list replaces the card's (up to 20)\" },\n branch: { type: \"string\", description: \"The branch it works on\" },\n \"pr-url\": { type: \"string\", description: \"The pull request's https link\" },\n \"pr-number\": { type: \"number\", description: \"The pull request's number\" },\n additions: { type: \"number\", description: \"Lines the pull request adds\" },\n deletions: { type: \"number\", description: \"Lines the pull request removes\" },\n files: { type: \"number\", description: \"Files the pull request changes\" },\n \"pr-state\": { type: \"string\", description: \"Where the pull request stands: draft, open (ready for review), merged or closed\" },\n \"pr-checks\": { type: \"string\", description: \"The pull request's checks: passing, failing or pending\" },\n tag: { type: \"list\", description: \"A short label, like an area or a repository (24 characters); up to 3. An update's tags replace the card's: give all of them\" },\n parent: { type: \"string\", description: \"The id of the task of yours in this chat that this one is a part of\" },\n ...cardOptions,\n};\n\n/**\n * `--also week=80` or `--also week=80@2026-10-10T00:00:00Z`: another window of a usage card, its percent left and, after\n * `@`, when it resets.\n */\nexport function usageWindowOf(value: string): { window: string; left: number; resetsAt?: number } {\n const match = /^([^=]+)=(\\d+(?:\\.\\d+)?)(?:@(.+))?$/.exec(value.trim());\n if (!match) throw new CliError(\"usage\", `--also takes window=percent or window=percent@time, like week=80@2026-10-10T00:00:00Z, not ${JSON.stringify(value)}`);\n const resetsAt = cardTime(match[3], \"also\");\n return { window: match[1].trim(), left: Number(match[2]), ...(resetsAt !== undefined && { resetsAt }) };\n}\n\n/** A usage card's fields from its flags (WISP 405 § Usage); `--json` gives any of them, the flags win over it. */\nexport function usageFields(options: Parsed[\"options\"]): Record<string, unknown> {\n const fields: Record<string, unknown> = { ...cardJson(options.json) };\n percent(options, \"left\");\n if (options.left !== undefined && (options.used !== undefined || options.limit !== undefined)) throw new CliError(\"usage\", \"--left or --used with --limit, not both\");\n for (const key of [\"id\", \"left\", \"used\", \"limit\", \"label\", \"account\", \"window\"] as const) if (options[key] !== undefined) fields[key] = options[key];\n const resets = cardTime(options.resets, \"resets\");\n if (resets !== undefined) fields.resetsAt = resets;\n if (Array.isArray(options.also) && options.also.length) fields.windows = (options.also as string[]).map(usageWindowOf);\n return fields;\n}\n\n/** Options `usage send` takes. */\nexport const usageOptions: Record<string, OptionSpec> = {\n left: { type: \"number\", description: \"Percent of the quota left, 0 to 100\" },\n used: { type: \"number\", description: \"How much is used, with --limit (instead of --left; the percent is worked out)\" },\n limit: { type: \"number\", description: \"The quota, with --used\" },\n label: { type: \"string\", description: \"What the quota is of, like Claude (24 characters)\" },\n account: { type: \"string\", description: \"Which of your accounts it is, short (24 characters)\" },\n window: { type: \"string\", description: \"The quota's window, as people say it: \\\"5 h\\\", \\\"week\\\" (16 characters)\" },\n resets: { type: \"string\", description: \"When the window starts again: milliseconds or a date like 2026-10-07T18:00:00Z\" },\n also: { type: \"list\", description: \"Another window, window=percent[@time], like week=80@2026-10-10T00:00:00Z (up to 3; shown in the details)\" },\n id: { type: \"string\", description: \"The card's id (default usage: one card per chat)\" },\n all: { type: \"boolean\", description: \"Every 1:1 chat with a contact, instead of one chat\" },\n json: cardOptions.json,\n text: cardOptions.text,\n force: cardOptions.force,\n};\n","import { CliError } from \"../errors\";\nimport { type Command, rate } from \"./shared\";\n\n/** Voice calls: one entry per command, in alphabetical order (test/commands.test.ts checks). */\nexport const commands: Record<string, Command> = {\n \"call answer\": {\n method: \"call.answer\", usage: \"call answer [<chat|call>] [--rate 48000]\", summary: \"Answer a call that rings (the only one, or the one named)\",\n args: [\"call...?\"], options: { rate: rate }, params: ({ options }, a) => ({ call: a.call, rate: options.rate }),\n },\n \"call auto\": {\n method: \"call.auto\", usage: \"call auto [on|off] [--from <chat>]... [--rate 48000]\", summary: \"Answer calls by themselves: from anyone, or the chats named (kept in the profile)\",\n args: [\"mode...?\"], options: { from: { type: \"list\", description: \"Only calls from this chat (again for more)\" }, rate: rate },\n params: ({ options }, a) => {\n if (a.mode === undefined) return {};\n if (a.mode !== \"on\" && a.mode !== \"off\") throw new CliError(\"usage\", \"ghostly call auto [on|off] [--from <chat>]... [--rate n]\");\n return a.mode === \"on\" ? { on: true, from: options.from, rate: options.rate } : { on: false };\n },\n },\n \"call flush\": { method: \"call.flush\", usage: \"call flush [<chat|call>]\", summary: \"Drop the audio queued and not played yet (barge-in)\", args: [\"call...?\"], params: (_, a) => ({ call: a.call }) },\n \"call hangup\": { method: \"call.hangup\", usage: \"call hangup [<chat|call>]\", summary: \"Hang up, or decline a call that rings\", args: [\"call...?\"], params: (_, a) => ({ call: a.call }) },\n \"call list\": { method: \"call.list\", usage: \"call list\", summary: \"Calls on now, their audio sockets, and auto-answer\" },\n \"call start\": {\n method: \"call.start\", usage: \"call start <chat> [--rate 48000]\", summary: \"Call a contact (voice); the result names the call's audio socket (daemon)\",\n args: [\"chat\"], options: { rate: rate }, params: ({ options }, a) => ({ chat: a.chat, rate: options.rate }),\n },\n};\n","import { CliError } from \"../errors\";\nimport { type Command, force, groupWait, cursor, needs, prLinked, quota, routineFields, routineOptions, runOf, secret, showInvite, taskFields, taskOptions, typingFor, usageFields, usageOptions, wait } from \"./shared\";\n\nconst BUTTON_UPDATE = \"button update <chat|group> <message> [--chosen <button>] [--close] [--text <text>] [--force] [--wait none|confirmed|sent] [--timeout s]\";\n\n/** Invites, chats and messages: one entry per command, in alphabetical order (test/commands.test.ts checks). */\nexport const commands: Record<string, Command> = {\n \"button press\": {\n method: \"button.press\", usage: \"button press <chat|group> <message> <button> [--wait none|sent|delivered] [--timeout s]\",\n summary: \"Press a button of someone else's message, as a tap in the app does (its id from the message's card)\",\n args: [\"chat\", \"message\", \"button\"],\n options: {\n wait: { type: \"string\", description: \"none, sent (a one-shot's default in a chat) or delivered; a group takes none or sent\" },\n timeout: groupWait.timeout,\n },\n params: ({ options }, a) => ({ chat: a.chat, message: a.message, button: a.button, wait: options.wait, timeout: options.timeout }),\n },\n \"button update\": {\n method: \"button.update\", usage: BUTTON_UPDATE,\n summary: \"Show the answer on a message's buttons (--chosen), close them, or change its text (the messageId send --button gave)\",\n args: [\"chat\", \"message\"],\n options: {\n chosen: { type: \"string\", description: \"The button to show as the answer (its id)\" },\n close: { type: \"boolean\", description: \"Close the buttons: no more presses\" },\n text: { type: \"string\", description: \"A new text for the message (default: the one it has); people see it marked edited\" },\n force,\n wait: { type: \"string\", description: \"none (default), confirmed (a chat's contact took it) or sent (a group's edge took it)\" },\n timeout: groupWait.timeout,\n },\n params: ({ options }, a) => {\n if (!options.chosen && options.close !== true && !options.text) throw new CliError(\"usage\", `Give --chosen, --close or --text: ghostly ${BUTTON_UPDATE}`);\n return { chat: a.chat, message: a.message, chosen: options.chosen, close: options.close === true, text: options.text, force: options.force === true, wait: options.wait, timeout: options.timeout };\n },\n },\n \"chat connect\": { method: \"chat.connect\", usage: \"chat connect <chat>\", summary: \"Reconnect a chat now (ends a --hold)\", args: [\"chat\"], params: (_, { chat }) => ({ chat }) },\n \"chat disconnect\": {\n method: \"chat.disconnect\", usage: \"chat disconnect <chat> [--hold <minutes>]\", summary: \"Close a chat's live session (the contact may redial; --hold keeps it on the DHT that long, 0 ends it)\", args: [\"chat\"],\n options: { hold: { type: \"number\", description: \"Stay off the direct link this many minutes (up to 10080): DHT only, taken back after\" } },\n params: ({ options }, { chat }) => ({ chat, hold: options.hold }),\n },\n \"chat history\": {\n method: \"chat.history\", usage: \"chat history <chat> [--limit n] [--before id|ms] [--after id|ms]\", summary: \"Messages, oldest first (a page)\",\n args: [\"chat\"],\n options: { limit: { type: \"number\", description: \"Messages per page (default 50)\" }, before: { type: \"string\", description: \"Only before this message id or timestamp\" }, after: { type: \"string\", description: \"Only after this message id or timestamp\" } },\n params: ({ options }, { chat }) => ({ chat, limit: options.limit, before: cursor(options.before), after: cursor(options.after) }),\n },\n \"chat list\": { method: \"chat.list\", usage: \"chat list [--show-secret]\", summary: \"Chats, newest first (a pending invite code only with --show-secret)\", options: showInvite, params: ({ options }) => secret(options) },\n \"chat remove\": {\n method: \"chat.remove\", usage: \"chat remove <chat> --yes\", summary: \"Delete a chat, its keys and history on this device\", args: [\"chat\"],\n options: { yes: { type: \"boolean\", description: \"Confirm\" } }, params: ({ options }, { chat }) => ({ chat, yes: options.yes === true }),\n },\n \"chat rename\": { method: \"chat.rename\", usage: \"chat rename <chat> <name...>\", summary: \"Name a chat on this side (empty: the contact's name)\", args: [\"chat\", \"name...?\"], params: (_, { chat, name }) => ({ chat, name: name ?? \"\" }) },\n \"chat show\": { method: \"chat.get\", usage: \"chat show <chat> [--show-secret]\", summary: \"One chat and its connection (its pending invite code only with --show-secret)\", args: [\"chat\"], options: showInvite, params: ({ options }, { chat }) => ({ chat, ...secret(options) }) },\n \"chat transport\": { method: \"chat.transport\", usage: \"chat transport <chat> <auto|dht|webrtc|iroh|hyperdht>\", summary: \"Choose what carries a chat\", args: [\"chat\", \"transport\"], params: (_, a) => ({ chat: a.chat, transport: a.transport }) },\n \"chat verify\": {\n method: \"chat.verify\", usage: \"chat verify <chat> --code <code>\", summary: \"Mark a contact verified after comparing codes\", args: [\"chat\"],\n options: { code: { type: \"string\", description: \"The code the contact reads out\" } }, params: ({ options }, { chat }) => {\n if (typeof options.code !== \"string\" || !options.code.trim()) throw new CliError(\"usage\", \"--code is needed: ghostly chat verify <chat> --code <code>\");\n return { chat, code: options.code };\n },\n },\n \"chat wait\": {\n method: \"chat.wait\", usage: \"chat wait <chat> [--until live|text|paired] [--timeout s]\", summary: \"Wait for a chat to reach a state\", args: [\"chat\"],\n options: { until: { type: \"string\", description: \"live (default), text or paired\" }, timeout: { type: \"number\", description: \"Seconds (default 60)\" } },\n params: ({ options }, { chat }) => ({ chat, until: options.until, timeout: options.timeout }),\n },\n \"forward\": {\n method: \"chat.forward\", usage: \"forward <chat|group> <message>... --to <chat|group>... [--force] [--wait none|sent] [--timeout s]\",\n summary: \"Forward messages (texts, files from the bytes here) to up to 5 chats and groups, as new messages of yours\",\n args: [\"chat\", \"message...\"],\n options: {\n to: { type: \"list\", description: \"A chat or a group to forward to (id, prefix or name; group:<id> for a group; a dash-leading id as it is); up to 5\" },\n force: { type: \"boolean\", description: \"Forward even if a text looks like a seed, a key or ecash\" },\n wait: { type: \"string\", description: \"none or sent: each text on its way (in a group, taken by an edge), each file's transfer done (default none)\" },\n timeout: groupWait.timeout,\n },\n params: ({ options }, a) => {\n if (!Array.isArray(options.to) || !options.to.length) throw new CliError(\"usage\", \"Missing --to: ghostly forward <chat|group> <message>... --to <chat|group>... [--force] [--wait none|sent] [--timeout s]\");\n return { chat: a.chat, messages: (a.message ?? \"\").split(\" \").filter(Boolean), to: options.to, force: options.force === true, wait: options.wait, timeout: options.timeout };\n },\n },\n \"invite create\": {\n method: \"invite.create\", usage: \"invite create [--label <name>]\", summary: \"A new chat's ghostly1 invite and its link\",\n options: { label: { type: \"string\", description: \"A name for the chat on this side\" } },\n params: ({ options }) => ({ label: options.label }),\n },\n \"invite join\": {\n method: \"invite.join\", usage: \"invite join <invite-or-link> [--label <name>]\", summary: \"Join a chat from its invite\",\n args: [\"invite\"], options: { label: { type: \"string\", description: \"A name for the chat on this side\" } },\n params: ({ options }, { invite }) => ({ invite, label: options.label }),\n },\n \"message delete\": { method: \"chat.delete\", usage: \"message delete <chat> <message>\", summary: \"Forget a message on this device (the contact keeps theirs)\", args: [\"chat\", \"message\"], params: (_, a) => ({ chat: a.chat, message: a.message }) },\n \"message details\": { method: \"chat.details\", usage: \"message details <chat> <message>\", summary: \"How a message travelled\", args: [\"chat\", \"message\"], params: (_, a) => ({ chat: a.chat, message: a.message }) },\n \"message retry\": { method: \"chat.retry\", usage: \"message retry <chat> <message>\", summary: \"Send a failed message again\", args: [\"chat\", \"message\"], params: (_, a) => ({ chat: a.chat, message: a.message }) },\n \"pin\": {\n method: \"chat.pin\", usage: \"pin <chat|group> <message> | --remove\", summary: \"Pin a message at the top of a chat or group (one per chat: it replaces the pinned one); --remove unpins\",\n args: [\"chat\", \"message...?\"], options: { remove: { type: \"boolean\", description: \"Unpin the chat's pinned message (it names no message)\" } },\n params: ({ options }, a) => {\n // --remove names no message: the chat has one pin. One given beside it is taken as before, and not read.\n if (a.message === undefined && options.remove !== true) throw new CliError(\"usage\", \"Missing <message>: ghostly pin <chat|group> <message> | --remove\");\n if (a.message?.includes(\" \")) throw new CliError(\"usage\", \"Too many arguments: ghostly pin <chat|group> <message> | --remove\");\n return { chat: a.chat, message: a.message, remove: options.remove === true };\n },\n },\n \"react\": {\n method: \"chat.react\", usage: \"react <chat> <message> <emoji> [--remove]\", summary: \"React to a message with one emoji (it replaces yours); --remove takes yours back\",\n args: [\"chat\", \"message\", \"emoji...?\"], options: { remove: { type: \"boolean\", description: \"Take your reaction back\" } },\n params: ({ options }, a) => ({ chat: a.chat, message: a.message, emoji: a.emoji, remove: options.remove === true }),\n },\n \"routine send\": {\n method: \"routine.send\", usage: \"routine send <chat|group> --name <name> --schedule \\\"every day 01:00\\\" [--cron \\\"0 1 * * *\\\"] [--state active|paused] [--next <time>] [--run ok|failed|skipped[:summary]] [--link url]... [--id id] [--json json|-|file] [--force] [--wait none|sent|delivered] [--timeout s]\",\n summary: \"Send a routine card (something a bot runs on a schedule: its last run and the next) to a chat or group\",\n args: [\"chat\"],\n options: { id: { type: \"string\", description: \"The routine's id, kept across updates (default: made up)\" }, ...routineOptions, ...groupWait, wait: wait.wait },\n params: ({ options }, { chat }) => ({ chat, card: needs(routineFields(options), [[\"name\", \"name\"], [\"schedule\", \"schedule\"]], \"routine send <chat|group> --name <name> --schedule \\\"every day 01:00\\\"\"), run: runOf(options.run), text: options.text, force: options.force === true, wait: options.wait, timeout: options.timeout }),\n },\n \"routine update\": {\n method: \"routine.update\", usage: \"routine update <chat|group> <routine> [--run ok|failed|skipped[:summary]] [--next <time>] [--state active|paused] [--schedule ...] [--json json|-|file] [--force] [--wait none|confirmed|sent] [--timeout s]\",\n summary: \"Update a routine card you sent: record a run, the next one, a pause (at most one update per 2.5 s; sooner ones merge)\",\n args: [\"chat\", \"routine\"],\n options: { ...routineOptions, wait: { type: \"string\", description: \"none (default), confirmed (a chat's contact took it) or sent (a group's edge took it)\" }, timeout: groupWait.timeout },\n params: ({ options }, a) => ({ chat: a.chat, routine: a.routine, card: routineFields(options), run: runOf(options.run), text: options.text, force: options.force === true, wait: options.wait, timeout: options.timeout }),\n },\n \"task send\": {\n method: \"task.send\", usage: \"task send <chat|group> --title <title> [--status s] [--progress n | --steps a/b] [--step \\\"...\\\"] [--item state:text]... [--pr-url url --pr-number n --additions n --deletions n --files n --pr-state s --pr-checks c] [--branch b] [--tag label]... [--parent task] [--link url]... [--id id] [--json json|-|file] [--force] [--wait none|sent|delivered] [--timeout s]\",\n summary: \"Send a task card (a bot's work: status, progress, its PR) to a chat or group; prints its id and message\",\n args: [\"chat\"],\n options: { id: { type: \"string\", description: \"The task's id, kept across updates (default: made up)\" }, ...taskOptions, ...groupWait, wait: wait.wait },\n params: ({ options }, { chat }) => ({ chat, card: prLinked(needs(taskFields(options), [[\"title\", \"title\"]], \"task send <chat|group> --title <title>\"), options, \"task send <chat|group> --title <title> --pr-url <url>\"), text: options.text, force: options.force === true, wait: options.wait, timeout: options.timeout }),\n },\n \"task update\": {\n method: \"task.update\", usage: \"task update <chat|group> <task> [--status s] [--progress n | --steps a/b] [--step \\\"...\\\"] [--item state:text]... [--pr-url ...] [--pr-state s] [--pr-checks c] [--tag label]... [--parent task] [--json json|-|file] [--force] [--wait none|confirmed|sent] [--timeout s]\",\n summary: \"Update a task card you sent: the fields given, merged over it (at most one update per 2.5 s; sooner ones merge)\",\n args: [\"chat\", \"task\"],\n options: { ...taskOptions, wait: { type: \"string\", description: \"none (default), confirmed (a chat's contact took it) or sent (a group's edge took it)\" }, timeout: groupWait.timeout },\n params: ({ options }, a) => ({ chat: a.chat, task: a.task, card: taskFields(options), text: options.text, force: options.force === true, wait: options.wait, timeout: options.timeout }),\n },\n \"typing\": {\n method: \"chat.typing\", usage: \"typing <chat> [--kind typing|recording|thinking] [--status \\\"<text>\\\"] [--for s] [--stop]\",\n summary: \"Show the contact you are writing, recording or thinking (live chats; it fades after 6 s unless said again, or kept with --for; a send or --stop ends it)\", args: [\"chat\"],\n options: {\n stop: { type: \"boolean\", description: \"Say you stopped\" },\n kind: { type: \"string\", description: \"typing (default), recording (a voice note) or thinking\" },\n status: { type: \"string\", description: \"A short line shown instead, e.g. \\\"Transcribing your audio…\\\" (40 characters, no links)\" },\n for: { type: \"number\", description: \"Keep saying it for this many seconds (up to 600), until a send or --stop\" },\n },\n params: ({ options }, { chat }) => ({ chat, stop: options.stop === true, kind: options.kind, status: options.status, for: typingFor(options) }),\n },\n \"usage send\": {\n method: \"usage.send\", usage: \"usage send [<chat|group>] [--all] --left <percent> [--window \\\"5 h\\\"] [--resets <time>] [--label Claude] [--account <name>] [--also week=80@<time>]... [--used n --limit n] [--id id] [--json json|-|file] [--force] [--wait none|sent] [--timeout s]\",\n summary: \"Say how much of your quota is left (a bot's usage card, one per chat, updated in place); --all for every 1:1 chat\",\n args: [\"chat?\"],\n options: { ...usageOptions, ...groupWait },\n params: ({ options }, { chat }) => {\n if (!chat && options.all !== true) throw new CliError(\"usage\", \"Name a chat or group, or give --all: ghostly usage send <chat|--all> --left <percent>\");\n return { chat, all: options.all === true, card: quota(usageFields(options), \"usage send <chat|--all> --left <percent>\"), text: options.text, force: options.force === true, wait: options.wait, timeout: options.timeout };\n },\n },\n};\n","import { GROUP_FILE_LIMITS } from \"@ghostly/core\";\nimport { type Command, here, reply } from \"./shared\";\n\n/** Files: one entry per command, in alphabetical order (test/commands.test.ts checks). */\nexport const commands: Record<string, Command> = {\n \"file accept\": { method: \"file.action\", usage: \"file accept [<chat>] <file>\", summary: \"Take a file the contact offers, or download a group's file that did not come by itself (<chat> may be the group)\", args: [\"chat?\", \"file\"], params: (_, a) => ({ chat: a.chat, file: a.file, action: \"accept\" }) },\n \"file cancel\": { method: \"file.action\", usage: \"file cancel [<chat>] <file>\", summary: \"Cancel a transfer\", args: [\"chat?\", \"file\"], params: (_, a) => ({ chat: a.chat, file: a.file, action: \"cancel\" }) },\n \"file decline\": { method: \"file.action\", usage: \"file decline [<chat>] <file>\", summary: \"Refuse a file the contact offers\", args: [\"chat?\", \"file\"], params: (_, a) => ({ chat: a.chat, file: a.file, action: \"decline\" }) },\n \"file list\": { method: \"file.list\", usage: \"file list <chat|group>\", summary: \"A chat's or a group's files and their transfers\", args: [\"chat\"], params: (_, { chat }) => ({ chat }) },\n \"file pause\": { method: \"file.action\", usage: \"file pause [<chat>] <file>\", summary: \"Pause a transfer\", args: [\"chat?\", \"file\"], params: (_, a) => ({ chat: a.chat, file: a.file, action: \"pause\" }) },\n \"file request\": { method: \"file.action\", usage: \"file request [<chat>] <file>\", summary: \"Ask again for a file that stopped arriving: it goes on from what is here (a group's file: asked of whoever holds it)\", args: [\"chat?\", \"file\"], params: (_, a) => ({ chat: a.chat, file: a.file, action: \"request\" }) },\n \"file resend\": { method: \"file.action\", usage: \"file resend [<chat>] <file>\", summary: \"Send again a file that stopped moving or failed: it goes on from what the contact holds\", args: [\"chat?\", \"file\"], params: (_, a) => ({ chat: a.chat, file: a.file, action: \"resend\" }) },\n \"file resume\": { method: \"file.action\", usage: \"file resume [<chat>] <file>\", summary: \"Resume a transfer\", args: [\"chat?\", \"file\"], params: (_, a) => ({ chat: a.chat, file: a.file, action: \"resume\" }) },\n \"file save\": {\n method: \"file.save\", usage: \"file save [<chat>] <file> [--dir d | --path p] [--force] [--wait [--timeout s]]\", summary: \"Write a received file to disk (never over a file without --force; --wait: once it is all here)\",\n args: [\"chat?\", \"file\"],\n options: {\n dir: { type: \"string\", description: \"Into this folder, under its own name\" }, path: { type: \"string\", description: \"To this path\" }, force: { type: \"boolean\", description: \"Replace a file there\" },\n wait: { type: \"boolean\", description: \"Wait for the transfer to finish first\" }, timeout: { type: \"number\", description: \"Seconds --wait waits (default 300)\" },\n },\n params: ({ options }, a) => ({\n chat: a.chat, file: a.file, dir: here(options.dir ?? \".\"), path: options.path === undefined ? undefined : here(options.path), force: options.force === true,\n wait: options.wait === true ? true : undefined, timeout: options.timeout,\n }),\n },\n \"file send\": {\n method: \"file.send\", usage: \"file send <chat|group> <path> [--name n] [--mime t] [--voice [ms] [--peaks 0,40,…]] [--reply <message>]\", summary: `Send a file (or, with --voice, a voice note), as a reply with --reply; to a group, every member's app fetches it (up to ${GROUP_FILE_LIMITS.maxBytes / 1024 / 1024} MiB)`,\n args: [\"chat\", \"path\"], options: { name: { type: \"string\", description: \"The name the contact (or the group) sees\" }, mime: { type: \"string\", description: \"Its type (default: from the extension)\" }, voice: { type: \"number\", optionalValue: true, description: \"A voice note; its length in milliseconds (default: measured from the sound)\" }, peaks: { type: \"string\", description: \"Loudness bars 0-255, comma-separated (default: measured from the sound)\" }, reply },\n params: ({ options }, a) => ({ chat: a.chat, path: here(a.path), name: options.name, mime: options.mime, voice: options.voice, peaks: typeof options.peaks === \"string\" ? options.peaks.split(\",\") : undefined, reply: options.reply }),\n },\n \"file wait\": {\n method: \"file.wait\", usage: \"file wait [<chat>] <file> [--timeout s]\", summary: \"Wait until a transfer is done (exit 0) or failed (exit 1, with its error)\",\n args: [\"chat?\", \"file\"], options: { timeout: { type: \"number\", description: \"Seconds (default 300)\" } },\n params: ({ options }, a) => ({ chat: a.chat, file: a.file, timeout: options.timeout }),\n },\n};\n","import { CliError } from \"../errors\";\nimport { type Command, here } from \"./shared\";\n\n/** The profile's status, its picture, the event journal: one entry per command, in alphabetical order (test/commands.test.ts checks). */\nexport const commands: Record<string, Command> = {\n \"events\": {\n method: \"events.replay\", usage: \"events [--since seq]\", summary: \"Events the journal holds, without following\",\n options: { since: { type: \"number\", description: \"Only events after this seq\" } }, params: ({ options }) => ({ since: options.since ?? 0 }),\n },\n \"profile picture\": {\n method: \"profile.picture\", usage: \"profile picture <jpeg> | --clear\", summary: \"The picture contacts see (a square JPEG, 128 px is what the app sends)\", args: [\"path...?\"],\n options: { clear: { type: \"boolean\", description: \"Remove the picture\" } },\n params: ({ options }, a) => {\n if (a.path === undefined && options.clear !== true) throw new CliError(\"usage\", \"Missing <jpeg>: ghostly profile picture <jpeg> | --clear\");\n if (a.path !== undefined && options.clear === true) throw new CliError(\"usage\", \"--clear removes the picture: not with a <jpeg>\");\n return { path: here(a.path), clear: options.clear === true };\n },\n },\n \"status\": { method: \"status\", usage: \"status\", summary: \"The profile, its chats and whether a daemon runs it\" },\n};\n","import { CliError } from \"../errors\";\nimport { type Command, here, showSecret, secret, cursor, typingFor } from \"./shared\";\n\n/** Groups: one entry per command, in alphabetical order (test/commands.test.ts checks). */\nexport const commands: Record<string, Command> = {\n \"group accept\": { method: \"group.accept\", usage: \"group accept <group>\", summary: \"Accept an invitation to a group\", args: [\"group\"], params: (_, { group }) => ({ group }) },\n \"group admin\": {\n method: \"group.admin\", usage: \"group admin <group> <member> [--show-secret]\", summary: \"Make a member the admin (admin)\", args: [\"group\", \"member\"],\n options: showSecret, params: ({ options }, a) => ({ group: a.group, member: a.member, ...secret(options) }),\n },\n \"group create\": {\n method: \"group.create\", usage: \"group create <name...> [--mesh]\", summary: \"A community (a link anyone can open) or, with --mesh, a private group of up to 32\",\n args: [\"name...\"], options: { mesh: { type: \"boolean\", description: \"A private mesh of contacts instead of a community\" } },\n params: ({ options }, { name }) => ({ name, profile: options.mesh ? \"mesh\" : \"community\" }),\n },\n \"group decline\": { method: \"group.decline\", usage: \"group decline <group>\", summary: \"Decline an invitation to a group\", args: [\"group\"], params: (_, { group }) => ({ group }) },\n \"group forget\": {\n method: \"group.forget\", usage: \"group forget <group> --yes\", summary: \"Leave and delete a group's history here\", args: [\"group\"],\n options: { yes: { type: \"boolean\", description: \"Confirm\" } }, params: ({ options }, { group }) => ({ group, yes: options.yes === true }),\n },\n \"group history\": {\n method: \"group.history\", usage: \"group history <group> [--limit n] [--before id|ms] [--after id|ms]\", summary: \"A group's messages (a page)\", args: [\"group\"],\n options: { limit: { type: \"number\", description: \"Messages per page (default 50)\" }, before: { type: \"string\", description: \"Only before this message id or timestamp\" }, after: { type: \"string\", description: \"Only after this message id or timestamp\" } },\n params: ({ options }, { group }) => ({ group, limit: options.limit, before: cursor(options.before), after: cursor(options.after) }),\n },\n \"group hub\": {\n method: \"group.hub\", usage: \"group hub <group> <member> [--pin | --exclude | --auto] [--show-secret]\",\n summary: \"Pin a member as a hub of a private group past 16 members, exclude one, or leave it to their app (admin)\", args: [\"group\", \"member\"],\n options: { ...showSecret, pin: { type: \"boolean\", description: \"Always a hub while online\" }, exclude: { type: \"boolean\", description: \"Never a hub\" }, auto: { type: \"boolean\", description: \"A hub if their app stays online (the default)\" } },\n params: ({ options }, a) => {\n const roles = ([\"pin\", \"exclude\", \"auto\"] as const).filter((role) => options[role] === true);\n if (roles.length > 1) throw new CliError(\"usage\", \"--pin, --exclude or --auto: one of them\");\n return { group: a.group, member: a.member, role: roles[0] ?? \"auto\", ...secret(options) };\n },\n },\n \"group invite\": {\n method: \"group.invite\", usage: \"group invite <group> <chat> [--show-secret]\", summary: \"Invite a contact into a group you administer\", args: [\"group\", \"chat\"],\n options: showSecret, params: ({ options }, a) => ({ group: a.group, chat: a.chat, ...secret(options) }),\n },\n \"group join\": { method: \"group.join\", usage: \"group join <link>\", summary: \"Join a group by its link\", args: [\"link\"], params: (_, { link }) => ({ link }) },\n \"group leave\": { method: \"group.leave\", usage: \"group leave <group>\", summary: \"Leave a group\", args: [\"group\"], params: (_, { group }) => ({ group }) },\n \"group link\": {\n method: \"group.link\", usage: \"group link <group> [--off] [--reset]\", summary: \"Turn the group's link on (--reset: a new one), or --off\", args: [\"group\"],\n options: { off: { type: \"boolean\", description: \"Turn the link off\" }, reset: { type: \"boolean\", description: \"Replace it: the old link stops working\" } },\n params: ({ options }, a) => {\n if (options.off === true && options.reset === true) throw new CliError(\"usage\", \"--off turns the link off: not with --reset\");\n return { group: a.group, on: options.off !== true, reset: options.reset === true };\n },\n },\n \"group list\": { method: \"group.list\", usage: \"group list [--show-secret]\", summary: \"Groups and invitations\", options: showSecret, params: ({ options }) => secret(options) },\n \"group picture\": {\n method: \"group.picture\", usage: \"group picture <group> <jpeg> | --clear\", summary: \"Set or clear the group's picture (admin; a square JPEG)\", args: [\"group\", \"path...?\"],\n options: { clear: { type: \"boolean\", description: \"Remove the picture\" } },\n params: ({ options }, a) => {\n if (a.path === undefined && options.clear !== true) throw new CliError(\"usage\", \"Missing <jpeg>: ghostly group picture <group> <jpeg> | --clear\");\n if (a.path !== undefined && options.clear === true) throw new CliError(\"usage\", \"--clear removes the picture: not with a <jpeg>\");\n return { group: a.group, path: here(a.path), clear: options.clear === true };\n },\n },\n \"group react\": {\n method: \"group.react\", usage: \"group react <group> <message> <emoji> [--remove]\", summary: \"React to a group's message with one emoji; --remove takes yours back\",\n args: [\"group\", \"message\", \"emoji...?\"], options: { remove: { type: \"boolean\", description: \"Take your reaction back\" } },\n params: ({ options }, a) => ({ group: a.group, message: a.message, emoji: a.emoji, remove: options.remove === true }),\n },\n \"group remove\": {\n method: \"group.remove\", usage: \"group remove <group> <member> [--show-secret]\", summary: \"Remove a member (admin)\", args: [\"group\", \"member\"],\n options: showSecret, params: ({ options }, a) => ({ group: a.group, member: a.member, ...secret(options) }),\n },\n \"group rename\": {\n method: \"group.rename\", usage: \"group rename <group> <name...> [--show-secret]\", summary: \"Rename the group (admin; the picture stays)\", args: [\"group\", \"name...\"],\n options: showSecret, params: ({ options }, a) => ({ group: a.group, name: a.name, ...secret(options) }),\n },\n \"group rotate\": {\n method: \"group.rotate\", usage: \"group rotate <group> [--show-secret]\", summary: \"A fresh group secret, members unchanged (admin)\", args: [\"group\"],\n options: showSecret, params: ({ options }, a) => ({ group: a.group, ...secret(options) }),\n },\n \"group show\": {\n method: \"group.get\", usage: \"group show <group> [--show-secret]\", summary: \"A group and its members (its entry link only with --show-secret)\", args: [\"group\"],\n options: showSecret, params: ({ options }, { group }) => ({ group, ...secret(options) }),\n },\n \"group typing\": {\n method: \"group.typing\", usage: \"group typing <group> [--kind typing|recording|thinking] [--status \\\"<text>\\\"] [--for s] [--stop]\",\n summary: \"Show the members you are writing, recording or thinking (private groups; it fades after 6 s unless said again, or kept with --for; a send or --stop ends it)\", args: [\"group\"],\n options: {\n stop: { type: \"boolean\", description: \"Say you stopped\" },\n kind: { type: \"string\", description: \"typing (default), recording (a voice note) or thinking\" },\n status: { type: \"string\", description: \"A short line shown instead, e.g. \\\"Reading the thread…\\\" (40 characters, no links)\" },\n for: { type: \"number\", description: \"Keep saying it for this many seconds (up to 600), until a send or --stop\" },\n },\n params: ({ options }, { group }) => ({ group, stop: options.stop === true, kind: options.kind, status: options.status, for: typingFor(options) }),\n },\n};\n","import { type Command, here } from \"./shared\";\n\n/** Identity proofs: one entry per command, in alphabetical order (test/commands.test.ts checks). */\nexport const commands: Record<string, Command> = {\n \"identity cancel\": { method: \"identity.cancel\", usage: \"identity cancel <draft>\", summary: \"Drop a proof not finished\", args: [\"draft\"], params: (_, a) => ({ draft: a.draft }) },\n \"identity complete\": {\n method: \"identity.complete\", usage: \"identity complete <draft> [--evidence-file f | --stdin]\", summary: \"Finish a proof: what the tool printed, or nothing for a published record\",\n args: [\"draft\"], options: { \"evidence-file\": { type: \"string\", description: \"The tool's output, from this file\" }, stdin: { type: \"boolean\", description: \"The tool's output, from stdin\" } },\n params: ({ options }, a) => ({ draft: a.draft, evidenceFile: here(options[\"evidence-file\"]), stdin: options.stdin === true }),\n },\n \"identity contact\": { method: \"identity.contact\", usage: \"identity contact <chat>\", summary: \"What a contact shared, as this device checked it\", args: [\"chat\"], params: (_, a) => ({ chat: a.chat }) },\n \"identity list\": { method: \"identity.list\", usage: \"identity list\", summary: \"This profile's identity proofs\" },\n \"identity providers\": { method: \"identity.providers\", usage: \"identity providers\", summary: \"Kinds of identity proof, and which signers work here\" },\n \"identity recheck\": { method: \"identity.recheck\", usage: \"identity recheck <chat> <id>\", summary: \"Check a contact's proof again\", args: [\"chat\", \"id\"], params: (_, a) => ({ chat: a.chat, id: a.id }) },\n \"identity remove\": { method: \"identity.remove\", usage: \"identity remove <id>\", summary: \"Remove a proof: withdrawn from every chat, revoked\", args: [\"id\"], params: (_, a) => ({ id: a.id }) },\n \"identity share\": { method: \"identity.share\", usage: \"identity share <chat> <id>\", summary: \"Show a proof to a contact\", args: [\"chat\", \"id\"], params: (_, a) => ({ chat: a.chat, id: a.id }) },\n \"identity withdraw\": { method: \"identity.withdraw\", usage: \"identity withdraw <chat> <id>\", summary: \"Withdraw a proof from a contact\", args: [\"chat\", \"id\"], params: (_, a) => ({ chat: a.chat, id: a.id }) },\n};\n","import { type Command, net, card, memo, confirmReal, sats, pairs } from \"./shared\";\n\n/** Wallets and payments: one entry per command, in alphabetical order (test/commands.test.ts checks). */\nexport const commands: Record<string, Command> = {\n \"chat accept\": {\n method: \"chat.accept\", usage: \"chat accept <chat> <method> [--off] [--networks mainnet,testnet]\", summary: \"Which ways of paying a chat takes\",\n args: [\"chat\", \"method\"], options: { off: { type: \"boolean\", description: \"Stop taking it\" }, networks: { type: \"string\", description: \"Comma-separated networks it is taken on\" } },\n params: ({ options }, a) => ({ chat: a.chat, method: a.method, on: options.off !== true, networks: typeof options.networks === \"string\" ? options.networks.split(\",\").map((n) => n.trim()).filter(Boolean) : undefined }),\n },\n \"chat pay\": {\n method: \"chat.pay\", usage: \"chat pay <chat> <sats> [--memo t] [--network testnet] [--confirm-real]\", summary: \"Send ecash to a contact (Mainnet needs --confirm-real)\",\n args: [\"chat\", \"amount\"], options: { memo, network: net, \"confirm-real\": confirmReal },\n params: ({ options }, a) => ({ chat: a.chat, amount: sats(a.amount), memo: options.memo, network: options.network, confirmReal: options[\"confirm-real\"] === true }),\n },\n \"chat pay-request\": {\n method: \"chat.payRequest\", usage: \"chat pay-request <chat> <payment> [--via lightning] [--max-fee sats] [--card <id>] [--confirm-real]\", summary: \"Pay a contact's request\",\n args: [\"chat\", \"payment\"], options: { via: { type: \"string\", description: \"lightning: pay its invoice instead of ecash\" }, \"max-fee\": { type: \"number\", description: \"Refuse a fee above this\" }, card, \"confirm-real\": confirmReal },\n params: ({ options }, a) => ({ chat: a.chat, payment: a.payment, via: options.via, maxFee: options[\"max-fee\"], card: options.card, confirmReal: options[\"confirm-real\"] === true }),\n },\n \"chat request\": {\n method: \"chat.request\", usage: \"chat request <chat> <sats> [--memo t] [--method m] [--rail cashu|lightning] [--network testnet] [--card <id>]\", summary: \"Ask a contact to pay\",\n args: [\"chat\", \"amount\"], options: { memo, method: { type: \"string\", description: \"cashu, arkade, usdt, bitcoin, spark, …\" }, rail: { type: \"string\", description: \"cashu or lightning\" }, network: net, card },\n params: ({ options }, a) => ({ chat: a.chat, amount: sats(a.amount), memo: options.memo, method: options.method, rail: options.rail, network: options.network, card: options.card }),\n },\n \"lightning default\": { method: \"lightning.default\", usage: \"lightning default <card> [--network testnet]\", summary: \"The Lightning card that receives by default\", args: [\"card\"], options: { network: net }, params: ({ options }, { card: c }) => ({ card: c, network: options.network }) },\n \"lightning rename\": { method: \"lightning.rename\", usage: \"lightning rename <card> <name...> [--network testnet]\", summary: \"Name a Lightning card\", args: [\"card\", \"name...\"], options: { network: net }, params: ({ options }, a) => ({ card: a.card, name: a.name, network: options.network }) },\n \"pay\": {\n method: \"pay\", usage: \"pay <invoice|lightning-address|lnurl> [--amount sats] [--network n] [--max-fee sats] [--card <id>] [--comment t] [--confirm-real]\", summary: \"Pay over Lightning from Testnet, or --network mainnet with --confirm-real\",\n args: [\"target\"], options: { amount: { type: \"number\", description: \"Sats, for an address or LNURL\" }, network: net, \"max-fee\": { type: \"number\", description: \"Refuse a fee above this\" }, card, comment: { type: \"string\", description: \"For the address's owner\" }, \"confirm-real\": confirmReal },\n params: ({ options }, { target }) => ({ target, amount: options.amount, network: options.network, maxFee: options[\"max-fee\"], card: options.card, comment: options.comment, confirmReal: options[\"confirm-real\"] === true }),\n },\n \"payment check\": { method: \"payment.check\", usage: \"payment check <chat> <payment>\", summary: \"Paid from another wallet: the contact's app looks now\", args: [\"chat\", \"payment\"], params: (_, a) => ({ chat: a.chat, payment: a.payment }) },\n \"payment list\": { method: \"payment.list\", usage: \"payment list [--chat <chat>]\", summary: \"Payments and requests, newest first\", options: { chat: { type: \"string\", description: \"Only this chat's\" } }, params: ({ options }) => ({ chat: options.chat }) },\n \"payment reclaim\": { method: \"payment.reclaim\", usage: \"payment reclaim <payment>\", summary: \"Take back ecash the contact has not taken\", args: [\"payment\"], params: (_, a) => ({ payment: a.payment }) },\n \"wallet add-mint\": {\n method: \"wallet.mint.add\", usage: \"wallet add-mint <url> [--primary]\", summary: \"Add a Cashu mint (a local or test mint is Testnet's)\",\n args: [\"url\"], options: { primary: { type: \"boolean\", description: \"Make it the one invoices are created at\" } }, params: ({ options }, { url }) => ({ url, primary: options.primary === true }),\n },\n \"wallet address\": {\n method: \"wallet.address\", usage: \"wallet address <bitcoin|arkade|spark|usdt> [--network testnet]\", summary: \"An address to be paid on\",\n args: [\"type\"], options: { network: net }, params: ({ options }, { type }) => ({ type, network: options.network }),\n },\n \"wallet create\": {\n method: \"wallet.create\", usage: \"wallet create <type> [--network testnet] [--provider <id>] [--value name=value]... [--stdin] [--invite <code>] [--api-key <key>]\", summary: \"A wallet: cashu, lightning, arkade, spark, bitcoin, fedimint, usdt (bark: app only)\",\n args: [\"type\"], options: { network: net, provider: { type: \"string\", description: \"Lightning or on-chain source (wallet list shows them)\" }, value: { type: \"list\", description: \"A field of the source's form, name=value\" }, stdin: { type: \"boolean\", description: \"Secret fields as name=value lines on stdin (api-key=… for the API key), kept out of ps\" }, invite: { type: \"string\", description: \"Fedimint: the federation's invite\" }, \"api-key\": { type: \"string\", description: \"Spark on Mainnet: your Breez API key (better on stdin: --stdin)\" } },\n params: ({ options }, { type }) => ({ type, network: options.network, provider: options.provider, invite: options.invite, apiKey: options[\"api-key\"], values: pairs(options.value), stdin: options.stdin === true ? true : undefined }),\n },\n \"wallet faucet\": {\n method: \"wallet.faucet\", usage: \"wallet faucet <type> [--card <id>]\", summary: \"Test coins from a Testnet wallet's faucet\",\n args: [\"type\"], options: { card }, params: ({ options }, { type }) => ({ type, network: \"testnet\", card: options.card }),\n },\n \"wallet history\": {\n method: \"wallet.history\", usage: \"wallet history [--network testnet] [--limit n]\", summary: \"A network's wallet history, newest first\",\n options: { network: net, limit: { type: \"number\", description: \"Entries (default 50)\" } }, params: ({ options }) => ({ network: options.network, limit: options.limit }),\n },\n \"wallet list\": {\n method: \"wallet.list\", usage: \"wallet list [--network mainnet|testnet]\", summary: \"Wallets with their balances, and what New can make\",\n options: { network: net }, params: ({ options }) => ({ network: options.network }),\n },\n \"wallet receive\": {\n method: \"wallet.receive\", usage: \"wallet receive <sats> [--network testnet] [--card <id>]\", summary: \"A Lightning invoice to be paid\",\n args: [\"amount\"], options: { network: net, card }, params: ({ options }, { amount }) => ({ amount: sats(amount), network: options.network, card: options.card }),\n },\n \"wallet redeem\": { method: \"wallet.redeem\", usage: \"wallet redeem [<cashu-token>]\", summary: \"Take the ecash of a Cashu token (its mint must be one of yours); the token on stdin keeps it out of ps\", args: [\"token?\"], params: (_, { token }) => ({ token }) },\n \"wallet remove\": {\n method: \"wallet.remove\", usage: \"wallet remove <type> [--network testnet] [--card <id>] [--accept-loss]\", summary: \"Remove a wallet; refused while it holds money or waits for some, unless --accept-loss\",\n args: [\"type\"], options: { network: net, card: card, \"accept-loss\": { type: \"boolean\", description: \"What it holds on this device is lost without its backup\" } },\n params: ({ options }, { type }) => ({ type, network: options.network, card: options.card, acceptLoss: options[\"accept-loss\"] === true }),\n },\n};\n","import { type Command } from \"./shared\";\n\n/** Shared local services: one entry per command, in alphabetical order (test/commands.test.ts checks). */\nexport const commands: Record<string, Command> = {\n \"service add\": { method: \"service.add\", usage: \"service add <name> <http://127.0.0.1:port>\", summary: \"A web app on this machine that contacts may be given\", args: [\"name\", \"target\"], params: (_, a) => ({ name: a.name, target: a.target }) },\n \"service close\": { method: \"service.close\", usage: \"service close <chat> <service>\", summary: \"Close a contact's app opened here\", args: [\"chat\", \"service\"], params: (_, a) => ({ chat: a.chat, service: a.service }) },\n \"service enable\": { method: \"service.enable\", usage: \"service enable <service> [--off]\", summary: \"Turn a shared app on or off\", args: [\"service\"], options: { off: { type: \"boolean\", description: \"Turn it off\" } }, params: ({ options }, a) => ({ service: a.service, off: options.off === true }) },\n \"service list\": { method: \"service.list\", usage: \"service list\", summary: \"Web apps this profile shares\" },\n \"service open\": { method: \"service.open\", usage: \"service open <chat> <service> [--port p]\", summary: \"A contact's app on a loopback port here (daemon)\", args: [\"chat\", \"service\"], options: { port: { type: \"number\", description: \"Local port (default: any free one)\" } }, params: ({ options }, a) => ({ chat: a.chat, service: a.service, port: options.port }) },\n \"service peer\": { method: \"service.peer\", usage: \"service peer <chat>\", summary: \"What a contact shares with you\", args: [\"chat\"], params: (_, a) => ({ chat: a.chat }) },\n \"service remove\": { method: \"service.remove\", usage: \"service remove <service>\", summary: \"Stop sharing a web app with anyone\", args: [\"service\"], params: (_, a) => ({ service: a.service }) },\n \"service share\": { method: \"service.share\", usage: \"service share <service> <chat> [--off]\", summary: \"Give (or take back) a contact access to a web app\", args: [\"service\", \"chat\"], options: { off: { type: \"boolean\", description: \"Take it back\" } }, params: ({ options }, a) => ({ service: a.service, chat: a.chat, off: options.off === true }) },\n};\n","import { CliError } from \"../errors\";\nimport { commands as apps } from \"./apps\";\nimport { commands as calls } from \"./calls\";\nimport { commands as chats } from \"./chats\";\nimport { commands as files } from \"./files\";\nimport { commands as general } from \"./general\";\nimport { commands as groups } from \"./groups\";\nimport { commands as identities } from \"./identities\";\nimport { commands as payments } from \"./payments\";\nimport { commands as services } from \"./services\";\nimport { type Command, wait, groupWait, force, reply, buttonOptions } from \"./shared\";\nimport type { OptionSpec } from \"../args\";\n\nexport type { Command };\n\n/**\n * The commands that are one API method each, one file per area of the app, each in alphabetical order. A new\n * command goes in its area's file at its place; a new area is a new file and its two lines here. The order here\n * is the order `ghostly help` lists them in.\n */\nexport const COMMANDS: Record<string, Command> = {\n ...general,\n ...chats,\n ...groups,\n ...files,\n ...payments,\n ...identities,\n ...services,\n ...calls,\n ...apps,\n};\n\n/** Commands that take text (argument or stdin), with the secret guard and delivery waits. */\nexport const TEXT_COMMANDS: Record<string, { method: string; target: \"chat\" | \"group\"; message?: true; usage: string; summary: string; args: string[]; options: Record<string, OptionSpec> }> = {\n \"send\": {\n method: \"chat.send\", target: \"chat\", args: [\"chat\", \"text...?\"], usage: \"send <chat> [text...] [--reply <message>] [--button id:Label]... [--style id=primary|neutral|danger]... [--once] [--id id] [--stdin] [--force] [--wait none|sent|delivered]\", summary: \"Send a message (text from arguments or stdin); --button puts buttons under it, each label its own (case aside)\",\n options: { stdin: { type: \"boolean\", description: \"Read the text from stdin\" }, force, reply, ...buttonOptions, ...wait },\n },\n \"edit\": {\n method: \"chat.edit\", target: \"chat\", message: true, args: [\"chat\", \"message\", \"text...?\"], usage: \"edit <chat> <message> [text... | --text <text> | --stdin] [--force] [--wait none|confirmed]\", summary: \"Replace the text of a message you sent (the id send gave; in a group, group edit)\",\n options: { stdin: { type: \"boolean\", description: \"Read the new text from stdin\" }, text: { type: \"string\", description: \"The new text, as one argument\" }, force, wait: { type: \"string\", description: \"none or confirmed (default: none with a daemon, confirmed without)\" }, timeout: wait.timeout },\n },\n \"group send\": {\n method: \"group.send\", target: \"group\", args: [\"group\", \"text...?\"], usage: \"group send <group> [text...] [--mention <member>]... [--reply <message>] [--button id:Label]... [--style id=primary|neutral|danger]... [--once] [--id id] [--stdin] [--force] [--wait none|sent] [--timeout s]\", summary: \"Send to a group; mention members written as @name in the text; --button puts buttons under it\",\n options: { stdin: { type: \"boolean\", description: \"Read the text from stdin\" }, force, reply, mention: { type: \"list\", description: \"A member (key, key prefix, name or everyone) named as @name in the text\" }, ...buttonOptions, ...groupWait },\n },\n \"group edit\": {\n method: \"group.edit\", target: \"group\", message: true, args: [\"group\", \"message\", \"text...?\"], usage: \"group edit <group> <message> [text... | --text <text> | --stdin] [--mention <member>]... [--force] [--wait none|sent] [--timeout s]\", summary: \"Replace the text of a message you sent to a group (the id group send gave)\",\n options: { stdin: { type: \"boolean\", description: \"Read the new text from stdin\" }, text: { type: \"string\", description: \"The new text, as one argument\" }, force, mention: { type: \"list\", description: \"A member newly named as @name in the text (those the message named stay)\" }, ...groupWait },\n },\n};\n\n\n/**\n * Positionals that name something by its id (a chat or a group also by a prefix or its name). The ids are the\n * engine's and the contacts', and drafts, payments and groups are base64url, which starts with `-` one time in 64:\n * in these a word that starts with a dash is the value. A mistyped flag there names nothing and fails as not found;\n * text, names and paths stay strict.\n */\nconst ID_ARGS = new Set([\"chat\", \"message\", \"group\", \"member\", \"payment\", \"file\", \"draft\", \"id\", \"card\", \"service\", \"call\"]);\n\n/** A leading positional that may be left out (`name?`); a trailing one that may (`text...?`) shifts nothing. */\nconst leadingOptional = (name: string) => name.endsWith(\"?\") && !name.endsWith(\"...?\");\n\n/**\n * Which of a command's positionals take an id, by index (a trailing `...` one covers the rest). An optional `name?`\n * shifts the others, so it counts only where it and what may come in its place are all ids.\n */\nexport function idSlot(command: { args?: string[] }): (index: number) => boolean {\n const names = (command.args ?? []).map((name) => name.replace(/\\?$/, \"\"));\n const shift = (command.args ?? []).filter(leadingOptional).length;\n const rest = names.findIndex((name) => name.endsWith(\"...\"));\n const at = (index: number) => {\n const name = rest !== -1 && index >= rest ? names[rest].slice(0, -3) : names[index];\n return name !== undefined && ID_ARGS.has(name);\n };\n return (index) => at(index) && (shift === 0 || index + shift >= names.length || at(index + shift));\n}\n\n/**\n * The positionals of a command, by name; missing required ones are a usage error. A leading `name?` is optional: it\n * is given only when every required one is there too (`file accept [<chat>] <file>`). A trailing `name...` takes the\n * rest and needs at least one word; `name...?` may take none (text from stdin, `chat rename`'s name back to the\n * contact's).\n */\nexport function positionals(command: { usage: string; args?: string[] }, values: readonly string[]): Record<string, string | undefined> {\n let names = command.args ?? [];\n const optional = names.filter(leadingOptional);\n if (optional.length) {\n const skip = Math.max(0, optional.length - Math.max(0, values.length - (names.length - optional.length)));\n names = [...optional.slice(skip).map((name) => name.slice(0, -1)), ...names.filter((name) => !leadingOptional(name))];\n }\n const out: Record<string, string | undefined> = {};\n for (let i = 0; i < names.length; i++) {\n const name = names[i].replace(/\\?$/, \"\");\n if (name.endsWith(\"...\")) {\n const rest = values.slice(i);\n if (!rest.length && !names[i].endsWith(\"?\")) throw new CliError(\"usage\", `Missing <${name.slice(0, -3)}>: ghostly ${command.usage}`);\n out[name.slice(0, -3)] = rest.length ? rest.join(\" \") : undefined;\n return out;\n }\n if (values[i] === undefined) throw new CliError(\"usage\", `Missing <${name}>: ghostly ${command.usage}`);\n out[name] = values[i];\n }\n noMoreThan(names.length, values, command.usage);\n return out;\n}\n\n/** Words past a usage's arguments are refused, never dropped: `profile use my bot` would select `my`. */\nexport function noMoreThan(count: number, values: readonly string[], usage: string): void {\n if (values.length > count) throw new CliError(\"usage\", `Too many arguments: ghostly ${usage}`);\n}\n","import type { GhostlyNode } from \"@ghostly/browser/engine/node\";\nimport type { EventHub } from \"./events\";\n\n/**\n * What the app's chat screen does by itself, done here for a profile no screen shows (apps/ui/src/hooks/useChat.ts): the side\n * that joined says \"👋 <name> joined\" once the chat first goes live, and the side that invited answers once. Contacts\n * see the bot arrive as they see a person; each is said once per chat, across restarts.\n */\nexport function announceJoins(hub: EventHub, node: GhostlyNode): () => void {\n const say = (chat: string) => {\n if (hub.said(chat, \"join\")) return;\n hub.markSaid(chat, \"join\");\n const nick = node.getState().settings.nick?.trim();\n void node.sendMessage({ linkId: chat, text: nick ? `👋 ${nick} joined` : \"👋 joined\" }).catch(() => {});\n };\n return hub.onEvent((event) => {\n const chat = event.chat as string | undefined;\n if (!chat) return;\n const link = node.getState().links.find((l) => l.id === chat);\n if (!link?.profile) return;\n // The joiner once its first pairing is live; the other side when a notice arrives (said once either way).\n if (event.type === \"chat.connection\" && event.live && link.pairingProgress?.role === \"joiner\") say(chat);\n else if (event.type === \"chat.joined\") say(chat);\n });\n}\n","/**\n * The audio a call hands to an outside program, and takes from it (WISP 1100 § Calls): raw PCM, signed 16-bit\n * little-endian, mono, at the call's rate, in frames of 20 ms. Opus runs at every one of these rates itself, so\n * nothing is resampled.\n */\nexport const FRAME_MS = 20;\nexport const CALL_RATES = [8000, 12000, 16000, 24000, 48000] as const;\nexport type CallRate = (typeof CALL_RATES)[number];\nexport const DEFAULT_RATE: CallRate = 48000;\n\nexport const frameSamples = (rate: CallRate): number => (rate * FRAME_MS) / 1000;\nexport const frameBytes = (rate: CallRate): number => frameSamples(rate) * 2;\nexport const bytesToMs = (rate: CallRate, bytes: number): number => Math.round((bytes / 2 / rate) * 1000);\n\nexport function isCallRate(value: unknown): value is CallRate {\n return CALL_RATES.includes(value as CallRate);\n}\n\n/** The most a program may queue ahead of the call: five minutes of speech. What goes past it is dropped. */\nexport const MAX_QUEUE_MS = 5 * 60_000;\n\n/**\n * A partial frame waits this long for the rest of it before it is played padded with silence: a program writing at\n * real time, a little late, is not cut into gaps; a program that stopped mid-frame is still heard to its end.\n */\nexport const PARTIAL_WAIT_MS = 60;\n\n/**\n * What the program wrote, waiting to be sent: taken one 20 ms frame per tick, silence when there is nothing, and\n * dropped whole by a flush (barge-in). The program may write any amount at any time, faster than real time too.\n */\nexport class PlaybackQueue {\n private chunks: Buffer[] = [];\n private head = 0;\n private length = 0;\n private lastPush = 0;\n private readonly frame: number;\n private readonly max: number;\n private readonly silence: Buffer;\n /** Bytes dropped because the queue was full, since the start. */\n dropped = 0;\n\n constructor(readonly rate: CallRate, private readonly now: () => number = Date.now, maxMs = MAX_QUEUE_MS) {\n this.frame = frameBytes(rate);\n // Whole samples only: an odd byte waits for its other half.\n this.max = Math.floor((rate * maxMs) / 1000) * 2;\n this.silence = Buffer.alloc(this.frame);\n }\n\n /** Adds what the program wrote. Returns the bytes it could not keep (the queue was full). */\n push(chunk: Buffer): number {\n if (!chunk.length) return 0;\n this.lastPush = this.now();\n const room = this.max - this.length;\n const kept = chunk.length <= room ? chunk : chunk.subarray(0, Math.max(0, room));\n const lost = chunk.length - kept.length;\n this.dropped += lost;\n if (kept.length) { this.chunks.push(Buffer.from(kept)); this.length += kept.length; }\n return lost;\n }\n\n /** Queued audio, in milliseconds (whole frames and a partial one). */\n get queuedMs(): number {\n return bytesToMs(this.rate, this.length - (this.length % 2));\n }\n\n /** One frame to send now, and whether it is the program's audio or silence. */\n next(): { frame: Buffer; silent: boolean } {\n if (this.length >= this.frame) return { frame: this.take(this.frame), silent: false };\n const whole = this.length - (this.length % 2);\n if (whole > 0 && this.now() - this.lastPush >= PARTIAL_WAIT_MS) {\n const frame = Buffer.alloc(this.frame);\n this.take(whole).copy(frame);\n return { frame, silent: false };\n }\n return { frame: this.silence, silent: true };\n }\n\n /** Drops everything queued; returns how much, in milliseconds. */\n flush(): number {\n const ms = this.queuedMs;\n this.chunks = [];\n this.head = 0;\n this.length = 0;\n return ms;\n }\n\n /** Drops up to that much from the front, in whole frames (audio whose time went by); returns how much, in milliseconds. */\n skip(ms: number): number {\n const frames = Math.min(Math.floor(ms / FRAME_MS), Math.floor(this.length / this.frame));\n if (frames > 0) this.take(frames * this.frame);\n return Math.max(0, frames) * FRAME_MS;\n }\n\n private take(bytes: number): Buffer {\n const out = Buffer.allocUnsafe(bytes);\n let filled = 0;\n while (filled < bytes) {\n const chunk = this.chunks[0];\n const n = Math.min(bytes - filled, chunk.length - this.head);\n chunk.copy(out, filled, this.head, this.head + n);\n filled += n;\n this.head += n;\n if (this.head === chunk.length) { this.chunks.shift(); this.head = 0; }\n }\n this.length -= bytes;\n return out;\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { chmodSync, existsSync, lstatSync, readdirSync, rmSync } from \"node:fs\";\nimport { createServer, type Server, type Socket } from \"node:net\";\nimport { dirname, join } from \"node:path\";\nimport { privateFolder } from \"../privateFolder\";\nimport { bytesToMs, type CallRate } from \"./pcm\";\n\n/**\n * A call's audio socket (WISP 1100 § Calls): `<profile>/calls/<call>.sock`, 0600, in a folder only the owner may\n * open. When that path is too long for a Unix socket: `/tmp/ghostly-calls-<hash of the profile>/<call>.sock`, in a\n * folder of the owner's, 0700, checked as such before use. A named pipe on Windows.\n */\nexport function audioSocketPath(profileDir: string, callId: string): string {\n const inside = join(profileDir, \"calls\", `${callId}.sock`);\n if (process.platform === \"win32\") return `\\\\\\\\.\\\\pipe\\\\ghostly-call-${createHash(\"sha256\").update(inside).digest(\"hex\").slice(0, 24)}`;\n if (Buffer.byteLength(inside) <= 100) return inside;\n return join(\"/tmp\", `ghostly-calls-${createHash(\"sha256\").update(profileDir).digest(\"hex\").slice(0, 24)}`, `${callId}.sock`);\n}\n\n/**\n * Removes the call sockets a daemon that died during a call left in this profile's folder (`audioSocketPath`; call\n * ids are 12 hex digits): no call outlives its daemon. A folder that is a link or someone else's is left alone.\n */\nexport function clearAudioSockets(profileDir: string): void {\n if (process.platform === \"win32\") return;\n const folder = dirname(audioSocketPath(profileDir, \"0\".repeat(12)));\n try {\n const info = lstatSync(folder);\n if (!info.isDirectory() || (process.getuid && info.uid !== process.getuid())) return;\n for (const entry of readdirSync(folder, { withFileTypes: true })) {\n if (entry.name.endsWith(\".sock\") && !entry.isDirectory()) rmSync(join(folder, entry.name), { force: true });\n }\n } catch {\n // No folder yet, or one it cannot read: nothing to clear.\n }\n}\n\n/** How far a program may fall behind reading the call's audio before frames are dropped for it. */\nexport const MAX_UNREAD_MS = 1000;\n\n/**\n * Serves one call's audio to one program at a time: what the program writes is handed to `onAudio` as it comes (raw\n * PCM, any amount); `write` gives it the caller's audio. A second connection replaces the first. `close` ends the\n * program's connection (it reads EOF) and removes the socket.\n */\nexport class AudioSocket {\n private client: Socket | null = null;\n private closed = false;\n /** Frames the program did not read in time, since the start. */\n dropped = 0;\n\n private constructor(readonly path: string, private readonly server: Server, private readonly rate: CallRate) {}\n\n static async open(path: string, rate: CallRate, handlers: { onAudio(chunk: Buffer): void; onClient?(connected: boolean): void }): Promise<AudioSocket> {\n if (process.platform !== \"win32\") {\n privateFolder(join(path, \"..\"), \"calls will not put their audio there\");\n if (existsSync(path)) rmSync(path, { force: true });\n }\n let self: AudioSocket | null = null;\n const server = createServer((socket) => self!.accept(socket, handlers));\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(path, () => { server.off(\"error\", reject); resolve(); });\n });\n if (process.platform !== \"win32\") chmodSync(path, 0o600);\n self = new AudioSocket(path, server, rate);\n return self;\n }\n\n get connected(): boolean {\n return !!this.client && !this.client.destroyed;\n }\n\n private accept(socket: Socket, handlers: { onAudio(chunk: Buffer): void; onClient?(connected: boolean): void }): void {\n if (this.closed) { socket.destroy(); return; }\n const before = this.client;\n this.client = socket;\n before?.end();\n socket.setNoDelay?.(true);\n socket.on(\"data\", (chunk: Buffer) => { if (this.client === socket) handlers.onAudio(chunk); });\n socket.on(\"error\", () => {});\n socket.on(\"close\", () => {\n if (this.client !== socket) return;\n this.client = null;\n handlers.onClient?.(false);\n });\n handlers.onClient?.(true);\n }\n\n /** One frame of the caller's audio for the program; dropped when nobody listens or the program stopped reading. */\n write(frame: Buffer): void {\n const client = this.client;\n if (!client || client.destroyed) return;\n if (bytesToMs(this.rate, client.writableLength) > MAX_UNREAD_MS) { this.dropped++; return; }\n client.write(frame);\n }\n\n /** Ends the program's connection (EOF once what was written is read) and removes the socket. */\n close(): Promise<void> {\n if (this.closed) return Promise.resolve();\n this.closed = true;\n const client = this.client;\n this.client = null;\n client?.end();\n if (client) setTimeout(() => client.destroy(), 2000).unref();\n return new Promise((resolve) => {\n this.server.close(() => resolve());\n if (process.platform !== \"win32\") rmSync(this.path, { force: true });\n });\n }\n}\n","import { randomBytes } from \"node:crypto\";\n\n/**\n * RTP for a call's Opus audio (RFC 3550, RFC 7587): libdatachannel runs ICE, DTLS and SRTP, and hands this side\n * plain RTP packets each way. The RTP clock of Opus is 48 kHz whatever the audio's own rate.\n */\nexport const OPUS_CLOCK = 48_000;\n/** RTP timestamp ticks in one 20 ms frame. */\nexport const TICKS_PER_FRAME = (OPUS_CLOCK * 20) / 1000;\n\nexport interface RtpPacket { payloadType: number; marker: boolean; sequence: number; timestamp: number; ssrc: number; payload: Buffer }\n\n/** Whether a packet on the muxed port is RTCP (RFC 5761 § 4: its second byte is 192-223), not RTP. */\nexport function isRtcp(packet: Buffer): boolean {\n return packet.length >= 2 && packet[1] >= 192 && packet[1] <= 223;\n}\n\n/** Reads an RTP packet: CSRCs, a header extension and padding are stepped over. Null for anything malformed. */\nexport function parseRtp(packet: Buffer): RtpPacket | null {\n if (packet.length < 12 || packet[0] >> 6 !== 2 || isRtcp(packet)) return null;\n let offset = 12 + (packet[0] & 0x0f) * 4;\n if (packet[0] & 0x10) {\n if (packet.length < offset + 4) return null;\n offset += 4 + packet.readUInt16BE(offset + 2) * 4;\n }\n let end = packet.length;\n if (packet[0] & 0x20) {\n const padding = packet[packet.length - 1];\n if (padding === 0) return null;\n end -= padding;\n }\n if (end < offset) return null;\n return {\n payloadType: packet[1] & 0x7f,\n marker: (packet[1] & 0x80) !== 0,\n sequence: packet.readUInt16BE(2),\n timestamp: packet.readUInt32BE(4),\n ssrc: packet.readUInt32BE(8),\n payload: packet.subarray(offset, end),\n };\n}\n\n/** Numbers this side's packets: one SSRC, a sequence and a timestamp from random starts. */\nexport class RtpWriter {\n private sequence = randomBytes(2).readUInt16BE(0);\n private timestamp = randomBytes(4).readUInt32BE(0);\n\n constructor(readonly ssrc: number, readonly payloadType: number) {}\n\n /** The next frame's packet. `marker` starts a talkspurt (the first sound after silence). */\n packet(payload: Buffer, marker = false): Buffer {\n const header = Buffer.allocUnsafe(12);\n header[0] = 0x80;\n header[1] = (marker ? 0x80 : 0) | (this.payloadType & 0x7f);\n header.writeUInt16BE(this.sequence, 2);\n header.writeUInt32BE(this.timestamp, 4);\n header.writeUInt32BE(this.ssrc >>> 0, 8);\n this.sequence = (this.sequence + 1) & 0xffff;\n this.timestamp = (this.timestamp + TICKS_PER_FRAME) >>> 0;\n return Buffer.concat([header, payload]);\n }\n}\n\n/** What the reorder buffer gives out: a packet's payload to decode, or a frame that never came. */\nexport type Playout = { payload: Buffer } | { lost: true };\n\n/** The most silence a gap in the packets becomes. */\nconst MAX_LOST_FRAMES = 3;\n\n/** Sequence numbers compared across the 16-bit wrap: positive when `a` comes after `b`. */\nexport function sequenceDelta(a: number, b: number): number {\n const d = (a - b) & 0xffff;\n return d >= 0x8000 ? d - 0x10000 : d;\n}\n\n/**\n * Puts packets back in order, holding at most `depth` of them for one that is late: a lost packet costs one frame\n * of silence (40 ms of waiting at most), a duplicate or a packet that came too late is dropped, and a new stream (its\n * SSRC changed, or its sequence jumped) starts over.\n */\nexport class ReorderBuffer {\n private expected: number | null = null;\n private ssrc: number | null = null;\n private readonly held = new Map<number, Buffer>();\n\n constructor(private readonly depth = 2) {}\n\n push(packet: RtpPacket): Playout[] {\n if (this.ssrc !== packet.ssrc || this.expected === null || Math.abs(sequenceDelta(packet.sequence, this.expected)) > 1000) {\n this.ssrc = packet.ssrc;\n this.expected = packet.sequence;\n this.held.clear();\n }\n const ahead = sequenceDelta(packet.sequence, this.expected);\n if (ahead < 0 || this.held.has(packet.sequence)) return [];\n this.held.set(packet.sequence, packet.payload);\n const out: Playout[] = [];\n this.release(out);\n // Still waiting for one that is late: once too many wait behind it, the gap is given up for lost. A long gap\n // (the network stalled) is not replayed as that much silence: a few frames of it mark the break.\n while (this.held.size > this.depth) {\n const next = [...this.held.keys()].reduce((a, b) => (sequenceDelta(a, b) < 0 ? a : b));\n const gap = sequenceDelta(next, this.expected);\n for (let i = 0; i < Math.min(gap, MAX_LOST_FRAMES); i++) out.push({ lost: true });\n this.expected = next;\n this.release(out);\n }\n return out;\n }\n\n private release(out: Playout[]): void {\n for (;;) {\n const payload = this.held.get(this.expected!);\n if (!payload) return;\n this.held.delete(this.expected!);\n out.push({ payload });\n this.expected = (this.expected! + 1) & 0xffff;\n }\n }\n}\n","import { FRAME_MS, frameBytes, frameSamples, PlaybackQueue, type CallRate } from \"./pcm\";\nimport { isRtcp, parseRtp, ReorderBuffer, RtpWriter } from \"./rtp\";\n\n/** The longest stall of the sender whose frames are still sent after it (see `catchUp`). */\nexport const MAX_CATCH_UP_MS = 1000;\n\n/**\n * Silence is encoded until its packet came out the same this many frames in a row (half a second): by then the\n * encoder gives that packet for as long as the silence lasts, and each encode (libopus in WebAssembly, 0.5 to 1 ms at\n * 48 kHz, 50 times a second) only finds it again. See `settle`.\n */\nexport const SETTLED_FRAMES = 25;\n\n/** An Opus encoder or decoder at one rate, mono (opusscript's shape). */\nexport interface OpusCodec {\n encode(pcm: Buffer, frameSize: number): Buffer;\n decode(packet: Buffer): Buffer;\n delete?(): void;\n}\n\n/** The call's audio lane: RTP packets out, and every packet (RTP and RTCP) that comes in. */\nexport interface RtpTrack {\n send(packet: Buffer): void;\n onPacket(listener: (packet: Buffer) => void): void;\n}\n\nexport interface CallAudioOptions {\n rate: CallRate;\n /** What the call's SDP gives Opus. */\n payloadType: number;\n ssrc: number;\n track: RtpTrack;\n encoder: OpusCodec;\n decoder: OpusCodec;\n /** A 20 ms frame of the contact's audio, decoded. */\n onFrame(frame: Buffer): void;\n /** What the program wrote, to send (the call's own, kept from before the media existed); a new one if none. */\n queue?: PlaybackQueue;\n now?: () => number;\n}\n\n/**\n * One call's audio: every 20 ms a frame from the queue (the program's audio, or silence) is encoded and sent; what\n * comes in is put back in order, decoded, and handed on in 20 ms frames as soon as it is. Nothing waits on a clock\n * but the sending side, so the audio adds at most a frame of delay each way inside the CLI.\n */\nexport class CallAudio {\n readonly queue: PlaybackQueue;\n private readonly writer: RtpWriter;\n private readonly reorder = new ReorderBuffer();\n private readonly frame: number;\n private readonly samples: number;\n private readonly silence: Buffer;\n private pending: Buffer = Buffer.alloc(0);\n private timer: ReturnType<typeof setTimeout> | null = null;\n private startedAt = 0;\n private ticks = 0;\n /** What was left in the queue after the last frame sent: the program's audio written ahead of the call. */\n private ahead = 0;\n /** After a stall that was not replayed: how much of its audio is still to drop, down to what queue, and until when. */\n private owed = 0;\n private kept = 0;\n private owedUntil = 0;\n private talking = false;\n /** The last silent frame's packet, how many silent frames in a row gave it, and that packet once it is kept (see `settle`). */\n private lastQuiet: Buffer | null = null;\n private same = 0;\n private quiet: Buffer | null = null;\n private stopped = false;\n /** Frames sent and received, for `call list`. */\n sent = 0;\n received = 0;\n /** When the last packet, RTP or RTCP, came from the contact (Date.now(); 0: none yet), for the call's watch. */\n heardAt = 0;\n\n constructor(private readonly options: CallAudioOptions) {\n const now = options.now ?? Date.now;\n this.queue = options.queue ?? new PlaybackQueue(options.rate, now);\n this.writer = new RtpWriter(options.ssrc, options.payloadType);\n this.frame = frameBytes(options.rate);\n this.samples = frameSamples(options.rate);\n this.silence = Buffer.alloc(this.frame);\n options.track.onPacket((packet) => this.receive(packet));\n }\n\n /** Starts sending, a frame every 20 ms from now on. */\n start(): void {\n if (this.timer || this.stopped) return;\n this.startedAt = (this.options.now ?? Date.now)();\n this.ticks = 0;\n this.schedule();\n }\n\n stop(): void {\n this.stopped = true;\n if (this.timer) clearTimeout(this.timer);\n this.timer = null;\n this.options.encoder.delete?.();\n this.options.decoder.delete?.();\n }\n\n /** Sends the frame that is due now. Exposed for tests, which drive the clock themselves. */\n tick(): void {\n if (this.stopped) return;\n const { frame, silent } = this.queue.next();\n // Silence the program writes itself (a muted microphone) is the same silence to the encoder.\n const still = silent || frame.equals(this.silence);\n let payload: Buffer;\n if (still && this.quiet) payload = this.quiet;\n else {\n try { payload = this.options.encoder.encode(frame, this.samples); } catch { return; }\n this.settle(still, payload);\n }\n // A talkspurt starts at the first sound after silence (RFC 3551 § 4.1).\n const marker = !silent && !this.talking;\n this.talking = !silent;\n this.options.track.send(this.writer.packet(payload, marker));\n this.sent++;\n this.ahead = this.queue.queuedMs;\n }\n\n /**\n * Keeps the packet silence encodes to once the encoder gave it SETTLED_FRAMES times in a row: it is sent again for\n * the silent frames that follow, without the encoder, until the program's audio is back.\n */\n private settle(still: boolean, payload: Buffer): void {\n if (!still) { this.lastQuiet = null; this.quiet = null; this.same = 0; return; }\n if (this.lastQuiet?.equals(payload)) { if (++this.same >= SETTLED_FRAMES) this.quiet = this.lastQuiet; return; }\n // A copy: the encoder gives a view of its own memory.\n this.lastQuiet = Buffer.from(payload);\n this.same = 1;\n }\n\n /** Handles one packet from the track. Exposed for tests. */\n receive(packet: Buffer): void {\n if (this.stopped) return;\n this.heardAt = (this.options.now ?? Date.now)();\n if (isRtcp(packet)) return;\n const rtp = parseRtp(packet);\n if (!rtp || rtp.payloadType !== this.options.payloadType) return;\n for (const item of this.reorder.push(rtp)) {\n let pcm: Buffer;\n if (\"lost\" in item) pcm = this.silence;\n else {\n try { pcm = this.options.decoder.decode(item.payload); } catch { pcm = this.silence; }\n }\n this.emit(pcm);\n }\n }\n\n /** Hands decoded audio on in 20 ms frames, whatever the contact's packet length (10, 20, 40 or 60 ms). */\n private emit(pcm: Buffer): void {\n let data = this.pending.length ? Buffer.concat([this.pending, pcm]) : pcm;\n while (data.length >= this.frame) {\n this.options.onFrame(Buffer.from(data.subarray(0, this.frame)));\n this.received++;\n data = data.subarray(this.frame);\n }\n this.pending = Buffer.from(data);\n }\n\n private schedule(): void {\n const now = this.options.now ?? Date.now;\n const due = this.startedAt + (this.ticks + 1) * FRAME_MS;\n this.timer = setTimeout(() => {\n if (this.stopped) return;\n if (now() - due < FRAME_MS) { this.ticks++; this.drop(); this.tick(); this.schedule(); return; }\n // Late (a busy event loop: a file going out, a journal fsync, a Pkarr publish): what the program wrote meanwhile\n // is still in its socket, read after the timers of this turn, so the frames wait for that.\n setImmediate(() => {\n if (this.stopped) return;\n this.catchUp(now() - due);\n this.schedule();\n });\n }, Math.max(0, due - now()));\n }\n\n /**\n * Meets the clock again after the loop was busy: every frame that is due goes out at once, so a loop that turns\n * slower than a frame for a while (every 40 ms under a large file) keeps up, and a program writing at real time\n * stays there. The contact's jitter buffer takes the short burst. A stall past MAX_CATCH_UP_MS is not replayed: the\n * clock starts again and what the program wrote during it is dropped, or it would be heard that much late for the\n * rest of the call.\n */\n private catchUp(late: number): void {\n const now = this.options.now ?? Date.now;\n if (late > MAX_CATCH_UP_MS) {\n this.owed = late;\n this.kept = this.ahead;\n this.owedUntil = now() + MAX_CATCH_UP_MS;\n this.startedAt = now();\n this.ticks = 0;\n this.drop();\n this.tick();\n return;\n }\n do { this.ticks++; this.drop(); this.tick(); } while (this.startedAt + (this.ticks + 1) * FRAME_MS <= now());\n }\n\n /**\n * Drops the audio of a stall that was not replayed, as it is read: a long one does not fit the program's socket, so\n * the rest of it comes over the next turns. What was queued before the stall (a clip written ahead) is kept.\n */\n private drop(): void {\n if (this.owed <= 0) return;\n if ((this.options.now ?? Date.now)() > this.owedUntil) { this.owed = 0; return; }\n this.owed -= this.queue.skip(Math.min(this.owed, this.queue.queuedMs - this.kept));\n }\n}\n","import { randomBytes } from \"node:crypto\";\nimport { lookup } from \"node:dns/promises\";\nimport type { IceServer, PeerConnection, Track } from \"node-datachannel\";\nimport { buildSdpFromSignal, callRtcConfig, extractParamsFromSdp, PAIRED_CALL_CANDIDATES, RTC_CONFIG, type CallIceServer, type CallSignal } from \"@ghostly/core\";\nimport { CallAudio, type OpusCodec } from \"./audio\";\nimport type { CallRate, PlaybackQueue } from \"./pcm\";\n\n/**\n * A call's media on Node (WISP 601, WISP 1100 § Calls): libdatachannel (node-datachannel, the same native module\n * the CLI's chats use) runs ICE, DTLS-SRTP and RTP; Opus is libopus built to WebAssembly (opusscript). Audio only:\n * a call's video section is answered closed (`vr: 0`), and nothing is sent or read on it.\n */\nexport interface CallStack {\n ndc: typeof import(\"node-datachannel\");\n opus(rate: CallRate): OpusCodec;\n}\n\nlet loading: Promise<CallStack | string> | null = null;\n\n/** The media stack, or why calls are not available here (the reason the call buttons of the contact's app show). */\nexport function loadCallStack(): Promise<CallStack | string> {\n return (loading ??= (async () => {\n if (process.env.GHOSTLY_WEBRTC === \"0\") return \"Calls are off on this headless Ghostly (GHOSTLY_WEBRTC=0)\";\n try {\n const ndc = (await import(\"node-datachannel\")) as unknown as CallStack[\"ndc\"] & { default?: CallStack[\"ndc\"] };\n const lib = (ndc.PeerConnection ? ndc : ndc.default!) as CallStack[\"ndc\"];\n const OpusScript = ((await import(\"opusscript\")) as unknown as { default: typeof import(\"opusscript\") }).default;\n const opus = (rate: CallRate): OpusCodec => {\n const codec = new OpusScript(rate, 1, OpusScript.Application.VOIP);\n // What browsers send for speech; the default of the build is far more.\n codec.setBitrate(32_000);\n return codec as unknown as OpusCodec;\n };\n // Checked once here: a broken wasm build would otherwise fail the first call.\n opus(48000).delete?.();\n return { ndc: lib, opus };\n } catch (error) {\n return `Calls are not available on this headless Ghostly (${error instanceof Error ? error.message : String(error)})`;\n }\n })());\n}\n\n/**\n * The ICE servers of a call, as libdatachannel takes them: the apps' STUN servers (RTC_CONFIG; libjuice uses the\n * first; none with `GHOSTLY_STUN=0`), then the profile's own (Settings `iceServers`: a TURN relay, typically), as the\n * apps' calls use them.\n */\nexport function callIceServers(extra: readonly CallIceServer[] = [], env = process.env): (string | IceServer)[] {\n const out: (string | IceServer)[] = env.GHOSTLY_STUN === \"0\" ? [] : (RTC_CONFIG.iceServers ?? []).flatMap((server) => [server.urls].flat()).slice(0, 2);\n for (const server of callRtcConfig(extra).iceServers?.slice(RTC_CONFIG.iceServers?.length ?? 0) ?? []) {\n for (const url of [server.urls].flat()) {\n const m = /^(stun|turns?):([^:?\\s]+)(?::(\\d+))?(?:\\?transport=(udp|tcp))?$/i.exec(url);\n if (!m) continue;\n const scheme = m[1].toLowerCase(), hostname = m[2], transport = m[4]?.toLowerCase();\n if (scheme === \"stun\") { out.push(`stun:${hostname}:${m[3] ?? 3478}`); continue; }\n out.push({\n hostname, port: Number(m[3] ?? (scheme === \"turns\" ? 5349 : 3478)),\n ...(server.username ? { username: server.username } : {}), ...(typeof server.credential === \"string\" ? { password: server.credential } : {}),\n relayType: scheme === \"turns\" ? \"TurnTls\" : transport === \"tcp\" ? \"TurnTcp\" : \"TurnUdp\",\n });\n }\n }\n return out;\n}\n\nexport type MediaState = \"connected\" | \"failed\" | \"closed\";\n\nexport interface MediaOptions {\n rate: CallRate;\n /** A 20 ms frame of the contact's audio. */\n onFrame(frame: Buffer): void;\n onState(state: MediaState): void;\n /** The call's queue of the program's audio. */\n queue?: PlaybackQueue;\n /** The profile's own ICE servers (Settings `iceServers`), after the apps' STUN servers. */\n iceServers?: readonly CallIceServer[];\n /** A line about the connection (candidates, ICE states, the pair it runs on), for the daemon's log. */\n log?(line: string): void;\n}\n\n/** The call's connection as `call list` reports it: the ICE state and, once connected, the pair it runs on. */\nexport interface IceView { state: string; pair: string | null }\n\n/** A candidate as `call list` and the log show it: address, port and type. */\nfunction describePair(pair: ReturnType<PeerConnection[\"getSelectedCandidatePair\"]>): string | null {\n if (!pair) return null;\n const one = (c: { address: string; port: number; type: string }) => `${c.address}:${c.port} ${c.type}`;\n return `${one(pair.local)} <-> ${one(pair.remote)}`;\n}\n\n/**\n * For tests (the e2e of a web app calling back a bot): the first this many answers the process applies are refused\n * as libdatachannel 0.24.5 refuses one in its race (see CallManager's `redial`): the connection closes, and it throws.\n */\nlet refuseAnswers = Number(process.env.GHOSTLY_CALL_REFUSE_ANSWERS) || 0;\n\n/** How long a description waits for its candidates: a server reflexive one and a little after it, or this long. */\nconst GATHER_MS = 5000;\nconst SETTLE_MS = 400;\n/**\n * With a candidate in (a host one comes in milliseconds), the server reflexive one is waited for this long at most,\n * not the whole of `GATHER_MS`: a STUN server that answers does in tens of milliseconds, and one that never does (UDP\n * to it blocked, as on many office, school and hotel networks) made every call ring 5 s late and connect 5 s after\n * the answer. The apps' calls and the chats' data link bound theirs the same way (`HOST_GATHER_MS`,\n * `OFFER_HOST_GATHER_MS`).\n */\nexport const HOST_GATHER_MS = 2000;\n\n/**\n * One call's peer connection. Its SDP never travels: the compact signal does (`local`), and each side rebuilds the\n * other's SDP from it (`buildSdpFromSignal`), so the payload types, SSRC and mids here are the ones that rebuilt SDP\n * uses: audio is mid 0, Opus is 111 unless the contact's offer says otherwise.\n */\nexport class CallMedia {\n readonly audio: CallAudio;\n private closed = false;\n private iceState = \"new\";\n private pair: string | null = null;\n private log: (line: string) => void = () => {};\n\n private constructor(private readonly pc: PeerConnection, private readonly track: Track, private readonly videoTrack: Track | null, readonly local: Partial<CallSignal>, audio: CallAudio) {\n this.audio = audio;\n }\n\n /** An offer: audio only (the contact's app answers one section, and its video lane stays closed). */\n static async offer(stack: CallStack, options: MediaOptions): Promise<CallMedia> {\n return CallMedia.create(stack, options, null);\n }\n\n /** The answer to a contact's offer, which is rebuilt into its SDP here. */\n static async answer(stack: CallStack, offer: CallSignal, options: MediaOptions): Promise<CallMedia> {\n return CallMedia.create(stack, options, offer);\n }\n\n private static async create(stack: CallStack, options: MediaOptions, offer: CallSignal | null): Promise<CallMedia> {\n const { ndc } = stack;\n const bind = process.env.GHOSTLY_CALL_BIND;\n const pc = new ndc.PeerConnection(\"ghostly-call\", { iceServers: callIceServers(options.iceServers), ...(bind ? { bindAddress: bind } : {}) });\n // Each line says how long after this connection was made it happened: the order of the connection's states and\n // the contact's answer is what explains a call that failed as it connected. The states are heard from the start.\n const born = Date.now();\n const sink = options.log ?? (() => {});\n const log = (line: string) => sink(`+${Date.now() - born}ms ${line}`);\n let media: CallMedia | null = null;\n pc.onSignalingStateChange((state) => { if (!media?.closed) log(`signaling ${state}`); });\n pc.onIceStateChange((state) => {\n if (media?.closed) return;\n let pair: string | null = null;\n if (state === \"connected\" || state === \"completed\") {\n try { pair = describePair(pc.getSelectedCandidatePair()); } catch { /* none yet */ }\n }\n if (media) { media.iceState = state; if (state === \"connected\" || state === \"completed\") media.pair = pair; }\n log(`ice ${state}${pair ? ` over ${pair}` : \"\"}`);\n });\n pc.onStateChange((state) => {\n if (media?.closed) return;\n log(`connection ${state}`);\n if (!media) return;\n if (state === \"connected\") options.onState(\"connected\");\n else if (state === \"failed\" || state === \"closed\") options.onState(state);\n });\n try {\n const payloadType = offer?.ap ?? 111;\n const ssrc = randomBytes(4).readUInt32BE(0) || 1;\n const audioSection = new ndc.Audio(\"0\", \"SendRecv\");\n audioSection.addOpusCodec(payloadType);\n audioSection.addSSRC(ssrc, \"ghostly\", \"stream\", \"audio0\");\n const track = pc.addTrack(audioSection);\n // The contact's offer always has a video section (the apps open one for a camera or a screen later): it is\n // answered closed, and the answer says so (`vr: 0` below), so the contact's app sends no picture on it. An app\n // that does not know `vr` sends one all the same: it is dropped as it comes (libdatachannel keeps what nobody reads).\n let videoTrack: Track | null = null;\n if (offer?.m?.includes(\"v\")) {\n const video = new ndc.Video(String(offer.m.indexOf(\"v\")), \"Inactive\");\n video.addVP8Codec(offer.vp ?? 96);\n videoTrack = pc.addTrack(video);\n videoTrack.onMessage(() => {});\n }\n const gathered = gathering(pc);\n if (offer) pc.setRemoteDescription(buildSdpFromSignal(offer), \"offer\");\n else pc.setLocalDescription(\"offer\");\n await gathered;\n const sdp = pc.localDescription()?.sdp;\n if (!sdp) throw new Error(\"No local description\");\n // Every path, as a signal on the chat session may carry (pickCallCandidates); bound to loopback (the tests,\n // GHOSTLY_CALL_BIND=127.0.0.1), that is the one candidate there is.\n const params = extractParamsFromSdp(sdp, { maxCandidates: PAIRED_CALL_CANDIDATES, loopback: !!bind });\n const local: Partial<CallSignal> = { ...params, ss: [ssrc] };\n // An answer lists the offer's sections, video too: the rebuilt SDP must have as many as the offer.\n if (offer?.m) local.m = [...offer.m];\n // No picture is taken here, and none is sent: the contact's app rebuilds the video section closed (WISP 601).\n if (videoTrack) local.vr = 0;\n const listeners: ((packet: Buffer) => void)[] = [];\n track.onMessage((packet) => { for (const listener of listeners) listener(packet); });\n const audio = new CallAudio({\n rate: options.rate, payloadType, ssrc,\n track: { send: (packet) => { if (track.isOpen()) track.sendMessageBinary(packet); }, onPacket: (listener) => listeners.push(listener) },\n encoder: stack.opus(options.rate), decoder: stack.opus(options.rate), onFrame: options.onFrame, queue: options.queue,\n });\n media = new CallMedia(pc, track, videoTrack, local, audio);\n media.log = log;\n // What each side offered to meet on: when a call does not connect, the log says between which addresses.\n log(`candidates here: ${(local.c ?? []).join(\" | \") || \"none\"}`);\n if (offer) log(`contact's candidates: ${(offer.c ?? []).join(\" | \") || \"none\"}`);\n try { media.iceState = pc.iceState(); } catch { /* keeps \"new\" */ }\n if (offer) mdnsNote(offer, log);\n track.onOpen(() => audio.start());\n if (track.isOpen()) audio.start();\n return media;\n } catch (error) {\n pc.close();\n throw error;\n }\n }\n\n /** The contact's answer to this side's offer. */\n applyAnswer(answer: CallSignal): void {\n this.log(`contact's candidates: ${(answer.c ?? []).join(\" | \") || \"none\"}`);\n mdnsNote(answer, this.log);\n if (refuseAnswers > 0) {\n refuseAnswers--;\n this.pc.close();\n throw new Error(\"libdatachannel error while adding remote description: Got a remote candidate without ICE transport (GHOSTLY_CALL_REFUSE_ANSWERS)\");\n }\n this.pc.setRemoteDescription(buildSdpFromSignal(answer), \"answer\");\n this.log(\"answer applied\");\n }\n\n get ice(): IceView {\n return { state: this.iceState, pair: this.pair };\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.audio.stop();\n try { this.track.close(); } catch { /* closed with the connection */ }\n try { this.videoTrack?.close(); } catch { /* same */ }\n try { this.pc.close(); } catch { /* already */ }\n }\n}\n\n/**\n * WebKit (Safari, the macOS app) hides its host addresses behind mDNS names, which other machines, and often this\n * one, cannot resolve: then the call can only connect when the contact's own checks reach this side. The log says so,\n * since that is what a call stuck on \"connecting\" needs to be understood.\n */\nfunction mdnsNote(signal: CallSignal, log: (line: string) => void): void {\n for (const candidate of signal.c ?? []) {\n const host = candidate.split(\" \")[4];\n if (!host?.endsWith(\".local\")) continue;\n void lookup(host, { all: true }).then(\n (found) => log(`${host} resolves here to ${found.map((a) => a.address).join(\", \")}`),\n () => log(`${host} does not resolve here: the call connects only if the contact's checks reach this side`),\n );\n }\n}\n\n/** Resolves once the local description has its candidates (see GATHER_MS and HOST_GATHER_MS). */\nexport function gathering(pc: Pick<PeerConnection, \"onGatheringStateChange\" | \"onLocalCandidate\">): Promise<void> {\n return new Promise((resolve) => {\n let settle: ReturnType<typeof setTimeout> | null = null;\n let any = false;\n const done = () => { clearTimeout(cap); clearTimeout(bounded); if (settle) clearTimeout(settle); resolve(); };\n const cap = setTimeout(done, GATHER_MS);\n const bounded = setTimeout(() => { if (any) done(); }, HOST_GATHER_MS);\n pc.onGatheringStateChange((state) => { if (state === \"complete\") done(); });\n pc.onLocalCandidate((candidate) => {\n any = true;\n if (!settle && / typ srflx/.test(candidate)) settle = setTimeout(done, SETTLE_MS);\n });\n });\n}\n","import { randomBytes } from \"node:crypto\";\nimport { existsSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { answersOffer, callSignalHeardAt, parseCallSignal, signalHasVideo, CALL_SIGNAL_MAX_AGE_MS, type CallSignal } from \"@ghostly/core\";\nimport type { EngineState, LinkView } from \"@ghostly/browser/shared/types\";\nimport { findChat } from \"../apiKit\";\nimport { CliError } from \"../errors\";\nimport { AudioSocket, audioSocketPath, clearAudioSockets } from \"./audioSocket\";\nimport { CallMedia, loadCallStack, type CallStack, type MediaOptions } from \"./media\";\nimport { DEFAULT_RATE, FRAME_MS, isCallRate, PlaybackQueue, type CallRate } from \"./pcm\";\n\n/**\n * Voice calls on the headless Ghostly (WISP 1100 § Calls): the calls/1 signals of the chat session (WISP 601), the\n * media on a WebRTC connection of the CLI's own, and the audio handed to an outside program over a Unix socket per\n * call. The rules are the apps' (packages/react/src/useWebRTC.ts): a call per chat; an offer rings while this side\n * is idle, or when it wins a glare with this side's own offer; an answer counts only after this side's offer; a hang-up ends whatever is on; a signal older than the\n * last one handled is ignored; after a hang-up the signal is cleared 5 s later.\n */\n\nexport type CallState = \"ringing\" | \"connecting\" | \"connected\";\nexport type EndReason = \"hangup\" | \"remote-hangup\" | \"missed\" | \"rejected\" | \"unanswered\" | \"crossed\" | \"failed\" | \"stopped\";\n\n/** What the engine gives the calls: its state, and a way to send this side's signal. */\nexport interface CallEngine {\n getState(): EngineState;\n setCallSignal(params: { linkId: string; signal: string | null }): Promise<void> | void;\n}\n\nexport interface CallHost {\n engine: CallEngine;\n emit(type: string, id: string, fields: Record<string, unknown>): void;\n profileDir: string;\n /** Whether this host answers and places calls: a daemon does; a one-shot command leaves, so it only reports. */\n answers?: boolean;\n /** The media stack (tests give their own). */\n stack?: () => Promise<CallStack | string>;\n now?: () => number;\n /**\n * How many times an outgoing call offers again (MAX_REDIALS): tests that refuse some answers themselves raise it by\n * theirs, so the real race's own refusals, which can come on top, still find the app's budget whole.\n */\n maxRedials?: number;\n /** STALL_MS and STALL_END_MS (tests shorten them). */\n stall?: { afterMs: number; endMs: number };\n}\n\n/** The audio contract of one call, as commands and events report it. */\nexport interface AudioInfo { socket: string; rate: CallRate; channels: 1; format: \"s16le\"; frameMs: typeof FRAME_MS }\n\n/** An outgoing call rings this long, then ends as `unanswered`. */\nexport const RING_MS = 60_000;\n/** An answered or accepted call has this long to connect its media, then ends as `failed`. */\nexport const CONNECT_MS = 30_000;\n/** How long a call whose media closed or failed waits for the contact's hang-up signal to say why. */\nexport const MEDIA_GRACE_MS = 3_000;\n/** After a hang-up, the signal is cleared this much later (the apps' own delay). */\nexport const CLEAR_MS = 5_000;\n/** How many times an outgoing call offers again after an answer was refused (`redial`), and the wait before each. */\nexport const MAX_REDIALS = 4;\nexport const REDIAL_BACKOFF_MS = [150, 300, 600, 600] as const;\n/** A connected call that hears no packet from the contact this long says so (`call.stalled`). */\nexport const STALL_MS = 5_000;\n/** A connected call that hears no packet from the contact this long ends as `failed`. */\nexport const STALL_END_MS = 15_000;\n/** A connection that fails this soon after its answer went in lost the race `redial` describes, and starts over. */\nexport const RACE_FAIL_MS = 2_000;\n\ninterface Call {\n id: string;\n chat: string;\n direction: \"in\" | \"out\";\n state: CallState;\n rate: CallRate;\n video: boolean;\n offer: CallSignal | null;\n /** This side's offer's timestamp: only a later answer counts. */\n offerTs: number;\n /** This side's offer waits for its answer. */\n offering: boolean;\n /** How many times this side offered again on a new connection after an answer was refused (`redial`). */\n redials: number;\n /** Counts the connections made for the call: one that comes back after a newer one was asked for is closed. */\n attempt: number;\n /** The attempt whose connection has the contact's answer (0: none yet), and when it went in (Date.now()). */\n answered: number;\n answeredAt: number;\n media: CallMedia | null;\n socket: AudioSocket | null;\n /** What the program wrote and the call has not sent yet: kept from the moment the socket opens. */\n queue: PlaybackQueue | null;\n startedAt: number;\n connectedAt: number | null;\n timer: ReturnType<typeof setTimeout> | null;\n /** The connected call's watch on the contact's packets (`watch`). */\n watcher: ReturnType<typeof setInterval> | null;\n /** Since when nothing came from the contact, while the call says it stalled (null: it does not); how many times it did. */\n stalledFrom: number | null;\n stalls: number;\n ended: boolean;\n /** Why it ended, once it has. */\n reason?: EndReason;\n}\n\n/** Auto-answer, kept in the profile's `calls.json`: every chat, or the listed ones, at a rate. */\nexport interface AutoAnswer { on: boolean; from: string[]; rate: CallRate }\n\nexport class CallManager {\n private readonly calls = new Map<string, Call>();\n /** Per chat: the timestamp of the last signal handled. */\n private readonly lastSignal = new Map<string, number>();\n /** Per chat: the timer that clears the hang-up signal. */\n private readonly clearing = new Map<string, ReturnType<typeof setTimeout>>();\n private auto: AutoAnswer;\n private readonly configPath: string;\n private stopping = false;\n\n constructor(private readonly host: CallHost) {\n this.configPath = join(host.profileDir, \"calls.json\");\n this.auto = readAuto(this.configPath);\n // A daemon starts with no call: a socket left in its folder is one a daemon that crashed during a call never closed.\n if (host.answers !== false) clearAudioSockets(host.profileDir);\n }\n\n private get now(): number { return (this.host.now ?? Date.now)(); }\n\n // ---------- signals from the contact ----------\n\n /** A signal the contact's app sent in a chat (the engine's `call-signal`). */\n onSignal(chat: string, json: string): void {\n // A stopping daemon takes no call, and its own have been hung up (`stopAll`): nothing here is heard any more.\n if (this.stopping) return;\n const signal = parseCallSignal(json, this.now);\n if (!signal) return;\n if (signal.ts <= (this.lastSignal.get(chat) ?? 0)) return;\n const call = this.byChat(chat);\n // Reconnecting (WISP 601): this side never says it restarts ICE (`x`), so an app sends it no restart offer (`r`),\n // and one that comes all the same is dropped here, on a call or not. libdatachannel (0.24.5) cannot restart ICE\n // on a connection that has started: it refuses a remote offer with new ICE credentials (\"Invalid ICE settings\n // from remote SDP\") and cannot make one. A call whose path is lost ends as before (`MEDIA_GRACE_MS`).\n if (signal.t === \"r\" || (signal.t === \"a\" && signal.re !== undefined)) return;\n // A connected call only hears a hang-up (and picture changes, which are no business of an audio-only side).\n if (call?.state === \"connected\" && signal.t !== \"h\" && signal.t !== \"v\") return;\n if (signal.t === \"o\" && !call) {\n this.lastSignal.set(chat, signal.ts);\n this.incoming(chat, signal);\n } else if (signal.t === \"a\" && call?.direction === \"out\" && call.offering && answersOffer(signal, call.offerTs)) {\n this.lastSignal.set(chat, signal.ts);\n this.accepted(call, signal);\n } else if (signal.t === \"o\" && call?.direction === \"out\" && call.state === \"ringing\") {\n // Both called at once (WISP 601, \"Both call at once\"): the earlier offer wins on both sides, at the same\n // millisecond the lower DTLS fingerprint; ours still being made counts as later. The loser rings.\n this.lastSignal.set(chat, signal.ts);\n const mine = call.offering ? call.offerTs : 0;\n const theirsFirst = !mine || signal.ts < mine || (signal.ts === mine && (signal.f ?? \"\") < (call.media?.local.f ?? \"\"));\n if (theirsFirst) void this.yieldTo(call, signal);\n } else if (signal.t === \"o\" && call?.direction === \"in\" && call.state === \"connecting\" && call.media && signal.ts > (call.offer?.ts ?? 0)) {\n // The contact's side offered again on a new connection (see `redial`): the answered call starts over on one.\n this.lastSignal.set(chat, signal.ts);\n void this.reanswer(call, signal);\n } else if (signal.t === \"v\") {\n this.lastSignal.set(chat, signal.ts);\n } else if (signal.t === \"h\") {\n this.lastSignal.set(chat, signal.ts);\n if (call) {\n // A hang-up while this side starts over (`redial`) is the contact's app ending a connection that failed. Once\n // a connection came up, the call was on: a start-over before it does not make the hang-up a failure.\n // A hang-up that says the contact's side could not connect (`r: \"u\"`, WISP 601): the call failed, not declined.\n const unreachable = signal.r === \"u\" && call.state !== \"connected\";\n const failed = unreachable || (call.state !== \"connected\" && (call.offering || (call.direction === \"out\" && call.redials > 0)));\n const reason: EndReason = call.state === \"ringing\" && call.direction === \"in\" ? \"missed\"\n : call.state === \"ringing\" && !unreachable ? \"rejected\" : failed ? \"failed\" : \"remote-hangup\";\n void this.end(call, reason, false);\n }\n }\n }\n\n /**\n * The contact's offer won a glare: our call ends as `crossed`, sending nothing (a hang-up would end the contact's\n * call, and clearing our signal could drop an answer we are about to give), and the contact's call rings here.\n */\n private async yieldTo(call: Call, offer: CallSignal): Promise<void> {\n await this.end(call, \"crossed\", false);\n // Something newer (the contact's hang-up) came while ours ended, or another call started: nothing rings.\n if (this.lastSignal.get(call.chat) !== offer.ts || this.byChat(call.chat) || this.stopping) return;\n this.incoming(call.chat, offer);\n }\n\n private incoming(chat: string, offer: CallSignal): void {\n const link = this.link(chat);\n const call = this.add(chat, \"in\", this.auto.rate);\n call.offer = offer;\n call.video = signalHasVideo(offer);\n // Unanswered, an offer goes stale: the contact's app has given up or will, and answering it would be refused.\n this.arm(call, Math.max(1000, callSignalHeardAt(offer) + CALL_SIGNAL_MAX_AGE_MS - this.now), \"missed\", false);\n this.host.emit(\"call.incoming\", `call.incoming:${call.id}`, { call: call.id, chat, name: link ? nameOf(link) : null, video: call.video, auto: this.autoFor(chat) });\n if (this.host.answers !== false && this.autoFor(chat)) {\n void this.answer(call.id, {}).catch((error) => process.stderr.write(`ghostly: auto-answer failed: ${error instanceof Error ? error.message : String(error)}\\n`));\n }\n }\n\n private accepted(call: Call, answer: CallSignal): void {\n call.offering = false;\n call.state = \"connecting\";\n const attempt = (call.answered = call.attempt);\n call.answeredAt = Date.now();\n try {\n call.media!.applyAnswer(answer);\n } catch (error) {\n // The connection may have failed first, while the answer was added: then it already started over.\n if (attempt === call.attempt) this.refused(call, `the answer was refused (${error instanceof Error ? error.message : String(error)})`);\n return;\n }\n if (attempt === call.attempt) this.arm(call, CONNECT_MS, \"failed\", true);\n }\n\n /**\n * The contact's answer on this connection was refused, or the connection failed right after it (the same race,\n * whose handshake can also fail once the answer is in): offering again (`redial`) while there are tries left.\n */\n private refused(call: Call, what: string): void {\n if (call.redials < this.maxRedials) {\n process.stderr.write(`ghostly: call ${call.id}: ${what}: offering again on a new connection\\n`);\n void this.redial(call);\n return;\n }\n process.stderr.write(`ghostly: call ${call.id}: ${what}\\n`);\n void this.end(call, \"failed\", true);\n }\n\n private get maxRedials(): number { return this.host.maxRedials ?? MAX_REDIALS; }\n\n /**\n * A new offer on a new connection, up to MAX_REDIALS times, each after a short wait. libdatachannel (0.24.5) can\n * refuse a good answer: when the contact's checks and its DTLS hello came before the answer (they do, the answer\n * crosses the chat session), ICE connects and the handshake ends inside setRemoteDescription, before the answer's\n * fingerprint is recorded; the fingerprint check fails, the connection closes its transports, and adding the\n * answer's candidates throws \"Got a remote candidate without ICE transport\". That connection is done for, and the\n * contact's side saw the handshake fail too, so both start over: the contact's side answers the newer offer\n * (`reanswer`). Each new connection runs the same race (CI runners lose about one in six, Linux arm64 twice in a\n * row), hence several. The race can also let the answer in and fail the handshake a moment later: a connection\n * that fails after its answer, before it ever connected, starts over the same way. The contact's side ends a call whose connection failed after MEDIA_GRACE_MS: every wait stays\n * well under it. Fixed upstream in libdatachannel 0235225a, which no release has yet.\n */\n private async redial(call: Call): Promise<void> {\n const wait = REDIAL_BACKOFF_MS[Math.min(call.redials, REDIAL_BACKOFF_MS.length - 1)];\n call.redials++;\n // The refused connection is dropped now: its closing must not arm its own timer over the one below while we wait.\n call.attempt++;\n call.media?.close();\n call.media = null;\n this.arm(call, CONNECT_MS, \"failed\", true);\n await new Promise((resolve) => setTimeout(resolve, wait));\n if (call.ended) return;\n try {\n const stack = await this.stack();\n if (!(await this.connect(call, (media) => CallMedia.offer(stack, media)))) return;\n call.offerTs = this.now;\n call.offering = true;\n await this.send(call.chat, { t: \"o\", ts: call.offerTs, ...call.media!.local, v: 0 });\n } catch (error) {\n process.stderr.write(`ghostly: call ${call.id}: offering again failed (${error instanceof Error ? error.message : String(error)})\\n`);\n void this.end(call, \"failed\", true);\n }\n }\n\n /** The contact's newer offer (its `redial`): answered on a new connection, on the call's socket. */\n private async reanswer(call: Call, offer: CallSignal): Promise<void> {\n process.stderr.write(`ghostly: call ${call.id}: the contact offered again: answering on a new connection\\n`);\n call.offer = offer;\n call.video = signalHasVideo(offer);\n this.arm(call, CONNECT_MS, \"failed\", true);\n try {\n const stack = await this.stack();\n if (!(await this.connect(call, (media) => CallMedia.answer(stack, offer, media)))) return;\n await this.send(call.chat, { t: \"a\", ts: this.now, o: offer.ts, ...call.media!.local, v: 0 });\n } catch (error) {\n process.stderr.write(`ghostly: call ${call.id}: answering again failed (${error instanceof Error ? error.message : String(error)})\\n`);\n void this.end(call, \"failed\", true);\n }\n }\n\n // ---------- commands ----------\n\n async start(chat: string, options: { rate?: unknown }): Promise<Record<string, unknown>> {\n const link = this.callable(chat);\n if (this.byChat(link.id)) throw new CliError(\"busy\", \"A call is already on in this chat\");\n const rate = rateOf(options.rate, this.auto.rate);\n const stack = await this.stack();\n // The contact's call may have come in while the stack loaded.\n if (this.byChat(link.id)) throw new CliError(\"busy\", \"A call is already on in this chat\");\n const call = this.add(link.id, \"out\", rate);\n try {\n await this.attach(call, (media) => CallMedia.offer(stack, media));\n if (call.reason === \"crossed\") throw new CliError(\"busy\", \"The contact called at the same time: their call rings here\");\n if (call.ended) throw new CliError(\"unavailable\", \"The call ended before it was placed\");\n call.offerTs = this.now;\n call.offering = true;\n await this.send(link.id, { t: \"o\", ts: call.offerTs, ...call.media!.local, v: 0 });\n } catch (error) {\n await this.end(call, \"failed\", false, false);\n throw error;\n }\n this.arm(call, RING_MS, \"unanswered\", true);\n this.host.emit(\"call.outgoing\", `call.outgoing:${call.id}`, { call: call.id, chat: link.id, name: nameOf(link), audio: this.audioInfo(call) });\n return this.view(call);\n }\n\n async answer(ref: string | undefined, options: { rate?: unknown }): Promise<Record<string, unknown>> {\n const call = this.find(ref, (c) => c.direction === \"in\" && c.state === \"ringing\");\n if (call.direction !== \"in\" || call.state !== \"ringing\") throw new CliError(\"refused\", \"That call is not ringing here\");\n this.callable(call.chat);\n call.rate = rateOf(options.rate, call.rate);\n const stack = await this.stack();\n // The call may have ended (the contact hung up, the offer went stale) or been answered by another command while\n // the stack loaded: an ended call opens nothing, and a call is answered once.\n if (call.ended) throw new CliError(\"unavailable\", \"The call ended before it was answered\");\n if (call.state !== \"ringing\") throw new CliError(\"refused\", \"That call is not ringing here\");\n // Answered: no longer missed if it goes stale; the media has its own time to connect.\n call.state = \"connecting\";\n this.disarm(call);\n try {\n await this.attach(call, (media) => CallMedia.answer(stack, call.offer!, media));\n if (call.ended) throw new CliError(\"unavailable\", \"The call ended before it was answered\");\n await this.send(call.chat, { t: \"a\", ts: this.now, o: call.offer!.ts, ...call.media!.local, v: 0 });\n } catch (error) {\n await this.end(call, \"failed\", true, false);\n throw error;\n }\n this.arm(call, CONNECT_MS, \"failed\", true);\n return this.view(call);\n }\n\n async hangup(ref: string | undefined): Promise<Record<string, unknown>> {\n const call = this.find(ref);\n const view = this.view(call);\n await this.end(call, \"hangup\", true);\n return { ...view, state: \"ended\", reason: \"hangup\" };\n }\n\n /** One call, by its id or its chat (or the only one). */\n get(ref: string | undefined): Record<string, unknown> {\n return this.view(this.find(ref));\n }\n\n list(): Record<string, unknown>[] {\n return [...this.calls.values()].map((call) => this.view(call));\n }\n\n /** Drops the audio the program queued and the call has not sent yet (barge-in). */\n flush(ref: string | undefined): Record<string, unknown> {\n const call = this.find(ref);\n const flushedMs = call.queue?.flush() ?? 0;\n return { call: call.id, chat: call.chat, flushedMs };\n }\n\n getAuto(): AutoAnswer { return { ...this.auto, from: [...this.auto.from] }; }\n\n setAuto(next: { on: boolean; from?: string[]; rate?: unknown }): AutoAnswer {\n const from = (next.from ?? []).map((ref) => this.chatId(ref));\n this.auto = { on: next.on, from: next.on ? from : [], rate: rateOf(next.rate, next.on ? this.auto.rate : DEFAULT_RATE) };\n const tmp = `${this.configPath}.tmp`;\n writeFileSync(tmp, JSON.stringify({ autoAnswer: this.auto }, null, 2) + \"\\n\", { mode: 0o600 });\n renameSync(tmp, this.configPath);\n return this.getAuto();\n }\n\n /** Before the engine stops: every call is hung up (the contact's app is told), its program gets EOF. */\n async stopAll(): Promise<void> {\n this.stopping = true;\n await Promise.all([...this.calls.values()].map((call) => this.end(call, \"stopped\", true)));\n for (const timer of this.clearing.values()) clearTimeout(timer);\n this.clearing.clear();\n }\n\n // ---------- inside ----------\n\n private add(chat: string, direction: \"in\" | \"out\", rate: CallRate): Call {\n if (this.stopping) throw new CliError(\"unavailable\", \"The daemon is stopping\");\n // A new call in this chat: the last one's hang-up must not be cleared over its signal.\n const clearing = this.clearing.get(chat);\n if (clearing) { clearTimeout(clearing); this.clearing.delete(chat); }\n const call: Call = {\n id: randomBytes(6).toString(\"hex\"), chat, direction, state: \"ringing\", rate, video: false, offer: null, offerTs: 0,\n offering: false, redials: 0, attempt: 0, answered: 0, answeredAt: 0, media: null, socket: null, queue: null, startedAt: this.now, connectedAt: null, timer: null,\n watcher: null, stalledFrom: null, stalls: 0, ended: false,\n };\n this.calls.set(call.id, call);\n return call;\n }\n\n /**\n * The call's socket (open from now on: a program may connect before the media is up) and its media. A call that\n * ended while its socket opened closes it here: `end` found none to close, and nothing lists the call any more.\n */\n private async attach(call: Call, create: (options: MediaOptions) => Promise<CallMedia>): Promise<void> {\n const queue = (call.queue = new PlaybackQueue(call.rate));\n const socket = await AudioSocket.open(audioSocketPath(this.host.profileDir, call.id), call.rate, {\n onAudio: (chunk) => queue.push(chunk),\n });\n if (call.ended) { await socket.close(); return; }\n call.socket = socket;\n await this.connect(call, create);\n }\n\n /**\n * A connection for the call, in place of the one it had (closed first); the socket and its queue stay. False when\n * the call ended, or asked for a newer connection, while this one was made: then it is closed.\n */\n private async connect(call: Call, create: (options: MediaOptions) => Promise<CallMedia>): Promise<boolean> {\n // No connection is made for a call that is over (it ended while the stack loaded or its socket opened).\n if (call.ended) return false;\n const attempt = ++call.attempt;\n call.media?.close();\n call.media = null;\n let failed = false;\n const media = await create({\n rate: call.rate,\n queue: call.queue ?? undefined,\n log: (line) => process.stderr.write(`ghostly: call ${call.id}: ${line}\\n`),\n iceServers: this.host.engine.getState().settings?.iceServers ?? [],\n onFrame: (frame) => call.socket?.write(frame),\n onState: (state) => {\n if (call.ended || attempt !== call.attempt) return;\n if (state === \"connected\") { this.connected(call); return; }\n // A connection fails, then closes: it still failed.\n if (state === \"closed\" && failed) return;\n failed ||= state === \"failed\";\n // The answer went in and the connection failed at once, before it ever connected: the race's other ending (a\n // contact out of reach fails later, and ends the call as before).\n if (state === \"failed\" && call.direction === \"out\" && call.answered === attempt && call.state !== \"connected\"\n && Date.now() - call.answeredAt < RACE_FAIL_MS) {\n this.refused(call, \"the connection failed as the answer went in\");\n return;\n }\n // The contact's app closes its connection as it hangs up, and that is often here before its hang-up signal\n // (which crosses the chat session): the signal gets a moment to say so. Without one, a connection the\n // contact closed was still a hang-up; one that failed ends the call as it does in the apps, with nothing to say.\n this.arm(call, MEDIA_GRACE_MS, state === \"closed\" ? \"remote-hangup\" : \"failed\", false);\n },\n });\n if (call.ended || attempt !== call.attempt) {\n media.close();\n return false;\n }\n call.media = media;\n return true;\n }\n\n private connected(call: Call): void {\n if (call.state === \"connected\") return;\n call.state = \"connected\";\n call.connectedAt = this.now;\n this.disarm(call);\n this.host.emit(\"call.connected\", `call.connected:${call.id}`, { call: call.id, chat: call.chat, direction: call.direction, audio: this.audioInfo(call) });\n this.watch(call);\n }\n\n /**\n * libjuice says nothing of a contact whose side went away (a crash, a closed lid, a lost network) until its consent\n * checks fail, about 30 s later: a connected call watches the contact's packets instead. An app sends them all along\n * (silence while muted, and RTCP), and so does the CLI. Nothing for STALL_MS: `call.stalled`; packets again:\n * `call.resumed`; nothing for STALL_END_MS: the call ends as `failed`, and the contact's app is told.\n */\n private watch(call: Call): void {\n const { afterMs, endMs } = this.host.stall ?? { afterMs: STALL_MS, endMs: STALL_END_MS };\n const since = Date.now();\n call.watcher = setInterval(() => {\n if (call.ended) return;\n const heardAt = Math.max(since, call.media?.audio.heardAt ?? 0);\n const silentMs = Date.now() - heardAt;\n const fields = { call: call.id, chat: call.chat, direction: call.direction };\n if (silentMs >= endMs) {\n process.stderr.write(`ghostly: call ${call.id}: nothing heard from the contact for ${Math.round(silentMs / 1000)} s\\n`);\n void this.end(call, \"failed\", true);\n } else if (silentMs >= afterMs && call.stalledFrom === null) {\n call.stalledFrom = heardAt;\n call.stalls++;\n process.stderr.write(`ghostly: call ${call.id}: stalled, nothing heard from the contact for ${silentMs} ms\\n`);\n this.host.emit(\"call.stalled\", `call.stalled:${call.id}:${call.stalls}`, { ...fields, silentMs });\n } else if (silentMs < afterMs && call.stalledFrom !== null) {\n const gapMs = heardAt - call.stalledFrom;\n call.stalledFrom = null;\n process.stderr.write(`ghostly: call ${call.id}: resumed after ${gapMs} ms\\n`);\n this.host.emit(\"call.resumed\", `call.resumed:${call.id}:${call.stalls}`, { ...fields, silentMs: gapMs });\n }\n }, Math.min(1000, afterMs / 5));\n call.watcher.unref?.();\n }\n\n private async end(call: Call, reason: EndReason, tell: boolean, report = true): Promise<void> {\n if (call.ended) return;\n call.ended = true;\n call.reason = reason;\n this.disarm(call);\n if (call.watcher) clearInterval(call.watcher);\n this.calls.delete(call.id);\n if (report) process.stderr.write(`ghostly: call ${call.id}: ended (${reason})${call.media ? `, ice ${call.media.ice.state}` : \"\"}\\n`);\n call.media?.close();\n await call.socket?.close();\n if (tell) {\n await this.send(call.chat, { t: \"h\", ts: this.now }).catch(() => {});\n if (!this.stopping) {\n const timer = setTimeout(() => {\n this.clearing.delete(call.chat);\n void this.sendRaw(call.chat, null).catch(() => {});\n }, CLEAR_MS);\n timer.unref?.();\n this.clearing.set(call.chat, timer);\n }\n } else if (reason !== \"crossed\") {\n // Nothing left to say: a signal still waiting for the next session is dropped.\n await this.sendRaw(call.chat, null).catch(() => {});\n }\n if (report) {\n this.host.emit(\"call.ended\", `call.ended:${call.id}`, {\n call: call.id, chat: call.chat, direction: call.direction, reason,\n ...(call.connectedAt !== null ? { duration: this.now - call.connectedAt } : {}),\n });\n }\n }\n\n private arm(call: Call, ms: number, reason: EndReason, tell: boolean): void {\n this.disarm(call);\n call.timer = setTimeout(() => void this.end(call, reason, tell), ms);\n call.timer.unref?.();\n }\n\n private disarm(call: Call): void {\n if (call.timer) clearTimeout(call.timer);\n call.timer = null;\n }\n\n private send(chat: string, signal: CallSignal): Promise<void> {\n return this.sendRaw(chat, JSON.stringify(signal));\n }\n\n private async sendRaw(chat: string, signal: string | null): Promise<void> {\n try {\n await this.host.engine.setCallSignal({ linkId: chat, signal });\n } catch (error) {\n throw new CliError(\"unavailable\", error instanceof Error ? error.message : String(error));\n }\n }\n\n private async stack(): Promise<CallStack> {\n if (this.host.answers === false) throw new CliError(\"unavailable\", \"Calls need the daemon (a call outlives this command): start it with `ghostly daemon --detach`\");\n const stack = await (this.host.stack ?? loadCallStack)();\n if (typeof stack === \"string\") throw new CliError(\"unavailable\", stack);\n return stack;\n }\n\n private audioInfo(call: Call): AudioInfo | null {\n return call.socket ? { socket: call.socket.path, rate: call.rate, channels: 1, format: \"s16le\", frameMs: FRAME_MS } : null;\n }\n\n private view(call: Call): Record<string, unknown> {\n const audio = call.media?.audio;\n return {\n call: call.id, chat: call.chat, direction: call.direction, state: call.state, stalled: call.stalledFrom !== null, video: call.video,\n startedAt: call.startedAt, connectedAt: call.connectedAt,\n audio: this.audioInfo(call),\n ...(call.socket ? { stats: { framesIn: audio?.received ?? 0, framesOut: audio?.sent ?? 0, queuedMs: call.queue?.queuedMs ?? 0, programConnected: call.socket.connected, ice: call.media?.ice ?? null } } : {}),\n };\n }\n\n private byChat(chat: string): Call | undefined {\n for (const call of this.calls.values()) if (call.chat === chat) return call;\n return undefined;\n }\n\n /** A call by its id, or by its chat; with no ref, the only call (or the only one `prefer` picks). */\n private find(ref: string | undefined, prefer?: (call: Call) => boolean): Call {\n if (ref) {\n const byId = this.calls.get(ref);\n if (byId) return byId;\n const call = this.byChat(this.chatId(ref));\n if (!call) throw new CliError(\"not_found\", `No call in chat ${ref}`);\n return call;\n }\n const all = [...this.calls.values()];\n const picked = prefer && all.filter(prefer).length === 1 ? all.filter(prefer) : all;\n if (picked.length === 1) return picked[0];\n if (!picked.length) throw new CliError(\"not_found\", \"No call is on\");\n throw new CliError(\"bad_request\", \"More than one call is on: name one (its id or its chat)\", { calls: picked.map((c) => c.id) });\n }\n\n private link(chat: string): LinkView | undefined {\n return this.host.engine.getState().links.find((l) => l.id === chat);\n }\n\n /** A chat by id, contact key, prefix or name (the API's rules, `findChat`). */\n private chatId(ref: string): string {\n return findChat(this.host.engine.getState().links, ref).id;\n }\n\n /** The chat, if a call can go in it now; else why not (the engine's reason). */\n private callable(ref: string): LinkView {\n const link = this.link(this.chatId(ref))!;\n if (!link.profile) throw new CliError(\"unavailable\", \"Calls are in chats made since 0.4 (the chat session): this is a compatibility chat\");\n if (link.callsUnavailable) throw new CliError(\"unavailable\", link.callsUnavailable);\n return link;\n }\n\n private autoFor(chat: string): boolean {\n return this.auto.on && (this.auto.from.length === 0 || this.auto.from.includes(chat));\n }\n}\n\nfunction nameOf(link: LinkView): string | null {\n return link.label?.trim() || link.peerNick?.trim() || null;\n}\n\nfunction rateOf(value: unknown, fallback: CallRate): CallRate {\n if (value === undefined || value === null) return fallback;\n if (!isCallRate(value)) throw new CliError(\"bad_request\", \"rate must be 8000, 12000, 16000, 24000 or 48000\");\n return value;\n}\n\nfunction readAuto(path: string): AutoAnswer {\n const off: AutoAnswer = { on: false, from: [], rate: DEFAULT_RATE };\n if (!existsSync(path)) return off;\n try {\n const saved = (JSON.parse(readFileSync(path, \"utf8\")) as { autoAnswer?: Partial<AutoAnswer> }).autoAnswer;\n if (!saved) return off;\n return {\n on: saved.on === true,\n from: Array.isArray(saved.from) ? saved.from.filter((c): c is string => typeof c === \"string\") : [],\n rate: isCallRate(saved.rate) ? saved.rate : DEFAULT_RATE,\n };\n } catch {\n return off;\n }\n}\n","import { appendFileSync, existsSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport type { EngineClientSink } from \"@ghostly/browser/engine/server\";\nimport type { EngineEvent, RpcResponse } from \"@ghostly/browser/shared/rpc\";\nimport type { EngineState, GroupView, LinkView, PinView, StoredMessage } from \"@ghostly/browser/shared/types\";\nimport { chatJson, chatMessageJson, groupJson, groupMessageJson } from \"./views\";\nimport { paymentJson } from \"./wallets\";\n\n/**\n * The event stream (WISP 1100 § Event stream): what happened, derived from the engine's own events, one JSON object\n * each, with a `seq` that grows across restarts and an `id` stable for the fact it reports.\n */\nexport interface GhostlyEvent { seq: number; id: string; type: string; at: number; [field: string]: unknown }\n\n/** Events the journal keeps for `listen --since`. */\nexport const JOURNAL_KEEP = 10_000;\nconst SEEN_DB = \"ghostly-cli\";\nconst SEEN = \"seen\";\n\n/** How long a `file.*` event waits for the message that carries its file. */\nexport const FILE_MESSAGE_WAIT_MS = 2_000;\n\n/** The notice an app sends when a chat first goes live (apps/ui/src/hooks/useChat.ts): shown as a line, not a message. */\nexport const JOIN_NOTICE = /^👋 (?:(.+) )?joined$/;\n\ntype Listener = (event: GhostlyEvent) => void;\ntype Seen = Map<string, Map<string, string>>;\n\ninterface ChatShape { stage: string | null; live: boolean; transport: string | null; name: string | null; typing: boolean; typingKind: string; typingStatus: string | null; pin: PinView | null }\ninterface GroupShape { status: string | null; members: string[]; typing: Record<string, { kind: string; status?: string }>; pin: PinView | null }\n\n/**\n * Turns the engine's events into the stream, keeps the journal, and remembers which messages it already reported\n * (a store of its own in the profile's IndexedDB): after a restart, only what is new is reported, and a first\n * start reports nothing of the history that was already there.\n */\nexport class EventHub {\n private seq = 0;\n private journalLines = 0;\n private readonly listeners = new Set<Listener>();\n private readonly stateListeners = new Set<(state: EngineState) => void>();\n private readonly callListeners = new Set<(chat: string, signal: string) => void>();\n private seen: Seen = new Map();\n private firstRun = false;\n private chats = new Map<string, ChatShape>();\n private groups = new Map<string, GroupShape>();\n private payments = new Map<string, string>();\n private transfers = new Map<string, string>();\n private received = new Map<string, string>();\n /** File id to the message that carries it, for `file.*` events. */\n private fileMessages = new Map<string, string>();\n /** `file.*` events whose message was not seen yet: they wait for it a moment (the engine stores it right after). */\n private pendingFiles = new Map<string, { timer: ReturnType<typeof setTimeout>; emits: ((messageId: string | null) => void)[] }>();\n private baselined = false;\n /**\n * Whether the seen set notes mentions (`+m`): false on a set written before it did, whose mentions were reported with\n * their `group.message` already. Its first baseline then notes them without a `group.mentioned` each.\n */\n private mentionsNoted = true;\n private db!: IDBDatabase;\n state: EngineState | null = null;\n\n /** `seenDb`: the IndexedDB database of the seen set (tests give each hub its own). */\n constructor(private readonly journalPath: string, private readonly now: () => number = Date.now, private readonly seenDb = SEEN_DB) {}\n\n /** Loads the journal's last seq and the seen set; call before `sink` is attached. */\n async open(): Promise<void> {\n if (existsSync(this.journalPath)) {\n const lines = readFileSync(this.journalPath, \"utf8\").split(\"\\n\").filter(Boolean);\n this.journalLines = lines.length;\n for (let i = lines.length - 1; i >= 0; i--) {\n try { this.seq = (JSON.parse(lines[i]) as GhostlyEvent).seq; break; } catch { /* a torn last line */ }\n }\n }\n this.db = await new Promise<IDBDatabase>((resolve, reject) => {\n const request = indexedDB.open(this.seenDb, 1);\n request.onupgradeneeded = () => { request.result.createObjectStore(SEEN); this.firstRun = true; };\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n // Two requests for the whole set, never a cursor: a cursor costs one tree search per row on Node's IndexedDB.\n const store = this.db.transaction(SEEN).objectStore(SEEN);\n const all = <T>(request: IDBRequest<T[]>) => new Promise<T[]>((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n const [keys, values] = await Promise.all([all(store.getAllKeys()), all(store.getAll() as IDBRequest<string[]>)]);\n for (let i = 0; i < keys.length; i++) {\n const [chat, id] = keys[i] as [string, string];\n this.seenOf(chat).set(id, values[i]);\n }\n this.mentionsNoted = this.firstRun || this.seen.get(FORMAT)?.get(MENTIONS_NOTED) === \"yes\";\n }\n\n /** The sink the engine server posts to. */\n readonly sink: EngineClientSink = {\n post: (message: EngineEvent | RpcResponse) => {\n try { this.handle(message); } catch (error) { process.stderr.write(`ghostly: event error: ${error instanceof Error ? error.stack : String(error)}\\n`); }\n },\n };\n\n /**\n * The engine as it is at start, before the sink is attached: chats and groups are known without events; messages\n * missing from the seen set are news (they came while no process derived events, a crash), except on a first\n * run, which only learns the history that was already there.\n */\n baseline(state: EngineState, histories: Map<string, readonly StoredMessage[]>): void {\n this.stateChanged(state);\n for (const [chat, messages] of histories) this.messages(chat, messages);\n this.baselined = true;\n this.firstRun = false;\n if (this.seen.get(FORMAT)?.get(MENTIONS_NOTED) !== \"yes\") {\n this.seenOf(FORMAT).set(MENTIONS_NOTED, \"yes\");\n this.persist(FORMAT, [[MENTIONS_NOTED, \"yes\"]]);\n }\n this.mentionsNoted = true;\n }\n\n onEvent(listener: Listener): () => void { this.listeners.add(listener); return () => this.listeners.delete(listener); }\n onState(listener: (state: EngineState) => void): () => void { this.stateListeners.add(listener); return () => this.stateListeners.delete(listener); }\n /** A call signal from a contact's app, as the engine gives it. */\n onCallSignal(listener: (chat: string, signal: string) => void): () => void { this.callListeners.add(listener); return () => this.callListeners.delete(listener); }\n\n /** Events after `since` still in the journal; a gap event first when older ones were dropped. */\n replay(since: number): GhostlyEvent[] {\n if (!existsSync(this.journalPath) || since >= this.seq) return [];\n const out: GhostlyEvent[] = [];\n for (const line of readFileSync(this.journalPath, \"utf8\").split(\"\\n\")) {\n if (!line) continue;\n try { const event = JSON.parse(line) as GhostlyEvent; if (event.seq > since) out.push(event); } catch { /* torn */ }\n }\n if (out.length && out[0].seq > since + 1) out.unshift({ seq: since, id: `events.gap:${since}:${out[0].seq}`, type: \"events.gap\", at: this.now(), from: since + 1, to: out[0].seq - 1 });\n return out;\n }\n\n get lastSeq(): number { return this.seq; }\n\n /** Emits an event the host makes itself (`daemon.started`). */\n emit(type: string, id: string, fields: Record<string, unknown> = {}): GhostlyEvent {\n const event: GhostlyEvent = { seq: ++this.seq, id, type, at: this.now(), ...fields };\n appendFileSync(this.journalPath, JSON.stringify(event) + \"\\n\", { mode: 0o600 });\n if (++this.journalLines > JOURNAL_KEEP * 2) this.trimJournal();\n for (const listener of this.listeners) {\n try { listener(event); } catch { /* one listener's failure is its own */ }\n }\n return event;\n }\n\n private trimJournal(): void {\n const lines = readFileSync(this.journalPath, \"utf8\").split(\"\\n\").filter(Boolean).slice(-JOURNAL_KEEP);\n writeFileSync(this.journalPath + \".tmp\", lines.join(\"\\n\") + \"\\n\", { mode: 0o600 });\n renameSync(this.journalPath + \".tmp\", this.journalPath);\n this.journalLines = lines.length;\n }\n\n private handle(message: EngineEvent | RpcResponse): void {\n switch (message.kind) {\n case \"state\": this.stateChanged(message.state); break;\n case \"messages\": this.messages(message.linkId, message.messages); break;\n // Only what changed: the same events as the whole history would give, and a deletion is named, not missing.\n case \"message-changes\": this.messages(message.linkId, message.messages, message.deleted); break;\n // Calls (WISP 1100 § Calls): the call manager reports them (call.incoming, call.connected, call.ended).\n case \"call-signal\":\n for (const listener of this.callListeners) {\n try { listener(message.linkId, message.signal); } catch (error) { process.stderr.write(`ghostly: call error: ${error instanceof Error ? error.stack : String(error)}\\n`); }\n }\n break;\n default: break;\n }\n }\n\n private stateChanged(state: EngineState): void {\n const quiet = !this.baselined;\n const links = new Map(state.links.map((link) => [link.id, link]));\n for (const [id, link] of links) {\n const shape = chatShape(link), before = this.chats.get(id);\n this.chats.set(id, shape);\n if (quiet) continue;\n if (!before) { this.emit(\"chat.created\", `chat.created:${id}`, { chat: id, summary: { ...chatJson(link), invite: null } }); continue; }\n if (shape.stage && shape.stage !== before.stage) {\n const progress = link.pairingProgress;\n this.emit(\"chat.pairing\", `chat.pairing:${id}:${shape.stage}:${progress?.since ?? this.now()}`, {\n chat: id, stage: shape.stage, ...(progress?.detail ? { detail: progress.detail } : {}), ...(progress?.reason ? { reason: progress.reason } : {}), ...(progress?.peerSeen ? { peerSeen: true } : {}),\n });\n }\n if (shape.live !== before.live || shape.transport !== before.transport) {\n const at = this.now();\n this.emit(\"chat.connection\", `chat.connection:${id}:${shape.live ? shape.transport : \"down\"}:${at}`, { chat: id, live: shape.live, transport: shape.transport, text: link.textDelivery ?? null });\n }\n if (shape.name !== before.name) this.emit(\"chat.renamed\", `chat.renamed:${id}:${this.now()}`, { chat: id, name: shape.name });\n this.pinned(\"chat\", id, before.pin, shape.pin);\n // The contact started or stopped writing (WISP 401 § Typing): it stops by itself 6 s after its last word. What\n // it is doing (typing, recording, thinking, a bot's status) goes with the start, and a change while it lasts\n // is a start again (its id then carries the sequence, so two changes in one millisecond stay apart).\n if (shape.typing !== before.typing) {\n this.emit(shape.typing ? \"typing.started\" : \"typing.stopped\", `typing.${shape.typing ? \"started\" : \"stopped\"}:${id}:${this.now()}`, { chat: id, ...(shape.typing ? typingFields(shape) : {}) });\n } else if (shape.typing && (shape.typingKind !== before.typingKind || shape.typingStatus !== before.typingStatus)) {\n this.emit(\"typing.started\", `typing.started:${id}:${this.now()}:${this.seq + 1}`, { chat: id, ...typingFields(shape) });\n }\n }\n for (const id of [...this.chats.keys()]) {\n if (links.has(id)) continue;\n this.chats.delete(id);\n if (!quiet) this.emit(\"chat.removed\", `chat.removed:${id}`, { chat: id });\n }\n const groups = new Map(state.groups.map((group) => [group.id, group]));\n for (const [id, group] of groups) {\n const shape = groupShape(group), before = this.groups.get(id);\n this.groups.set(id, shape);\n if (quiet) continue;\n if (!before) { this.emit(\"group.created\", `group.created:${id}`, { group: id, summary: groupJson(group) }); continue; }\n if (shape.status !== before.status) this.emit(\"group.status\", `group.status:${id}:${shape.status}:${this.now()}`, { group: id, status: shape.status });\n const joined = shape.members.filter((key) => !before.members.includes(key));\n const left = before.members.filter((key) => !shape.members.includes(key));\n if (joined.length || left.length) this.emit(\"group.members\", `group.members:${id}:${this.now()}`, { group: id, joined, left });\n this.pinned(\"group\", id, before.pin, shape.pin);\n // A member started or stopped writing in a private group (WISP 902 · Group Mesh § Typing), as `typing.*` in a chat.\n for (const [member, word] of Object.entries(shape.typing)) {\n const was = before.typing[member];\n if (was && was.kind === word.kind && was.status === word.status) continue;\n this.emit(\"group.typing.started\", `group.typing.started:${id}:${member}:${this.now()}${was ? `:${this.seq + 1}` : \"\"}`, { group: id, member, ...word });\n }\n for (const member of Object.keys(before.typing)) {\n if (!shape.typing[member]) this.emit(\"group.typing.stopped\", `group.typing.stopped:${id}:${member}:${this.now()}`, { group: id, member });\n }\n }\n for (const id of [...this.groups.keys()]) {\n if (groups.has(id)) continue;\n this.groups.delete(id);\n if (!quiet) this.emit(\"group.removed\", `group.removed:${id}`, { group: id });\n }\n for (const [id, payment] of Object.entries(state.payments ?? {})) {\n const shape = `${payment.state}|${payment.error ?? \"\"}|${payment.closed ? 1 : 0}`, before = this.payments.get(id);\n this.payments.set(id, shape);\n if (quiet || before === shape) continue;\n const json = paymentJson(payment);\n if (before === undefined) this.emit(\"payment.created\", `payment.created:${id}`, { chat: payment.linkId, payment: json });\n else this.emit(\"payment.updated\", `payment.updated:${id}:${payment.state}:${this.now()}`, { chat: payment.linkId, payment: json });\n }\n for (const link of state.links) {\n for (const identity of link.identities?.received ?? []) {\n const key = `${link.id}:${identity.id}`, shape = identity.status, before = this.received.get(key);\n this.received.set(key, shape);\n if (quiet || before === shape) continue;\n this.emit(before === undefined ? \"identity.received\" : \"identity.status\", `identity.${before === undefined ? \"received\" : \"status\"}:${key}:${shape}`, {\n chat: link.id, identity: { id: identity.id, provider: identity.provider, subject: identity.subject, status: identity.status, verified: identity.verified },\n });\n }\n }\n for (const [id, transfer] of Object.entries(state.transfers ?? {})) {\n const shape = `${transfer.state}|${transfer.stage ?? \"\"}`, before = this.transfers.get(id);\n this.transfers.set(id, shape);\n if (quiet || before === shape) continue;\n const chat = state.links.find((l) => id.startsWith(`${l.id}-`))?.id ?? null;\n // A group's file (WISP 503) names its group.\n const group = chat ? undefined : state.groups.find((g) => id.startsWith(`group-${g.id}-`))?.id;\n const fields = { chat, ...(group ? { group } : {}), file: id, direction: transfer.direction ?? (id.includes(\"-in-\") ? \"in\" : \"out\"), size: transfer.size, transferred: transfer.transferred, ...(transfer.error ? { error: transfer.error } : {}) };\n let emit: ((messageId: string | null) => void) | null = null;\n if (transfer.state === \"done\") { const key = `file.done:${id}`; emit = (messageId) => this.emit(\"file.done\", key, { ...fields, messageId }); }\n else if (transfer.state === \"failed\") { const key = `file.failed:${id}:${this.now()}`; emit = (messageId) => this.emit(\"file.failed\", key, { ...fields, messageId, retry: !!transfer.retry }); }\n else if (transfer.stage === \"asking\" && fields.direction === \"in\") { const key = `file.offered:${id}:${this.now()}`; emit = (messageId) => this.emit(\"file.offered\", key, { ...fields, messageId, room: transfer.room ?? null }); }\n else if (transfer.stage) {\n const key = `file.stage:${id}:${transfer.stage}:${this.now()}`, stage = { stage: transfer.stage, ...(transfer.pausedBy ? { pausedBy: transfer.pausedBy } : {}) };\n emit = (messageId) => this.emit(\"file.stage\", key, { ...fields, messageId, ...stage });\n }\n if (emit) this.fileEvent(id, emit);\n }\n this.state = state;\n for (const listener of this.stateListeners) {\n try { listener(state); } catch { /* its own */ }\n }\n }\n\n /**\n * Someone else pinned a message, or unpinned it (WISP 400 § Pinned message): `chat.pinned` or `group.pinned`, with\n * `messageId` (the row here; null when it is not here, or unpinned), `ref` (the id both sides know), `by` (`peer` or a\n * member key) and `removed`. What this profile pins itself says nothing: its command answered already.\n */\n private pinned(kind: \"chat\" | \"group\", id: string, before: PinView | null, now: PinView | null): void {\n if (!now || now.by === \"me\" || (before?.id === now.id && before.at === now.at)) return;\n const fields = { [kind]: id, messageId: now.messageId ?? null, ref: now.id || null, by: now.by, removed: !now.id };\n this.emit(`${kind}.pinned`, `${kind}.pinned:${id}:${now.id || \"off\"}:${now.at}`, fields);\n }\n\n /**\n * A `file.*` event names the message that carries the file. The engine can say where a transfer stands before it\n * stores that message: the event then waits for it, up to 2 s, and goes with `messageId: null` after that.\n */\n private fileEvent(fileId: string, emit: (messageId: string | null) => void): void {\n const pending = this.pendingFiles.get(fileId);\n const messageId = this.fileMessages.get(fileId);\n if (messageId && !pending) { emit(messageId); return; }\n if (pending) { pending.emits.push(emit); return; }\n const timer = setTimeout(() => this.flushFile(fileId, null), FILE_MESSAGE_WAIT_MS);\n timer.unref?.();\n this.pendingFiles.set(fileId, { timer, emits: [emit] });\n }\n\n private flushFile(fileId: string, messageId: string | null): void {\n const pending = this.pendingFiles.get(fileId);\n if (!pending) return;\n this.pendingFiles.delete(fileId);\n clearTimeout(pending.timer);\n for (const emit of pending.emits) emit(messageId);\n }\n\n /** Whether this side said something once in a chat (the join notice): kept with the seen set. */\n said(chat: string, what: string): boolean {\n return this.seen.get(`said:${chat}`)?.has(what) ?? false;\n }\n markSaid(chat: string, what: string): void {\n this.seenOf(`said:${chat}`).set(what, \"yes\");\n this.persist(`said:${chat}`, [[what, \"yes\"]]);\n }\n\n private seenOf(chat: string): Map<string, string> {\n let map = this.seen.get(chat);\n if (!map) this.seen.set(chat, (map = new Map()));\n return map;\n }\n\n /** A chat's or group's whole history, or with `deleted` only what changed in it (then only those ids are gone). */\n private messages(chat: string, messages: readonly StoredMessage[], deleted?: readonly string[]): void {\n const known = this.seenOf(chat);\n const group = chat.startsWith(\"group:\") ? chat.slice(6) : null;\n // A group's whole history, empty: I left or forgot the group and its history went with it (a group joined again\n // starts from nothing). Nobody deleted a message: its rows are forgotten here without a `group.deleted` each.\n const quiet = (this.firstRun && !this.baselined) || (!!group && !deleted && messages.length === 0);\n const changes: [string, string | null][] = [];\n const present = new Set<string>();\n this.reactions(chat, group, messages, quiet);\n const files: [string, string][] = [];\n for (const message of messages) {\n present.add(message.id);\n if (message.file && this.fileMessages.get(message.file.id) !== message.id) { this.fileMessages.set(message.file.id, message.id); files.push([message.file.id, message.id]); }\n let state = stateOf(message);\n const before = known.get(message.id);\n // A mention reported stays reported: an edit that drops it and one that names me again say nothing more.\n if (before !== undefined && mentionedIn(before) && !mentionedIn(state)) state = withFlag(state, MENTIONED);\n if (before === state) continue;\n known.set(message.id, state);\n changes.push([message.id, state]);\n if (quiet) continue;\n const json = group ? groupMessageJson(message, this.state?.groups.find((g) => g.id === group)) : chatMessageJson(message, this.state?.links.find((l) => l.id === chat));\n if (before === undefined) {\n if (group) {\n const type = message.event ? \"group.event\" : message.sender === \"peer\" ? \"group.message\" : \"group.sent\";\n this.emit(type, `${type}:${group}:${message.id}`, { group, message: json });\n } else {\n const notice = JOIN_NOTICE.exec(message.text);\n const type = notice ? (message.sender === \"peer\" ? \"chat.joined\" : \"chat.announced\") : message.sender === \"peer\" ? \"message.received\" : \"message.sent\";\n this.emit(type, `${type}:${chat}:${message.id}`, { chat, message: json, ...(notice ? { name: notice[1] ?? null } : {}) });\n }\n if (message.sender === \"peer\" && message.press) this.pressed(chat, group, message, json);\n continue;\n }\n // A press the engine learned after the reply was first seen here: a group reply first held from a copy a member\n // handed on without its reply box, which its author's whole copy completes (WISP 902 · Group Mesh § Catch-up).\n if (message.sender === \"peer\" && message.press && !pressedIn(before)) this.pressed(chat, group, message, json);\n // A mention of me learned after the message was reported: a group message first held from a copy a member handed\n // on without its author's whole signature, which the whole copy completes (WISP 902 · Group Mesh § Catch-up), or\n // an edit that names me. Its `group.message` said no mention; this says it, once.\n if (group && mentionedIn(state) && !mentionedIn(before) && this.mentionsNoted)\n this.emit(\"group.mentioned\", `group.mentioned:${group}:${message.id}`, { group, messageId: message.id, message: json });\n if (group) {\n // A new text in a group (WISP 902 § Edits): mine as made here, a member's as it came. Once per edit number.\n const [, edits] = splitState(state), [, edited] = splitState(before);\n if (edits > edited) this.emit(\"group.message.edited\", `group.message.edited:${group}:${message.id}:${edits}`, { group, messageId: message.id, edits, message: json });\n } else {\n const [delivery, edits] = splitState(state), [was, edited] = splitState(before);\n // A new text (WISP 400 § Edits): mine as made here, the contact's as it came. Once per edit number. The engine's\n // own edit that sends a question's buttons again (WISP 406 · Message Buttons) is none: same text, nobody's edit.\n if (edits > edited && !(message.sender === \"me\" && message.edit?.restore)) this.emit(\"message.edited\", `message.edited:${chat}:${message.id}:${edits}`, { chat, messageId: message.id, edits, message: json });\n if (message.sender === \"me\" && delivery !== was)\n this.emit(\"message.delivery\", `message.delivery:${chat}:${message.id}:${delivery}`, { chat, messageId: message.id, delivery, ...(message.deliveryError ? { error: message.deliveryError } : {}) });\n }\n }\n for (const id of deleted ?? [...known.keys()]) {\n if (present.has(id) || !known.has(id)) continue;\n known.delete(id);\n changes.push([id, null]);\n if (!quiet) this.emit(group ? \"group.deleted\" : \"message.deleted\", `${group ? \"group\" : \"message\"}.deleted:${group ?? chat}:${id}`, group ? { group, messageId: id } : { chat, messageId: id });\n }\n if (changes.length) this.persist(chat, changes);\n // After the message's own event: what waited for it goes now.\n for (const [fileId, messageId] of files) this.flushFile(fileId, messageId);\n }\n\n /**\n * A press on a button of a message of mine (WISP 406 · Message Buttons), after the reply's own event: the question\n * (`messageId`), the button, its label, who pressed (`by`: the chat, or a group member's key), `name` (the chat's name\n * here, as `chat list` gives it: its label, else the contact's own name; a group member's roster name) and the reply\n * (`replyId`). `untrusted.name` is the name the person gave themselves, as an agent turn's: theirs to choose, so\n * data, never instructions. The engine marks a reply a press only while the buttons were open for that person.\n */\n private pressed(chat: string, group: string | null, message: StoredMessage, json: { nick: string | null }): void {\n const press = message.press!;\n const link = group ? undefined : this.state?.links.find((l) => l.id === chat);\n const name = group ? json.nick : link ? chatJson(link).name : null;\n this.emit(\"button.pressed\", `button.pressed:${group ?? chat}:${message.id}`, {\n ...(group ? { group } : { chat }), messageId: press.messageId, button: press.button, label: press.label,\n by: group ? message.member ?? null : chat, name: name ?? null, replyId: message.id, ...(press.inferred ? { inferred: true } : {}),\n untrusted: { name: json.nick ?? null },\n });\n }\n\n /**\n * Reactions that changed (WISP 400 § Reactions): `message.reaction` in a chat, `group.reaction` in a group, one per\n * person and number; `emoji` \"\" when one was taken back. Kept apart from the messages' own seen set.\n */\n private reactions(chat: string, group: string | null, messages: readonly StoredMessage[], quiet: boolean): void {\n const key = `reactions:${chat}`, known = this.seenOf(key);\n const changes: [string, string | null][] = [];\n for (const message of messages) for (const [by, r] of Object.entries(message.reactions ?? {})) {\n const id = `${message.id}|${by}`, value = String(r.n);\n if (known.get(id) === value) continue;\n const before = known.get(id);\n known.set(id, value);\n changes.push([id, value]);\n // A reaction taken back before this process ever reported it says nothing.\n if (quiet || (before === undefined && !r.e)) continue;\n const fields = { messageId: message.id, by, emoji: r.e, removed: !r.e, mine: message.sender === \"me\" };\n if (group) this.emit(\"group.reaction\", `group.reaction:${group}:${message.id}:${by}:${r.n}`, { group, ...fields });\n else this.emit(\"message.reaction\", `message.reaction:${chat}:${message.id}:${by}:${r.n}`, { chat, ...fields });\n }\n if (changes.length) this.persist(key, changes);\n }\n\n /**\n * Relaxed: nobody waits for it, and a note lost to a power cut is made again from the history at the next start\n * (its event is then reported a second time, as after a crash between a message's row and its note). A disk sync\n * for each one held the whole daemon for milliseconds, three times per message sent.\n */\n private persist(chat: string, changes: [string, string | null][]): void {\n const tx = this.db.transaction(SEEN, \"readwrite\", { durability: \"relaxed\" });\n const store = tx.objectStore(SEEN);\n for (const [id, state] of changes) {\n if (state === null) store.delete([chat, id]);\n else store.put(state, [chat, id]);\n }\n }\n}\n\nfunction deliveryOf(message: StoredMessage): string {\n return message.sender === \"peer\" ? \"received\" : message.delivery ?? \"sent\";\n}\n\n/** What a received button press adds to its delivery in the seen set (`received+p`): it is reported once. */\nconst PRESSED = \"+p\";\n/** What a group message that mentions me adds (`received+m`): a mention learned late is reported once. */\nconst MENTIONED = \"+m\";\n/** The seen set's own notes: whether it notes mentions (sets from before `+m` do not). */\nconst FORMAT = \"format:\";\nconst MENTIONS_NOTED = \"mentions\";\n\n/**\n * What the seen set keeps of a message: its delivery (`+p` once a press on a message of mine was reported, `+m` once a\n * group message's mention of me was), and its edit number once edited (`sent#e3`, `received+p+m#e2`).\n */\nfunction stateOf(message: StoredMessage): string {\n const peer = message.sender === \"peer\";\n const delivery = deliveryOf(message) + (peer && message.press ? PRESSED : \"\") + (peer && message.mentioned && message.linkId.startsWith(\"group:\") ? MENTIONED : \"\");\n return message.edit ? `${delivery}#e${message.edit.seq}` : delivery;\n}\nconst flagsOf = (state: string): string[] => splitState(state)[0].split(\"+\").slice(1).map((flag) => \"+\" + flag);\nconst pressedIn = (state: string): boolean => flagsOf(state).includes(PRESSED);\nconst mentionedIn = (state: string): boolean => flagsOf(state).includes(MENTIONED);\n/** The state with a flag added before its edit number. */\nfunction withFlag(state: string, flag: string): string {\n const at = state.lastIndexOf(\"#e\");\n return at === -1 ? state + flag : state.slice(0, at) + flag + state.slice(at);\n}\nfunction splitState(state: string): [string, number] {\n const at = state.lastIndexOf(\"#e\");\n return at === -1 ? [state, 0] : [state.slice(0, at), Number(state.slice(at + 2)) || 0];\n}\n\nfunction chatShape(link: LinkView): ChatShape {\n const view = chatJson(link);\n return {\n stage: view.stage, live: view.live, transport: view.transport, name: view.name, typing: !!link.peerTyping,\n typingKind: link.peerTyping ? link.peerTypingKind ?? \"typing\" : \"typing\", typingStatus: link.peerTyping ? link.peerTypingStatus ?? null : null, pin: link.pin ?? null,\n };\n}\n\n/** A `typing.started`'s word: the kind, and the contact's status line when it gave one (already cleaned). */\nfunction typingFields(shape: ChatShape): { kind: string; status?: string } {\n return { kind: shape.typingKind, ...(shape.typingStatus ? { status: shape.typingStatus } : {}) };\n}\n\nfunction groupShape(group: GroupView): GroupShape {\n return { status: group.status ?? (group.invitation ? \"invited\" : null), members: group.members.map((m) => m.key).sort(),\n typing: Object.fromEntries((group.typing ?? []).map((t) => [t.key, { kind: t.kind ?? \"typing\", ...(t.status ? { status: t.status } : {}) }])), pin: group.pin ?? null };\n}\n","import { fromBase64Url, irohDescriptor, irohRelayUrl, type BoundChannel, type FrameChannel, type NativeBinding, type NativeEndpoint } from \"@ghostly/core\";\n\n/**\n * Iroh (WISP 102) in a browser: the same QUIC/TLS session and binding as the\n * Desktop's native Iroh, but relay only. A page cannot send UDP, so every\n * packet goes through an Iroh relay over a WebSocket; the relay sees who talks\n * to whom and when, never the frames.\n *\n * The wasm (about 1 MB gzip) loads on the first chat that starts an endpoint,\n * never on the app's first load.\n */\n\n/**\n * n0's public relays, the same ones the Desktop's Iroh homes on by default. Iroh itself names them with the trailing\n * dot of a full domain name (`use1-1.relay.n0.iroh.link.`), and compares relay URLs as text; here they have none,\n * because WebKit opens no host name that ends in a dot. `BROWSER_SPELLING` is what keeps the two one relay.\n */\nexport const DEFAULT_IROH_RELAYS = [\n \"https://use1-1.relay.n0.iroh.link/\",\n \"https://euc1-1.relay.n0.iroh.link/\",\n \"https://aps1-1.relay.n0.iroh.link/\",\n \"https://usw1-1.relay.n0.iroh.link/\",\n] as const;\n\n/**\n * The spelling of every relay URL handed to the wasm Iroh (`irohRelayUrl`): the relays it homes on and a contact's\n * relay before a dial. One spelling, so a contact on this endpoint's relay is reached over the connection already\n * open to it; without the dot, so Safari can open it (a Desktop's record names its relay with the dot).\n */\nconst BROWSER_SPELLING = \"plain\";\n\n/**\n * Why a relay URL cannot be used, or null. TLS, except on this machine (a local `iroh-relay --dev`): a page\n * may not open plain WebSockets elsewhere, and the relay's operator would see traffic metadata in the clear.\n */\nexport function irohRelayProblem(value: string): string | null {\n let url: URL;\n try { url = new URL(value); } catch { return `Not a relay address: ${value}`; }\n const local = [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname);\n if (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && local)) return `Use an https:// relay address: ${value}`;\n if (url.username || url.password || url.search || url.hash || value.length > 256) return `Not a relay address: ${value}`;\n return null;\n}\n\n/** A browser endpoint's descriptor: no direct addresses, only its relay. */\nexport interface IrohWebDescriptor { id: string; relay: string | null; addresses: string[] }\n\ninterface WasmConn {\n binding(): NativeBinding;\n send(text: string): Promise<void>;\n recv(): Promise<string | undefined>;\n rtt(): number;\n close(): void;\n free(): void;\n}\ninterface WasmNode {\n address(): IrohWebDescriptor;\n connect(descriptor: unknown, timeoutMs: number): Promise<WasmConn>;\n accept(): Promise<WasmConn | undefined>;\n close(): Promise<void>;\n free(): void;\n}\nexport interface IrohWasm {\n IrohNode: { start(seed: Uint8Array, relays: string[], onlineMs: number): Promise<WasmNode> };\n}\n\nlet loaded: Promise<IrohWasm> | null = null;\n/** Loads the wasm once per page. Tests pass their own `load` (Node reads the file). */\nexport function loadIrohWasm(): Promise<IrohWasm> {\n loaded ??= import(\"@ghostly/iroh-web\").then(async module => {\n await module.default();\n return module as unknown as IrohWasm;\n }).catch(error => { loaded = null; throw error; });\n return loaded;\n}\n\nexport interface IrohWebOptions {\n /** Relay URLs, first preferred. Default: n0's public relays. */\n relays?: readonly string[];\n /** How long to wait for the first relay to answer. */\n onlineMs?: number;\n connectMs?: number;\n load?: () => Promise<IrohWasm>;\n}\n\nconst SEND_BUDGET = 120 * 1024;\nconst MAX_FRAME = 60 * 1024;\n\n/** One paired channel over one QUIC stream. Sends are queued in order. */\nclass IrohWebChannel implements FrameChannel {\n private reader: FrameChannel[\"onMessage\"] = null;\n private buffered: string[] = [];\n private queue = Promise.resolve();\n private closed = false;\n bufferedAmount = 0;\n readonly sendBudget = SEND_BUDGET;\n onClose: FrameChannel[\"onClose\"] = null;\n constructor(private conn: WasmConn, private forget: () => void) { void this.read(); }\n get onMessage() { return this.reader; }\n set onMessage(reader: FrameChannel[\"onMessage\"]) {\n this.reader = reader;\n if (reader) for (const text of this.buffered.splice(0)) reader(text);\n }\n private async read() {\n try {\n for (;;) {\n const text = await this.conn.recv();\n if (text === undefined || this.closed) break;\n if (this.reader) this.reader(text);\n else if (this.buffered.length < 64) this.buffered.push(text);\n else break;\n }\n } catch { /* A broken stream ends the channel. */ }\n this.close();\n }\n send(data: string | Uint8Array) {\n if (typeof data !== \"string\" || this.closed || !data.length || data.length > MAX_FRAME || this.bufferedAmount + data.length > SEND_BUDGET)\n throw new Error(\"Iroh channel is closed or its send budget is full\");\n this.bufferedAmount += data.length;\n this.queue = this.queue.then(() => this.closed ? undefined : this.conn.send(data))\n .catch(() => this.close()).finally(() => { this.bufferedAmount -= data.length; });\n }\n drained() { return this.queue; }\n get rttMs() { const rtt = this.conn.rtt(); return rtt >= 0 ? rtt : undefined; }\n close() {\n if (this.closed) return;\n this.closed = true; this.buffered = [];\n try { this.conn.close(); } catch { /* Already gone. */ }\n this.forget();\n this.onClose?.();\n }\n}\n\n/** A NativeEndpoint for `iroh/1`, as the Desktop's is, for one chat's transport seed. */\nexport async function createIrohWebEndpoint(seedB64: string, options: IrohWebOptions = {}): Promise<NativeEndpoint> {\n const seed = fromBase64Url(seedB64);\n if (seed.length !== 32) throw new Error(\"Invalid transport seed\");\n // Either spelling of a relay (with or without the trailing dot) homes on it under the one a browser opens.\n const relays = [...new Set((options.relays?.length ? options.relays : DEFAULT_IROH_RELAYS).map(relay => irohRelayUrl(relay, BROWSER_SPELLING)))];\n const wasm = await (options.load ?? loadIrohWasm)();\n const node = await wasm.IrohNode.start(seed, relays, options.onlineMs ?? 10_000);\n const channels = new Set<IrohWebChannel>();\n let stopped = false;\n let handler: NativeEndpoint[\"onConnection\"] = null;\n const early: BoundChannel[] = [];\n const bind = (conn: WasmConn): BoundChannel => {\n const channel: IrohWebChannel = new IrohWebChannel(conn, () => channels.delete(channel));\n channels.add(channel);\n return { channel, binding: conn.binding() };\n };\n const endpoint: NativeEndpoint = {\n transport: \"iroh/1\",\n // Relay only: the rank puts it after every direct path (WISP 100, \"Relayed\").\n descriptor: { ...node.address(), relayed: true },\n onDescriptor: null,\n get onConnection() { return handler; },\n set onConnection(value) { handler = value; if (value) for (const bound of early.splice(0)) value(bound); },\n async connect(descriptor) {\n if (stopped) throw new Error(\"Iroh endpoint is stopped\");\n const conn = await node.connect(irohDescriptor(descriptor, BROWSER_SPELLING), options.connectMs ?? 20_000);\n if (stopped) { conn.close(); throw new Error(\"Iroh endpoint is stopped\"); }\n return bind(conn);\n },\n async close() {\n if (stopped) return;\n stopped = true;\n for (const channel of [...channels]) channel.close();\n for (const bound of early.splice(0)) bound.channel.close();\n await node.close().catch(() => {});\n },\n };\n void (async () => {\n while (!stopped) {\n let conn: WasmConn | undefined;\n try { conn = await node.accept(); } catch { break; }\n if (!conn) break;\n if (stopped) { conn.close(); break; }\n const bound = bind(conn);\n const accept = endpoint.onConnection;\n if (accept) accept(bound); else if (early.length < 4) early.push(bound); else bound.channel.close();\n }\n // The relay went away for good (or the endpoint was closed): stop offering Iroh.\n if (!stopped) { endpoint.onUnavailable?.(); await endpoint.close(); }\n })();\n return endpoint;\n}\n","import type { BitcoinNetwork } from \"@ghostly/core\";\n\n/**\n * The part of the Fedimint web SDK (`@fedimint/core`, WebAssembly client in a worker) Ghostly uses, behind an\n * interface small enough to fake in tests. The client runs in a worker, on a file it reads and writes through a sync\n * access handle: in a browser, a module worker and the origin-private file system; on Node, the CLI's\n * `worker_threads` worker and a file of the profile (`FedimintPlatform`).\n *\n * One client database per joined federation (`ghostly-fedimint-<id>.db`, one file): the SDK's services are bound to one client per database, and each database is held by one worker.\n * Every database of a profile and mode is given the same mnemonic; the client derives a different secret per\n * federation from it. Amounts are millisatoshis, as the SDK counts them.\n */\nexport interface FederationGuardian { name: string; url: string }\nexport interface FederationInfo {\n federationId: string;\n /** `meta.federation_name`, when the guardians set one. */\n name?: string;\n guardians: FederationGuardian[];\n /** Core consensus version, `major.minor`. */\n consensusVersion: string;\n /** The Bitcoin network of its wallet module. Unknown when the federation has no v1 wallet module. */\n network?: BitcoinNetwork;\n /** Module kinds (`mint`, `ln`, `wallet`, `meta`…). The SDK uses `mint`, `ln` and `wallet` (v1). */\n modules: string[];\n /** `meta.welcome_message`, shortened. */\n welcome?: string;\n}\n\n/** Out-of-band notes, parsed offline (signatures and prefix only: whether they are spent is the federation's to say). */\nexport interface ParsedNotes { amountMsats: number; federationIdPrefix: string; federationId?: string }\n\n/** Where a spend of notes stands. `canceled` = taken back (reissued to us); `taken` = the recipient redeemed them. */\nexport type SpendState = \"created\" | \"canceling\" | \"canceled\" | \"taken\" | \"refunded\";\n/** A redeem (reissue) of notes we were given. */\nexport type RedeemState = \"created\" | \"issuing\" | \"done\" | \"failed\";\nexport type ReceiveState = \"open\" | \"funded\" | \"claimed\" | \"canceled\";\nexport interface PayOutcome { state: \"pending\" | \"paid\" | \"failed\"; preimage?: string; error?: string }\n\nexport interface FedimintOperation {\n id: string;\n /** Module: `mint`, `ln`, `wallet`. */\n kind: string;\n /** What it was: `spend_oob`, `reissuance`, `receive`, `pay`… as the SDK names it. */\n variant?: string;\n createdAt: number;\n amountMsats?: number;\n /** The SDK's final outcome, once there is one. */\n outcome?: unknown;\n /** What Ghostly asked to be kept with it (`extra_meta`): the payment id it belongs to. */\n ghostly?: string;\n /** Lightning: the invoice it paid or was paid on, and the fee it paid (msats). */\n invoice?: string;\n feeMsats?: number;\n}\n\nexport interface FedimintClient {\n readonly federationId: string;\n info(): Promise<FederationInfo>;\n balance(): Promise<number>;\n /** Called with every new balance, until the returned function is called. */\n onBalance(listener: (msats: number) => void): () => void;\n parseNotes(notes: string): Promise<ParsedNotes>;\n /** Takes notes out of the wallet, as a bearer string. Unredeemed after `cancelAfterSecs`, they come back by themselves. */\n spend(amountMsats: number, options: { cancelAfterSecs: number; ghostly: string }): Promise<{ notes: string; operationId: string }>;\n /** Asks to take notes back: they are reissued to us unless the recipient redeemed them first. */\n cancelSpend(operationId: string): Promise<void>;\n /** The latest state within `waitMs` (it returns early on a final one). Undefined: the client knows no such operation. */\n spendState(operationId: string, waitMs: number): Promise<SpendState | undefined>;\n redeem(notes: string, ghostly: string): Promise<string>;\n redeemState(operationId: string, waitMs: number): Promise<RedeemState | undefined>;\n createInvoice(amountMsats: number, description: string, expirySecs: number, ghostly: string): Promise<{ operationId: string; invoice: string }>;\n receiveState(operationId: string, waitMs: number): Promise<ReceiveState | undefined>;\n /** What the federation's gateway would charge to pay this amount, in msats. Undefined: no gateway. */\n gatewayFee(amountMsats: number): Promise<number | undefined>;\n payInvoice(invoice: string, ghostly: string): Promise<{ operationId: string; feeMsats: number; internal: boolean }>;\n payState(operationId: string, internal: boolean, waitMs: number): Promise<PayOutcome>;\n operations(limit: number): Promise<FedimintOperation[]>;\n /** Ends the worker; the database stays. */\n close(): Promise<void>;\n}\n\nexport interface FedimintSdk {\n preview(invite: string): Promise<FederationInfo>;\n /** Joins (or, `recover`, restores from the federation's backup of this mnemonic) into a new database. */\n join(params: { database: string; mnemonic: string; invite: string; recover: boolean }): Promise<FedimintClient>;\n /** Opens a database joined before. */\n open(params: { database: string; mnemonic: string }): Promise<FedimintClient>;\n /** Deletes a database: only one that never held money (a failed join). */\n remove(database: string): Promise<void>;\n /** Whether this device has the database (a profile restored from a backup has the records, not the files). */\n exists(database: string): Promise<boolean>;\n}\n\nexport const fedimintDatabase = (id: string) => `ghostly-fedimint-${id}.db`;\n\n// ── The real SDK ───────────────────────────────────────────────────────────────────────────────────────────\n\ntype Json = null | boolean | number | string | Json[] | { [key: string]: Json };\nconst MODULE_WASM = () => import(\"@fedimint/fedimint-client-wasm-bundler/fedimint_client_wasm_bg.wasm?url\");\n/** The SDK checks it with an assert: any other length kills the worker. One client per database, always this name. */\nconst CLIENT_NAME = \"dd5135b2-c228-41b7-a4f9-3b6e7afe3088\";\nconst NETWORKS: Record<string, BitcoinNetwork> = { bitcoin: \"bitcoin\", signet: \"signet\", testnet: \"testnet\", testnet4: \"testnet\", regtest: \"regtest\", mutinynet: \"mutinynet\" };\n/**\n * A v1 wallet (or Lightning) module's client config comes consensus-encoded as hex (`config` from a joined client,\n * `unknown_module_hex` in a preview); its network is the chain's magic, a u32 as a compact-size integer (`fe` +\n * four bytes, little-endian). Exactly one network must be found, or it stays unknown.\n */\nconst MAGIC: [string, BitcoinNetwork][] = [[\"fed9b4bef9\", \"bitcoin\"], [\"fe0709110b\", \"testnet\"], [\"fe283f161c\", \"testnet\"], [\"fe40cf030a\", \"signet\"], [\"fecb2ddfa5\", \"mutinynet\"], [\"fedab5bffa\", \"regtest\"]];\nfunction moduleNetwork(module: { network?: unknown; config?: unknown; unknown_module_hex?: unknown }): BitcoinNetwork | undefined {\n if (typeof module.network === \"string\") return NETWORKS[module.network];\n const encoded = typeof module.config === \"string\" ? module.config : module.unknown_module_hex;\n if (typeof encoded !== \"string\" || !/^[0-9a-f]*$/i.test(encoded)) return undefined;\n const hex = encoded.toLowerCase();\n const found = [...new Set(MAGIC.filter(([magic]) => hex.includes(magic)).map(([, network]) => network))];\n return found.length === 1 ? found[0] : undefined;\n}\nconst hex = (value: unknown) => typeof value === \"string\" ? value : Array.isArray(value) ? value.map((b) => Number(b).toString(16).padStart(2, \"0\")).join(\"\") : undefined;\n\nlet compiling: Promise<WebAssembly.Module> | undefined;\n/** Compiled once per page (11 MB): each worker is handed the module, not the bytes. */\nfunction compiled(): Promise<WebAssembly.Module> {\n return compiling ??= (async () => {\n const url = (await MODULE_WASM()).default;\n const response = await fetch(url);\n // A server that sends the wrong MIME type cannot stream: compile from the bytes instead.\n try { return await WebAssembly.compileStreaming(response.clone()); } catch { return WebAssembly.compile(await response.arrayBuffer()); }\n })().catch((error) => { compiling = undefined; throw error; });\n}\n\n/** A transport of `@fedimint/core` onto our own worker (fedimintWorker.ts). */\nasync function browserTransport() {\n const [{ Transport }, module] = await Promise.all([import(\"@fedimint/types\"), compiled()]);\n class WorkerTransport extends Transport {\n readonly logger = { debug() {}, info() {}, warn() {}, error() {} };\n private readonly worker = new Worker(new URL(\"./fedimintWorker.ts\", import.meta.url), { type: \"module\" });\n constructor() {\n super();\n this.worker.onmessage = (event) => this.messageHandler(event.data);\n this.worker.onerror = (event) => { event.preventDefault(); this.messageHandler({ type: \"error\", error: `The Fedimint client stopped: ${event.message || \"unknown error\"}` }); };\n }\n postMessage(message: { type: string; payload?: Json; requestId?: number }) {\n // The worker gets the compiled module with its init, and nothing to log.\n this.worker.postMessage(message.type === \"init\" ? { ...message, payload: { ...(message.payload as object), module } } : message);\n }\n terminate() { this.worker.terminate(); }\n }\n return new WorkerTransport();\n}\n\nconst text = (value: unknown, max = 200) => typeof value === \"string\" ? value.slice(0, max) : undefined;\nconst errorText = (error: unknown) => error instanceof Error ? error.message : typeof error === \"string\" ? error : JSON.stringify(error);\n\nexport function federationInfo(federationId: string, config: unknown): FederationInfo {\n const c = config as { global?: { api_endpoints?: Record<string, { url?: unknown; name?: unknown }>; consensus_version?: { major?: unknown; minor?: unknown }; meta?: Record<string, unknown> }; modules?: Record<string, { kind?: unknown; network?: unknown; config?: unknown; unknown_module_hex?: unknown }> };\n const endpoints = Object.entries(c?.global?.api_endpoints ?? {}).sort(([a], [b]) => Number(a) - Number(b));\n const modules = Object.values(c?.modules ?? {});\n // The wallet module says it; a federation without one (Lightning and ecash only) says it through its Lightning module.\n const network = modules.filter((m) => m?.kind === \"wallet\").map(moduleNetwork).find(Boolean) ?? modules.filter((m) => m?.kind === \"ln\").map(moduleNetwork).find(Boolean);\n const version = c?.global?.consensus_version;\n return {\n federationId,\n name: text(c?.global?.meta?.federation_name, 80),\n guardians: endpoints.slice(0, 64).map(([peer, e]) => ({ name: text(e?.name, 80) || `Guardian ${peer}`, url: text(e?.url, 300) ?? \"\" })),\n consensusVersion: `${Number(version?.major ?? 0)}.${Number(version?.minor ?? 0)}`,\n network,\n modules: [...new Set(modules.map((m) => (typeof m?.kind === \"string\" ? m.kind.slice(0, 32) : \"\")).filter(Boolean))],\n welcome: text(c?.global?.meta?.welcome_message, 280),\n };\n}\n\n/** The first value a subscription gives that is final, or the last one within `waitMs`. */\nfunction watch<S>(subscribe: (onState: (state: S) => void, onError: (error: string) => void) => () => void, final: (state: S) => boolean, waitMs: number): Promise<S | undefined> {\n return new Promise((resolve) => {\n let last: S | undefined, done = false, cancel: () => void = () => {};\n const finish = () => { if (done) return; done = true; clearTimeout(timer); cancel(); resolve(last); };\n const timer = setTimeout(finish, waitMs);\n cancel = subscribe((state) => { last = state; if (final(state)) finish(); }, () => finish());\n });\n}\n\nconst SPEND: Record<string, SpendState> = { Created: \"created\", UserCanceledProcessing: \"canceling\", UserCanceledSuccess: \"canceled\", UserCanceledFailure: \"taken\", Success: \"taken\", Refunded: \"refunded\" };\nconst spendFinal = (s: SpendState) => s === \"canceled\" || s === \"taken\" || s === \"refunded\";\nconst REDEEM: Record<string, RedeemState> = { Created: \"created\", Issuing: \"issuing\", Done: \"done\" };\nconst receiveOf = (s: unknown): ReceiveState => s === \"claimed\" ? \"claimed\" : s === \"funded\" || s === \"awaiting_funds\" ? \"funded\" : typeof s === \"object\" && s && \"canceled\" in s ? \"canceled\" : \"open\";\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- the SDK's own types are loose unions of JSON; this is where they are narrowed. */\nclass RealClient implements FedimintClient {\n constructor(readonly federationId: string, private wallet: any, private director: any, private end: () => void) {}\n async info() { return federationInfo(this.federationId, await this.wallet.federation.getConfig()); }\n balance(): Promise<number> { return this.wallet.balance.getBalance(); }\n onBalance(listener: (msats: number) => void) { return this.wallet.balance.subscribeBalance(listener, () => {}); }\n async parseNotes(notes: string): Promise<ParsedNotes> {\n const parsed = await this.director.parseOobNotes(notes);\n return { amountMsats: Number(parsed.total_amount), federationIdPrefix: String(parsed.federation_id_prefix), federationId: parsed.federation_id ?? undefined };\n }\n async spend(amountMsats: number, { cancelAfterSecs, ghostly }: { cancelAfterSecs: number; ghostly: string }) {\n const { notes, operation_id } = await this.wallet.mint.spendNotes(amountMsats, cancelAfterSecs, false, { ghostly });\n return { notes: String(notes), operationId: String(operation_id) };\n }\n cancelSpend(operationId: string): Promise<void> { return this.wallet.mint.tryCancelSpendNotes(operationId); }\n spendState(operationId: string, waitMs: number) {\n return watch<SpendState>((ok, fail) => this.wallet.mint.subscribeSpendNotes(operationId, (s: string) => ok(SPEND[s] ?? \"created\"), fail), spendFinal, waitMs);\n }\n redeem(notes: string, ghostly: string): Promise<string> { return this.wallet.mint.reissueExternalNotes(notes, { ghostly }); }\n redeemState(operationId: string, waitMs: number) {\n return watch<RedeemState>((ok, fail) => this.wallet.mint.subscribeReissueExternalNotes(operationId, (s: unknown) => ok(typeof s === \"string\" ? REDEEM[s] ?? \"created\" : s && typeof s === \"object\" && \"Failed\" in s ? \"failed\" : \"created\"), (error: string) => { ok(\"failed\"); fail(error); }), (s) => s === \"done\" || s === \"failed\", waitMs);\n }\n async createInvoice(amountMsats: number, description: string, expirySecs: number, ghostly: string) {\n const { operation_id, invoice } = await this.wallet.lightning.createInvoice(amountMsats, description, expirySecs, undefined, { ghostly });\n return { operationId: String(operation_id), invoice: String(invoice) };\n }\n receiveState(operationId: string, waitMs: number) {\n return watch<ReceiveState>((ok, fail) => this.wallet.lightning.subscribeLnReceive(operationId, (s: unknown) => ok(receiveOf(s)), fail), (s) => s === \"claimed\" || s === \"canceled\", waitMs);\n }\n async gatewayFee(amountMsats: number) {\n await this.wallet.lightning.updateGatewayCache().catch(() => {});\n const gateway = await this.wallet.lightning.getAvailableGateway().catch(() => null);\n const fees = gateway?.fees;\n if (!fees) return undefined;\n return Number(fees.base_msat ?? 0) + Math.ceil(amountMsats * Number(fees.proportional_millionths ?? 0) / 1_000_000);\n }\n async payInvoice(invoice: string, ghostly: string) {\n const paid = await this.wallet.lightning.payInvoice(invoice, undefined, { ghostly });\n const internal = \"internal\" in paid.payment_type;\n return { operationId: String(internal ? paid.payment_type.internal : paid.payment_type.lightning), feeMsats: Number(paid.fee ?? 0), internal };\n }\n async payState(operationId: string, internal: boolean, waitMs: number): Promise<PayOutcome> {\n const state = await watch<unknown>((ok, fail) => internal ? this.wallet.lightning.subscribeInternalPayment(operationId, ok, fail) : this.wallet.lightning.subscribeLnPay(operationId, ok, fail),\n (s) => typeof s === \"object\" && !!s && (\"success\" in s || \"preimage\" in s || \"refunded\" in s || \"refund_success\" in s || \"unexpected_error\" in s || \"funding_failed\" in s) || s === \"canceled\", waitMs);\n if (state && typeof state === \"object\") {\n const s = state as Record<string, any>;\n // A payment out through the gateway ends in `success`; one to the same federation (internal) in `preimage` (bytes).\n if (s.success?.preimage || s.preimage) return { state: \"paid\", preimage: hex(s.success?.preimage ?? s.preimage) };\n if (\"refunded\" in s || \"refund_success\" in s || \"funding_failed\" in s) return { state: \"failed\", error: errorText(s.refunded?.gateway_error ?? s.funding_failed?.error ?? \"The gateway gave the payment back\") };\n if (\"unexpected_error\" in s) return { state: \"pending\", error: errorText(s.unexpected_error) };\n }\n if (state === \"canceled\") return { state: \"failed\", error: \"Canceled before it was funded\" };\n return { state: \"pending\" };\n }\n async operations(limit: number): Promise<FedimintOperation[]> {\n const list: [{ creation_time?: { secs_since_epoch?: number }; operation_id?: string }, { operation_module_kind?: string; meta?: any; outcome?: any }][] = await this.wallet.federation.listOperations(limit);\n return list.map(([key, log]) => {\n const variant = log.meta?.variant && typeof log.meta.variant === \"object\" ? Object.keys(log.meta.variant)[0] : undefined;\n const detail = variant ? log.meta.variant[variant] : undefined;\n return {\n id: String(key.operation_id ?? \"\"), kind: String(log.operation_module_kind ?? \"\"), variant,\n createdAt: Number(key.creation_time?.secs_since_epoch ?? 0) * 1000,\n amountMsats: typeof log.meta?.amount === \"number\" ? log.meta.amount : typeof detail?.requested_amount === \"number\" ? detail.requested_amount : undefined,\n outcome: log.outcome?.outcome, ghostly: typeof log.meta?.extra_meta?.ghostly === \"string\" ? log.meta.extra_meta.ghostly : undefined,\n invoice: typeof detail?.invoice === \"string\" ? detail.invoice : undefined,\n feeMsats: typeof detail?.fee === \"number\" ? detail.fee : undefined,\n };\n });\n }\n async close() { try { await this.wallet.cleanup(); } catch { /* already gone */ } this.end(); }\n}\n\n/**\n * Where the client runs: a worker per database, and the files the databases are. The browser's is a module worker on\n * the origin-private file system (below); a headless Ghostly on Node brings its own (packages/cli/src/runtime/fedimint.ts).\n */\nexport interface FedimintPlatform {\n /** A `@fedimint/types` Transport onto a new worker of its own, which one database will be opened in. */\n transport(): Promise<{ terminate(): void }>;\n exists(database: string): Promise<boolean>;\n /** Deletes a database, and is done when there is none. */\n remove(database: string): Promise<void>;\n}\n\nconst BROWSER: FedimintPlatform = {\n transport: browserTransport,\n async exists(database) {\n const root = await navigator.storage.getDirectory();\n try { const file = await (await root.getFileHandle(database)).getFile(); return file.size > 0; } catch { return false; }\n },\n async remove(database) {\n const root = await navigator.storage.getDirectory();\n await root.removeEntry(database).catch((error: unknown) => { if ((error as { name?: string })?.name !== \"NotFoundError\") throw error; });\n },\n};\n\nlet loading: Promise<FedimintSdk> | undefined;\n/** Loads the SDK on first use: profiles that never join a federation never fetch its 11 MB. */\nexport function loadFedimintSdk(): Promise<FedimintSdk> {\n return loading ??= fedimintSdkOn(BROWSER).catch((error) => { loading = undefined; throw error; });\n}\n\n/** The SDK on a platform's workers and files. */\nexport async function fedimintSdkOn(platform: FedimintPlatform): Promise<FedimintSdk> {\n const { WalletDirector } = await import(\"@fedimint/core\");\n /** A director on its own worker and database, with the mnemonic in place. */\n const director = async (database: string, mnemonic?: string) => {\n const t = await platform.transport();\n const d = new WalletDirector(t as any, database, true);\n try {\n await d.initialize(database);\n if (mnemonic && !(await d.hasMnemonicSet())) await d.setMnemonic(mnemonic.trim().split(/\\s+/));\n return { director: d, end: () => t.terminate() };\n } catch (error) { t.terminate(); throw error; }\n };\n const client = async (database: string, mnemonic: string, start: (wallet: any) => Promise<void>) => {\n const { director: d, end } = await director(database, mnemonic);\n try {\n const wallet = await d.createWallet();\n await start(wallet);\n return new RealClient(await wallet.federation.getFederationId(), wallet, d, end);\n } catch (error) { end(); throw error; }\n };\n const sdk: FedimintSdk = {\n async preview(invite) {\n // A scratch database of its own: previewing joins nothing.\n const database = fedimintDatabase(`preview-${crypto.randomUUID()}`);\n const { director: d, end } = await director(database);\n try { const preview = await d.previewFederation(invite.trim()); return federationInfo(preview.federation_id, preview.config); }\n finally { end(); await sdk.remove(database).catch(() => {}); }\n },\n join: ({ database, mnemonic, invite, recover }) => client(database, mnemonic, async (wallet) => {\n // joinFederation says false instead of throwing; its reason goes to the (silenced) logger.\n if (!(await wallet.joinFederation(invite.trim(), { clientName: CLIENT_NAME, forceRecover: recover }))) throw new Error(\"Could not join the federation: its guardians did not answer, or the invite code is not valid\");\n }),\n open: ({ database, mnemonic }) => client(database, mnemonic, (wallet) => wallet.open(CLIENT_NAME)),\n exists: (database) => platform.exists(database),\n remove: (database) => platform.remove(database),\n };\n return sdk;\n}\n/* eslint-enable @typescript-eslint/no-explicit-any */\n","import { existsSync, mkdirSync, readFileSync, rmSync, statSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { join } from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport { Worker } from \"node:worker_threads\";\nimport { fedimintSdkOn, type FedimintSdk } from \"@ghostly/browser/engine/paymentAdapters/fedimintSdk\";\n\n/**\n * The Fedimint client on Node (WISP 1100 § Wallet SDKs on Node): the app's own SDK and WebAssembly, each database in\n * a `worker_threads` worker (fedimintWorker.mjs) and a file of the profile's `fedimint/` folder, where the browser has\n * a module worker and the origin-private file system. Only the platform differs: the client and what the engine does\n * with it are the app's.\n */\n\nconst WASM_PACKAGE = \"@fedimint/fedimint-client-wasm-bundler\";\nconst DATABASE = /^ghostly-fedimint-[\\w-]{1,80}\\.db$/;\n\n/** The worker's file: beside this chunk wherever the bundler put it (the entry, or assets/), else the workspace's. */\nfunction workerFile(): URL {\n for (const place of [\"./fedimintWorker.mjs\", \"./assets/fedimintWorker.mjs\"]) {\n const url = new URL(place, import.meta.url);\n if (existsSync(fileURLToPath(url))) return url;\n }\n throw new Error(\"The Fedimint worker is missing from this build\");\n}\n\nlet compiling: Promise<WebAssembly.Module> | undefined;\n/** Compiled once per process (11 MB): each worker is handed the module, not the bytes. */\nfunction compiled(): Promise<WebAssembly.Module> {\n return compiling ??= WebAssembly.compile(readFileSync(createRequire(import.meta.url).resolve(`${WASM_PACKAGE}/fedimint_client_wasm_bg.wasm`)))\n .catch((error) => { compiling = undefined; throw error; });\n}\n\nfunction databasePath(dir: string, database: string): string {\n if (!DATABASE.test(database)) throw new Error(\"Invalid Fedimint database name\");\n return join(dir, database);\n}\n\n/** The SDK on this profile's folder `dir`. Loaded on first use, as in the app: a bot that joins no federation compiles nothing. */\nexport function nodeFedimintSdk(dir: string): () => Promise<FedimintSdk> {\n let loading: Promise<FedimintSdk> | undefined;\n return () => loading ??= (async () => {\n const [{ Transport }, module] = await Promise.all([import(\"@fedimint/types\"), compiled()]);\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n const glue = pathToFileURL(createRequire(import.meta.url).resolve(`${WASM_PACKAGE}/fedimint_client_wasm_bg.js`)).href;\n const worker = workerFile();\n type Message = { type: string; payload?: unknown; requestId?: number };\n class ThreadTransport extends Transport {\n readonly logger = { debug() {}, info() {}, warn() {}, error() {} };\n private readonly thread = new Worker(worker, { workerData: { dir, glue } });\n /** Why the thread ended, once it has: later requests fail with it rather than wait on nothing. */\n private ended: string | undefined;\n constructor() {\n super();\n this.thread.on(\"message\", (message) => this.messageHandler(message));\n this.thread.on(\"error\", (error: unknown) => this.end(`The Fedimint client stopped: ${(error instanceof Error && error.message) || \"unknown error\"}`));\n this.thread.on(\"exit\", () => this.end(\"The Fedimint client stopped\"));\n }\n private end(error: string) {\n if (this.ended) return;\n this.ended = error;\n this.messageHandler({ type: \"error\", error });\n }\n postMessage(message: Message) {\n const ended = this.ended;\n if (ended) { queueMicrotask(() => this.messageHandler({ type: \"error\", error: ended, request_id: message.requestId })); return; }\n this.thread.postMessage(message.type === \"init\" ? { ...message, payload: { ...(message.payload as object), module } } : message);\n }\n terminate() { this.ended ??= \"The Fedimint client was closed\"; void this.thread.terminate(); }\n }\n return fedimintSdkOn({\n transport: async () => new ThreadTransport(),\n async exists(database) {\n try { return statSync(databasePath(dir, database)).size > 0; } catch { return false; }\n },\n async remove(database) { rmSync(databasePath(dir, database), { force: true }); },\n });\n })().catch((error) => { loading = undefined; throw error; });\n}\n","import { readFile } from \"node:fs/promises\";\nimport { fileURLToPath } from \"node:url\";\n\n/**\n * `fetch` for `file:` URLs, which Node's does not do. The wallet SDKs' WebAssembly (Bark, BDK, Breez, Iroh) is loaded\n * the way a page loads it, `fetch(new URL(\"…wasm\", import.meta.url))`; on Node that URL is a file beside the bundle.\n * Only `.wasm` files are answered, and nothing else of fetch changes.\n */\nexport function installFileFetch(): void {\n const original = globalThis.fetch;\n const patched: typeof fetch = async (input, init) => {\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n if (url.startsWith(\"file:\")) {\n const parsed = new URL(url);\n if (!parsed.pathname.endsWith(\".wasm\") || parsed.search || parsed.hash) throw new TypeError(`Refusing to fetch ${url}`);\n const bytes = await readFile(fileURLToPath(parsed));\n return new Response(bytes, { headers: { \"content-type\": \"application/wasm\" } });\n }\n return original(input, init);\n };\n globalThis.fetch = patched;\n}\n","import { lookup as dnsLookup } from \"node:dns\";\nimport { request } from \"node:https\";\nimport { isIP, type LookupFunction } from \"node:net\";\nimport { checkPushEndpoint, isPublicAddress, type PushRequest } from \"@ghostly/core\";\n\n/** An address as bytes (4 or 16), or null when it is not one. */\nexport function addressBytes(address: string): Uint8Array | null {\n const family = isIP(address);\n if (family === 4) return Uint8Array.from(address.split(\".\").map(Number));\n if (family !== 6) return null;\n let text = address.replace(/%.*$/, \"\");\n // An IPv4 tail (::ffff:1.2.3.4) as two groups.\n const v4 = /(\\d+\\.\\d+\\.\\d+\\.\\d+)$/.exec(text);\n if (v4) {\n const [a, b, c, d] = v4[1]!.split(\".\").map(Number);\n text = text.slice(0, -v4[1]!.length) + ((a! << 8) | b!).toString(16) + \":\" + ((c! << 8) | d!).toString(16);\n }\n const [head, tail] = text.includes(\"::\") ? text.split(\"::\") as [string, string] : [text, null];\n const left = head ? head.split(\":\") : [];\n const right = tail ? tail.split(\":\") : [];\n const groups = tail === null ? left : [...left, ...Array(8 - left.length - right.length).fill(\"0\"), ...right];\n if (groups.length !== 8) return null;\n const bytes = new Uint8Array(16);\n groups.forEach((group, i) => { const n = parseInt(group, 16); bytes[i * 2] = n >> 8; bytes[i * 2 + 1] = n & 0xff; });\n return bytes;\n}\n\n/** True when `address` is on the public Internet (see `isPublicAddress`). */\nexport function isPublicIp(address: string): boolean {\n const bytes = addressBytes(address);\n return bytes !== null && isPublicAddress(bytes);\n}\n\ntype Resolver = (hostname: string, options: { all: true }, callback: (error: NodeJS.ErrnoException | null, found: { address: string; family: number }[]) => void) => void;\n\n/** `lookup` over `resolve` (`dns.lookup`), refusing a name any of whose addresses is not public. */\nexport const publicOnly = (resolve: Resolver): LookupFunction => (hostname, options, callback) => {\n resolve(hostname, { ...options, all: true }, (error, found) => {\n if (error) return callback(error, \"\", 4);\n const addresses = found as unknown as { address: string; family: number }[];\n if (!addresses.length || addresses.some((a) => !isPublicIp(a.address))) {\n return callback(Object.assign(new Error(`${hostname} is not a public address`), { code: \"ENOTFOUND\" }), \"\", 4);\n }\n if ((options as { all?: boolean }).all) return (callback as unknown as (e: null, a: typeof addresses) => void)(null, addresses);\n callback(null, addresses[0]!.address, addresses[0]!.family);\n });\n};\n\nexport const publicOnlyLookup: LookupFunction = publicOnly(dnsLookup as unknown as Resolver);\n\n/**\n * Posts a wake-up (WISP 401 § Wake-up push) the way the Desktop does from Rust: only to a push service's endpoint\n * (`checkPushEndpoint`), whose name must resolve to public addresses only, no redirect followed, no proxy, 10 s at most.\n * Answers the push service's status.\n */\nexport async function nodePushSend(push: PushRequest, lookup: LookupFunction = publicOnlyLookup): Promise<number> {\n const url = checkPushEndpoint(push.url);\n return new Promise((resolve, reject) => {\n const req = request(url, { method: \"POST\", headers: { ...push.headers, \"Content-Length\": String(push.body.length) }, lookup, agent: false, timeout: 10_000 }, (res) => {\n res.resume();\n resolve(res.statusCode ?? 0);\n });\n req.on(\"timeout\", () => req.destroy(new Error(\"The push service did not answer\")));\n req.on(\"error\", reject);\n req.end(push.body);\n });\n}\n","import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, writeSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { deserialize, serialize } from \"node:v8\";\nimport { IDBFactory, IDBKeyRange, IDBTransaction } from \"fake-indexeddb\";\n\n/**\n * IndexedDB for the engine on Node, kept on disk.\n *\n * The engine (and the wallet SDKs under it) speak IndexedDB. fake-indexeddb gives the exact semantics in memory;\n * this module makes it durable with a snapshot and an append-only journal in the profile's folder:\n *\n * - every committed read-write transaction appends its puts, deletes and clears to `journal.bin` and is fsynced\n * before the transaction's `complete` event reaches the engine (a Cashu proof saved is a proof on disk); one opened\n * with `durability: \"relaxed\"` is appended at once and synced with the next one, or within a second;\n * - a schema change (an upgrade, a deleted database) writes a whole new `snapshot.bin` instead;\n * - at start the snapshot is loaded, the journal replayed, and both folded into a fresh snapshot (with no journal, the\n * snapshot stays as it is).\n *\n * Only one process may hold a profile's store: the caller takes the profile lock first (see `profiles.ts`).\n * fake-indexeddb is pinned: this reaches into its raw classes, and `storage.test.ts` checks them.\n */\n\ntype Key = unknown;\ntype Op =\n | { db: string; store: string; kind: \"put\"; key: Key; value: unknown }\n | { db: string; store: string; kind: \"delete\"; key: Key }\n | { db: string; store: string; kind: \"clear\" };\n\ninterface RawRecord { key: Key; value: unknown }\ninterface RawIndex {\n name: string; keyPath: string | string[]; multiEntry: boolean; unique: boolean; initialized: boolean; deleted: boolean;\n records: RawRecordStore; storeRecord(record: RawRecord): void;\n}\ninterface RawNode { record: RawRecord; left: RawNode | undefined; right: RawNode | undefined; parent: RawNode | undefined; deleted: boolean; red: boolean }\ninterface RawRecordStore {\n values(): Iterable<RawRecord>; clear(): unknown;\n get(key: Key): RawRecord | undefined;\n put(record: RawRecord, noOverwrite?: boolean): RawRecord | undefined;\n delete(key: Key): RawRecord[];\n deleteByValue(key: Key): RawRecord[];\n records: { _root: RawNode | undefined; _numNodes: number; _numTombstones: number; delete(record: RawRecord): void };\n keysAreUnique: boolean;\n}\ninterface RawStore {\n name: string; keyPath: string | string[] | null; autoIncrement: boolean; deleted: boolean;\n keyGenerator: { num: number } | null; rawIndexes: Map<string, RawIndex>; records: RawRecordStore; rawDatabase: RawDatabase;\n storeRecord(record: { key: Key; value: unknown }, noOverwrite: boolean, rollbackLog?: unknown[]): Key;\n deleteRecord(key: Key, rollbackLog?: unknown[]): void;\n clear(rollbackLog?: unknown[]): void;\n}\ninterface RawDatabase { name: string; version: number; rawObjectStores: Map<string, RawStore>; transactions: unknown[] }\ntype RawTransaction = IDBTransaction & { durability?: string; _rollbackLog: unknown[]; _state: string; db: { _rawDatabase: RawDatabase } };\ninterface RawClasses {\n Database: new (name: string, version: number) => RawDatabase;\n ObjectStore: new (db: RawDatabase, name: string, keyPath: string | string[] | null, autoIncrement: boolean) => RawStore;\n Index: new (store: RawStore, name: string, keyPath: string | string[], multiEntry: boolean, unique: boolean) => RawIndex;\n RecordStore: new (keysAreUnique: boolean) => RawRecordStore;\n}\n\ninterface SnapshotIndex { name: string; keyPath: string | string[]; multiEntry: boolean; unique: boolean }\ninterface SnapshotStore { name: string; keyPath: string | string[] | null; autoIncrement: boolean; keyGenerator: number | null; indexes: SnapshotIndex[]; records: [Key, unknown][] }\ninterface Snapshot { format: 1; databases: { name: string; version: number; stores: SnapshotStore[] }[] }\n\nconst SNAPSHOT = \"snapshot.bin\";\nconst JOURNAL = \"journal.bin\";\n/** A journal past this is folded into the snapshot at the next quiet moment. */\nconst COMPACT_BYTES = 32 * 1024 * 1024;\n/** How long `close` waits for the transactions still running: one left open for good does not keep the process. */\nconst CLOSE_WAIT_MS = 5_000;\n/** How long a relaxed transaction's entry may wait for a sync when no other transaction brings one. */\nconst RELAXED_SYNC_MS = 1_000;\nconst BLOB = \"__ghostlyBlob\";\nconst RANGE = \"__ghostlyRange\";\nconst PROBE = \"__ghostly_storage_probe__\";\n\nexport interface PersistentIndexedDb {\n readonly factory: IDBFactory;\n /** Waits for writes that carried a Blob (read asynchronously) to reach the disk. */\n flush(): Promise<void>;\n /** Folds the journal into a fresh snapshot. */\n compact(): Promise<void>;\n /** Flushes, compacts, and stops writing: the process is about to leave. */\n close(): Promise<void>;\n /** Bytes of the journal now, for tests and `ghostly status`. */\n journalBytes(): number;\n}\n\n/**\n * Opens the store in `dir` (created 0700; files 0600) and installs it as this process's `indexedDB` and\n * `IDBKeyRange`. Call once, before anything touches IndexedDB.\n */\nexport async function openPersistentIndexedDb(dir: string): Promise<PersistentIndexedDb> {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n const factory = new IDBFactory();\n const raw = factory as unknown as { _databases: Map<string, RawDatabase> };\n const classes = await probeClasses(factory);\n dropIndexEntriesByRecord(classes, (a, b) => factory.cmp(a, b));\n\n let writing = true;\n let restoring = true;\n let journalFd: number | null = null;\n let queue: Promise<void> = Promise.resolve();\n let queued = 0;\n let schemaDirty = false;\n /** Set while the journal holds relaxed entries no sync has covered yet. */\n let relaxed: ReturnType<typeof setTimeout> | null = null;\n const pending = new WeakMap<unknown[], Op[]>();\n\n const snapshotPath = join(dir, SNAPSHOT);\n const journalPath = join(dir, JOURNAL);\n\n // Load what the disk holds before any connection opens; a snapshot with no journal after it is already what is held.\n const snapshotFound = existsSync(snapshotPath), journalFound = existsSync(journalPath);\n if (snapshotFound) restore(classes, raw._databases, deserialize(readFileSync(snapshotPath)) as Snapshot, keyOrder((a, b) => factory.cmp(a, b)));\n if (journalFound) replay(raw._databases, readFileSync(journalPath), keyOrder((a, b) => factory.cmp(a, b)));\n restoring = false;\n if (journalFound || !snapshotFound) await writeSnapshot();\n\n // Record what each read-write transaction does, keyed by its rollback log (one array per transaction).\n const storeProto = classes.ObjectStore.prototype as RawStore;\n const storeRecord = storeProto.storeRecord;\n const deleteRecord = storeProto.deleteRecord;\n const clear = storeProto.clear;\n const opsOf = (log: unknown[]) => {\n let ops = pending.get(log);\n if (!ops) pending.set(log, (ops = []));\n return ops;\n };\n storeProto.storeRecord = function (this: RawStore, record, noOverwrite, rollbackLog) {\n const key = storeRecord.call(this, record, noOverwrite, rollbackLog);\n if (rollbackLog && !restoring) opsOf(rollbackLog).push({ db: this.rawDatabase.name, store: this.name, kind: \"put\", key, value: record.value });\n return key;\n };\n storeProto.deleteRecord = function (this: RawStore, key, rollbackLog) {\n deleteRecord.call(this, key, rollbackLog);\n if (rollbackLog && !restoring) opsOf(rollbackLog).push({ db: this.rawDatabase.name, store: this.name, kind: \"delete\", key: keyToDisk(key) });\n };\n storeProto.clear = function (this: RawStore, rollbackLog) {\n clear.call(this, rollbackLog);\n if (rollbackLog && !restoring) opsOf(rollbackLog).push({ db: this.rawDatabase.name, store: this.name, kind: \"clear\" });\n };\n\n // The transaction's `complete` is where it becomes durable: written before the engine hears of it.\n const txProto = IDBTransaction.prototype as unknown as { dispatchEvent(event: Event): boolean };\n const dispatch = txProto.dispatchEvent;\n txProto.dispatchEvent = function (this: RawTransaction, event: Event) {\n if (event.type === \"complete\" && writing) {\n if (this.mode === \"versionchange\") schemaDirty = true;\n else {\n const ops = pending.get(this._rollbackLog);\n if (ops?.length) append(ops, this.durability === \"relaxed\");\n }\n pending.delete(this._rollbackLog);\n if (schemaDirty) void enqueue(writeSnapshot);\n } else if (event.type === \"abort\") pending.delete(this._rollbackLog);\n const dispatched = dispatch.call(this, event);\n // fake-indexeddb keeps every transaction a database ever ran in its list (its scheduler only skips the finished\n // ones), each with its requests, results and rollback log: a daemon's heap grew by about 100 KB a message, without\n // end. Nothing looks for a finished one there again, so it goes once its last event is out.\n if ((event.type === \"complete\" || event.type === \"abort\") && this._state === \"finished\") {\n const list = this.db._rawDatabase.transactions;\n const at = list.indexOf(this);\n if (at >= 0) list.splice(at, 1);\n }\n return dispatched;\n };\n const deleteDatabase = factory.deleteDatabase.bind(factory);\n factory.deleteDatabase = (name: string) => {\n const request = deleteDatabase(name);\n request.addEventListener(\"success\", () => { schemaDirty = true; void enqueue(writeSnapshot); });\n return request;\n };\n\n function append(ops: Op[], lazy: boolean) {\n if (queued === 0 && !ops.some((op) => op.kind === \"put\" && hasBlob(op.value))) {\n writeEntry(serialize(ops), lazy);\n return;\n }\n void enqueue(async () => writeEntry(serialize(await Promise.all(ops.map(async (op) => (op.kind === \"put\" ? { ...op, value: await blobsToDisk(op.value) } : op)))), lazy));\n }\n\n function enqueue(work: () => Promise<void> | void): Promise<void> {\n queued++;\n queue = queue.then(work).catch(failed).finally(() => { queued--; });\n return queue;\n }\n\n function failed(error: unknown) {\n process.stderr.write(`ghostly: storage write failed: ${error instanceof Error ? error.message : String(error)}\\n`);\n }\n\n /**\n * `lazy`: a relaxed transaction's entry. A sync blocks the whole process for milliseconds, and what such a\n * transaction wrote can be made again (the event set's note of a message it already reported): written, it outlives\n * the process; a power cut may take its last second.\n */\n function writeEntry(entry: Buffer, lazy = false) {\n if (!writing) return;\n journalFd ??= openSync(journalPath, \"a\", 0o600);\n const header = Buffer.alloc(4);\n header.writeUInt32BE(entry.length);\n writeSync(journalFd, Buffer.concat([header, entry]));\n if (lazy) relaxed ??= setTimeout(() => { try { sync(); } catch (error) { failed(error); } }, RELAXED_SYNC_MS).unref();\n else sync();\n if (statSync(journalPath).size > COMPACT_BYTES) void enqueue(writeSnapshot);\n }\n\n function sync() {\n if (relaxed) { clearTimeout(relaxed); relaxed = null; }\n if (journalFd !== null) fsyncSync(journalFd);\n }\n\n async function writeSnapshot() {\n schemaDirty = false;\n const snapshot: Snapshot = { format: 1, databases: [] };\n for (const db of raw._databases.values()) {\n if (db.name === PROBE) continue;\n const stores: SnapshotStore[] = [];\n for (const store of db.rawObjectStores.values()) {\n if (store.deleted) continue;\n const records: [Key, unknown][] = [];\n for (const record of store.records.values()) records.push([record.key, await blobsToDisk(record.value)]);\n stores.push({\n name: store.name, keyPath: store.keyPath, autoIncrement: store.autoIncrement, keyGenerator: store.keyGenerator?.num ?? null,\n indexes: [...store.rawIndexes.values()].filter((index) => !index.deleted).map(({ name, keyPath, multiEntry, unique }) => ({ name, keyPath, multiEntry, unique })),\n records,\n });\n }\n snapshot.databases.push({ name: db.name, version: db.version, stores });\n }\n const temporary = snapshotPath + \".tmp\";\n const fd = openSync(temporary, \"w\", 0o600);\n try { writeSync(fd, serialize(snapshot)); fsyncSync(fd); } finally { closeSync(fd); }\n renameSync(temporary, snapshotPath);\n // The snapshot holds what the journal's entries said, the relaxed ones too.\n if (relaxed) { clearTimeout(relaxed); relaxed = null; }\n if (journalFd !== null) { closeSync(journalFd); journalFd = null; }\n rmSync(journalPath, { force: true });\n }\n\n /**\n * Resolves once no transaction is left to run. The engine does not wait for every write (`void db.deleteLink(…)`),\n * and fake-indexeddb runs a transaction on a later turn of the event loop: a command that closed in the turn it wrote\n * in (no chat to say goodbye to) took its last snapshot before the write, and answered done for what was not on disk.\n * Two quiet turns in a row: a write that begins when another ends (a chat's messages after its row) is seen too.\n */\n async function settled() {\n const until = Date.now() + CLOSE_WAIT_MS;\n for (let quiet = 0; quiet < 2 && Date.now() < until; ) {\n await new Promise((resolve) => setImmediate(resolve));\n const busy = [...raw._databases.values()].some((db) => db.transactions.some((transaction) => (transaction as RawTransaction)._state !== \"finished\"));\n quiet = busy ? 0 : quiet + 1;\n }\n }\n\n const installed = globalThis as unknown as Record<string, unknown>;\n installed.indexedDB = factory;\n installed.IDBKeyRange = IDBKeyRange;\n\n return {\n factory,\n flush: () => queue,\n compact: () => enqueue(writeSnapshot),\n async close() {\n await settled();\n await enqueue(writeSnapshot);\n writing = false;\n if (journalFd !== null) { closeSync(journalFd); journalFd = null; }\n },\n journalBytes: () => (existsSync(journalPath) ? statSync(journalPath).size : 0),\n };\n}\n\n/** fake-indexeddb's raw classes, from a throwaway database (they are not exported). */\nasync function probeClasses(factory: IDBFactory): Promise<RawClasses> {\n const db = await new Promise<IDBDatabase>((resolve, reject) => {\n const request = factory.open(PROBE, 1);\n request.onupgradeneeded = () => request.result.createObjectStore(\"s\", { autoIncrement: true }).createIndex(\"i\", \"x\");\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n const rawDb = (db as unknown as { _rawDatabase: RawDatabase })._rawDatabase;\n const rawStore = rawDb.rawObjectStores.get(\"s\")!;\n const classes: RawClasses = {\n Database: rawDb.constructor as RawClasses[\"Database\"],\n ObjectStore: rawStore.constructor as RawClasses[\"ObjectStore\"],\n Index: rawStore.rawIndexes.get(\"i\")!.constructor as RawClasses[\"Index\"],\n RecordStore: rawStore.records.constructor as RawClasses[\"RecordStore\"],\n };\n db.close();\n await new Promise<void>((resolve) => { const request = factory.deleteDatabase(PROBE); request.onsuccess = request.onerror = () => resolve(); });\n return classes;\n}\n\n/** The classes whose index upkeep is already by record: they are fake-indexeddb's own, shared by every store opened. */\nconst byRecord = new WeakSet<object>();\n\n/**\n * fake-indexeddb drops an overwritten or deleted record from an index by walking every entry of it (an index is in\n * its own key's order, not the record's): each delivery mark, edit or reaction cost time with every message of the\n * profile, 170 ms at 10,000, and a long chat slowed every other one. The store has the records it just replaced or\n * removed in hand, and they say which entries to drop; a call that comes any other way still walks.\n */\nfunction dropIndexEntriesByRecord(classes: RawClasses, cmp: (a: Key, b: Key) => number) {\n const storeProto = classes.ObjectStore.prototype as RawStore;\n const recordsProto = classes.RecordStore.prototype as RawRecordStore;\n if (byRecord.has(storeProto)) return;\n byRecord.add(storeProto);\n const { storeRecord, deleteRecord } = storeProto;\n const { put, delete: remove, deleteByValue } = recordsProto;\n let gone: { indexes: Map<string, RawIndex>; records: RawRecord[] } | null = null;\n\n storeProto.storeRecord = function (this: RawStore, record, noOverwrite, rollbackLog) {\n if (!this.rawIndexes.size) return storeRecord.call(this, record, noOverwrite, rollbackLog);\n const records: Partial<RawRecordStore> = this.records, indexes = this.rawIndexes, outer = gone;\n records.put = (next, keep) => {\n const replaced = put.call(this.records, next, keep);\n gone = { indexes, records: replaced ? [replaced] : [] };\n return replaced;\n };\n try { return storeRecord.call(this, record, noOverwrite, rollbackLog); } finally { delete records.put; gone = outer; }\n };\n storeProto.deleteRecord = function (this: RawStore, key, rollbackLog) {\n if (!this.rawIndexes.size) return deleteRecord.call(this, key, rollbackLog);\n const records: Partial<RawRecordStore> = this.records, indexes = this.rawIndexes, outer = gone;\n records.delete = (range) => {\n const removed = remove.call(this.records, range);\n gone = { indexes, records: removed };\n return removed;\n };\n try { deleteRecord.call(this, key, rollbackLog); } finally { delete records.delete; gone = outer; }\n };\n recordsProto.deleteByValue = function (this: RawRecordStore, key) {\n let index: RawIndex | undefined;\n if (gone) for (const candidate of gone.indexes.values()) if (candidate.records === this) index = candidate;\n if (!gone || !index) return deleteByValue.call(this, key);\n return dropEntries(index, gone.records, cmp);\n };\n}\n\n/** Drops from an index the entries of records that left its store, and gives back the ones it dropped. */\nfunction dropEntries(index: RawIndex, records: RawRecord[], cmp: (a: Key, b: Key) => number): RawRecord[] {\n const dropped: RawRecord[] = [];\n for (const entry of indexEntries(index, records, cmp)) {\n // A unique index finds an entry by its key alone: it may be another record's (a put the index refused).\n if (index.records.keysAreUnique) {\n const held = index.records.get(entry.key);\n if (!held || cmp(held.value, entry.value) !== 0) continue;\n }\n index.records.records.delete(entry);\n dropped.push(entry);\n }\n return dropped;\n}\n\n/**\n * `cmp` for keys that are stored already (a record's, an index entry's): numbers, strings and arrays of them are\n * ordered here, as IndexedDB orders them (a number before a string before an array; strings by code unit; arrays item\n * by item, then the shorter first). Anything else (a date, bytes, a value that is no key) is left to `cmp`, which\n * checks and copies both keys at every call: on the way back from a snapshot that was a third of a large store's time.\n */\nexport function keyOrder(cmp: (a: Key, b: Key) => number): (a: Key, b: Key) => number {\n const order = (a: Key, b: Key): number => {\n const first = typeof a, second = typeof b;\n if (first === \"number\" && second === \"number\") return a === b ? 0 : (a as number) > (b as number) ? 1 : (a as number) < (b as number) ? -1 : cmp(a, b);\n if (first === \"string\" && second === \"string\") return a === b ? 0 : (a as string) > (b as string) ? 1 : -1;\n if (first === \"number\" && second === \"string\") return -1;\n if (first === \"string\" && second === \"number\") return 1;\n if (Array.isArray(a)) {\n if (second === \"number\" || second === \"string\") return 1;\n if (!Array.isArray(b)) return cmp(a, b);\n const length = Math.min(a.length, b.length);\n for (let at = 0; at < length; at++) {\n const item = order(a[at], b[at]);\n if (item !== 0) return item;\n }\n return a.length === b.length ? 0 : a.length > b.length ? 1 : -1;\n }\n if (Array.isArray(b) && (first === \"number\" || first === \"string\")) return -1;\n return cmp(a, b);\n };\n return order;\n}\n\n/**\n * The snapshot's records come in key order (as `writeSnapshot` walks them), so each store's tree and each index's are\n * built whole from sorted arrays: storing record by record compared every key down the tree, a 70,000-message profile\n * took seconds. A snapshot not in order (never written so) is stored record by record.\n */\nfunction restore(classes: RawClasses, databases: Map<string, RawDatabase>, snapshot: Snapshot, cmp: (a: Key, b: Key) => number) {\n if (snapshot?.format !== 1) throw new Error(\"This profile was last used by a newer version of ghostly (its store has a format this version does not read). Update ghostly to open it.\");\n for (const saved of snapshot.databases) {\n const db = new classes.Database(saved.name, saved.version);\n for (const s of saved.stores) {\n const store = new classes.ObjectStore(db, s.name, s.keyPath, s.autoIncrement);\n if (store.keyGenerator && s.keyGenerator !== null) store.keyGenerator.num = s.keyGenerator;\n for (const i of s.indexes) {\n const index = new classes.Index(store, i.name, i.keyPath, i.multiEntry, i.unique);\n index.initialized = true;\n store.rawIndexes.set(i.name, index);\n }\n db.rawObjectStores.set(s.name, store);\n const records = s.records.map(([key, value]): RawRecord => ({ key, value: blobsFromDisk(value) }));\n if (records.some((record, at) => at > 0 && cmp(records[at - 1].key, record.key) >= 0)) {\n for (const record of records) store.storeRecord(record, false);\n continue;\n }\n fill(store.records, records);\n for (const index of store.rawIndexes.values()) fill(index.records, indexEntries(index, records, cmp));\n }\n databases.set(saved.name, db);\n }\n}\n\n/**\n * An index's entries for records in key order, sorted by the index's key: the index derives them itself (key path,\n * multi-entry, invalid keys skipped) into a list instead of its tree. A stable sort keeps equal index keys in the\n * records' key order, the tree's own tie-break.\n */\nfunction indexEntries(index: RawIndex, records: RawRecord[], cmp: (a: Key, b: Key) => number): RawRecord[] {\n const entries: RawRecord[] = [];\n const tree = index.records;\n index.records = { get: () => undefined, put: (entry: RawRecord) => { entries.push(entry); } } as unknown as RawRecordStore;\n try {\n for (const record of records) if (!absent(index.keyPath, record.value)) index.storeRecord(record);\n } finally { index.records = tree; }\n return entries.sort((a, b) => cmp(a.key, b.key));\n}\n\n/**\n * Whether a key path surely finds nothing in a value: a plain object along it lacks the step (most messages have no\n * `card` for the card index). The index would throw and catch a DataError for each of those; anything else it judges.\n */\nfunction absent(keyPath: string | string[], value: unknown): boolean {\n if (typeof keyPath !== \"string\" || keyPath === \"\") return false;\n let object = value;\n for (const step of keyPath.split(\".\")) {\n if (object === null || typeof object !== \"object\" || Array.isArray(object) || object instanceof Blob) return false;\n if (!Object.hasOwn(object, step)) return true;\n object = (object as Record<string, unknown>)[step];\n }\n return false;\n}\n\n/**\n * fake-indexeddb's red-black tree, built balanced from sorted records: every level full but the last, whose nodes\n * are red, so each path holds the same number of black nodes and later inserts rebalance as usual.\n */\nfunction fill(store: RawRecordStore, sorted: RawRecord[]) {\n const deepest = sorted.length ? Math.floor(Math.log2(sorted.length)) : 0;\n const build = (low: number, high: number, parent: RawNode | undefined, depth: number): RawNode | undefined => {\n if (low > high) return undefined;\n const middle = (low + high) >>> 1;\n const node: RawNode = { record: sorted[middle], left: undefined, right: undefined, parent, deleted: false, red: depth > 0 && depth === deepest };\n node.left = build(low, middle - 1, node, depth + 1);\n node.right = build(middle + 1, high, node, depth + 1);\n return node;\n };\n store.records._root = build(0, sorted.length - 1, undefined, 0);\n store.records._numNodes = sorted.length;\n store.records._numTombstones = 0;\n}\n\n/**\n * What a journal's replay changed in a store: its indexes, set aside, the records that left it (the ones the indexes\n * know; `null` once it was cleared: all of them) and the records that came and stayed.\n */\ninterface Touched { indexes: Map<string, RawIndex>; was: Set<RawRecord> | null; now: Set<RawRecord> }\n\n/**\n * Replays whole entries; a torn last entry (the process died mid-write) was never acknowledged and is dropped.\n *\n * A store's indexes are set aside while its ops replay and brought in step once at the end: fake-indexeddb drops an\n * overwritten or deleted record from an index by walking every record of it, so a crash after a busy chat took\n * tens of seconds to minutes to start, one write at a time against the whole profile. Only the rows the journal\n * touched change in an index (what each was before goes, what it is now comes in): a journal of one message costs one\n * message, not the profile's history once per index. A store the journal cleared has its indexes built again whole.\n */\nfunction replay(databases: Map<string, RawDatabase>, journal: Buffer, cmp: (a: Key, b: Key) => number) {\n const touched = new Map<RawStore, Touched>();\n try { replayEntries(databases, journal, touched); } finally {\n for (const [store, { indexes, was, now }] of touched) {\n const records: Partial<RawRecordStore> = store.records;\n delete records.put;\n delete records.delete;\n store.rawIndexes = indexes;\n for (const index of indexes.values()) {\n if (!was) index.records.clear();\n if (!index.initialized) continue;\n if (was) dropEntries(index, [...was], cmp);\n for (const record of was ? now : store.records.values()) if (!absent(index.keyPath, record.value)) index.storeRecord(record);\n }\n }\n }\n}\n\nfunction replayEntries(databases: Map<string, RawDatabase>, journal: Buffer, touched: Map<RawStore, Touched>) {\n let offset = 0;\n while (offset + 4 <= journal.length) {\n const length = journal.readUInt32BE(offset);\n if (offset + 4 + length > journal.length) break;\n let ops: Op[];\n try { ops = deserialize(journal.subarray(offset + 4, offset + 4 + length)) as Op[]; } catch { break; }\n offset += 4 + length;\n for (const op of ops) {\n const store = databases.get(op.db)?.rawObjectStores.get(op.store);\n if (!store) continue;\n let change = touched.get(store);\n if (!change) touched.set(store, (change = watch(store)));\n if (op.kind === \"put\") store.storeRecord({ key: op.key, value: blobsFromDisk(op.value) }, false);\n else if (op.kind === \"delete\") store.deleteRecord(keyFromDisk(op.key));\n else {\n change.was = null;\n store.clear();\n }\n }\n }\n}\n\n/**\n * Sets a store's indexes aside and notes, as its records are written over and removed, which ones left and which\n * ones came: the record store hands back what a put replaced and what a delete removed, so a row costs no lookup.\n * A record that came and left again was never in an index.\n */\nfunction watch(store: RawStore): Touched {\n const change: Touched = { indexes: store.rawIndexes, was: store.rawIndexes.size ? new Set() : null, now: new Set() };\n store.rawIndexes = new Map();\n if (!change.was) return change;\n const { put, delete: remove } = store.records;\n const left = (record: RawRecord) => { if (!change.now.delete(record)) change.was?.add(record); };\n store.records.put = (next, keep) => {\n const replaced = put.call(store.records, next, keep);\n if (replaced) left(replaced);\n change.now.add(next);\n return replaced;\n };\n store.records.delete = (key) => {\n const removed = remove.call(store.records, key);\n removed.forEach(left);\n return removed;\n };\n return change;\n}\n\nfunction keyToDisk(key: Key): Key {\n if (key instanceof IDBKeyRange) return { [RANGE]: { lower: key.lower, upper: key.upper, lowerOpen: key.lowerOpen, upperOpen: key.upperOpen } };\n return key;\n}\nfunction keyFromDisk(key: Key): Key {\n const range = (key as Record<string, { lower: unknown; upper: unknown; lowerOpen: boolean; upperOpen: boolean }> | null)?.[RANGE];\n if (!range) return key;\n if (range.lower === undefined) return IDBKeyRange.upperBound(range.upper, range.upperOpen);\n if (range.upper === undefined) return IDBKeyRange.lowerBound(range.lower, range.lowerOpen);\n return IDBKeyRange.bound(range.lower, range.upper, range.lowerOpen, range.upperOpen);\n}\n\nfunction hasBlob(value: unknown, depth = 0): boolean {\n if (value instanceof Blob) return true;\n if (!value || typeof value !== \"object\" || depth > 32 || ArrayBuffer.isView(value) || value instanceof ArrayBuffer) return false;\n if (value instanceof Map) { for (const [k, v] of value) if (hasBlob(k, depth + 1) || hasBlob(v, depth + 1)) return true; return false; }\n for (const item of Object.values(value)) if (hasBlob(item, depth + 1)) return true;\n return false;\n}\n\n/** v8 cannot serialize a Blob: it goes to disk as its bytes and type, and comes back a Blob. */\nasync function blobsToDisk(value: unknown, depth = 0): Promise<unknown> {\n if (!hasBlob(value, depth)) return value;\n if (value instanceof Blob) return { [BLOB]: { type: value.type, bytes: new Uint8Array(await value.arrayBuffer()) } };\n if (Array.isArray(value)) return Promise.all(value.map((item) => blobsToDisk(item, depth + 1)));\n if (value instanceof Map) return new Map(await Promise.all([...value].map(async ([k, v]) => [k, await blobsToDisk(v, depth + 1)] as const)));\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as object)) out[k] = await blobsToDisk(v, depth + 1);\n return out;\n}\nfunction blobsFromDisk(value: unknown, depth = 0): unknown {\n if (!value || typeof value !== \"object\" || depth > 32 || ArrayBuffer.isView(value) || value instanceof ArrayBuffer) return value;\n const blob = (value as Record<string, { type: string; bytes: Uint8Array }>)[BLOB];\n if (blob) return new Blob([blob.bytes as BlobPart], { type: blob.type });\n if (Array.isArray(value)) return value.map((item) => blobsFromDisk(item, depth + 1));\n if (value instanceof Map) return new Map([...value].map(([k, v]) => [k, blobsFromDisk(v, depth + 1)]));\n if (Object.getPrototypeOf(value) !== Object.prototype) return value;\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) out[k] = blobsFromDisk(v, depth + 1);\n return out;\n}\n","import { appendFileSync, existsSync, readFileSync, rmSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { fromBase64Url, RTC_CONFIG, setLinkTraceSink } from \"@ghostly/core\";\nimport { irohRelayProblem } from \"@ghostly/browser/platform/irohWeb\";\nimport type { EngineServer } from \"@ghostly/browser/engine/server\";\nimport { loadCallStack } from \"../calls/media\";\nimport { CliError } from \"../errors\";\nimport { nodeLocalFetch } from \"../services\";\nimport { nodeFedimintSdk } from \"./fedimint\";\nimport { installFileFetch } from \"./fileFetch\";\nimport { nodePushSend } from \"./pushSend\";\nimport { openPersistentIndexedDb, type PersistentIndexedDb } from \"./storage\";\nimport { RESTORED_MARK, type ProfilePaths } from \"../profiles\";\n\n/** What runs a profile: its store, the engine and the host around it. */\nexport interface Runtime {\n readonly paths: ProfilePaths;\n /** WebRTC runs here (node-datachannel loaded). */\n readonly webrtc: boolean;\n /** Why voice calls cannot run here, or null when they can (node-datachannel's media and Opus loaded). */\n readonly callsUnavailable: string | null;\n readonly server: EngineServer;\n readonly store: PersistentIndexedDb;\n /** Says goodbye to peers, then folds the store. */\n close(): Promise<void>;\n}\n\n/**\n * The HyperDHT network: `GHOSTLY_HYPERDHT_BOOTSTRAP` (\"host:port,…\") replaces the public bootstrap nodes, as it does\n * for the Desktop's sidecar; a network all on loopback (the tests') is announced on loopback too.\n */\nexport function hyperdhtNetwork(env = process.env.GHOSTLY_HYPERDHT_BOOTSTRAP): { bootstrap?: string[]; host?: string } {\n const bootstrap = (env ?? \"\").split(\",\").map((node) => node.trim()).filter(Boolean);\n if (!bootstrap.length) return {};\n const loopback = bootstrap.every((node) => /^(127\\.0\\.0\\.1|localhost):\\d+$/.test(node));\n return { bootstrap, ...(loopback ? { host: \"127.0.0.1\" } : {}) };\n}\n\n/**\n * The Pkarr relays of a private network or a test, `GHOSTLY_PKARR_RELAYS` (\"http://…,…\"), the Desktop's name for it:\n * the only relays this process uses, whatever the profile's `relays` setting says, from its very first publish. Null\n * when unset.\n */\nexport function pkarrRelays(env = process.env): string[] | null {\n const relays = (env.GHOSTLY_PKARR_RELAYS ?? \"\").split(\",\").map((url) => url.trim()).filter(Boolean);\n if (!relays.length) return null;\n const bad = relays.find((url) => !/^https?:\\/\\/[^/\\s]/i.test(url));\n if (bad) throw new CliError(\"usage\", `GHOSTLY_PKARR_RELAYS takes http:// or https:// URLs, comma-separated, not ${JSON.stringify(bad)}`);\n return relays;\n}\n\n/**\n * The Iroh relays of a private network or a test, `GHOSTLY_IROH_RELAYS` (\"https://…,…\"; http:// on loopback): the only\n * relays this process's Iroh homes on, whatever the profile's `irohRelays` setting says. Null when unset (the setting,\n * else n0's public relays).\n */\nexport function irohRelays(env = process.env): string[] | null {\n const relays = (env.GHOSTLY_IROH_RELAYS ?? \"\").split(\",\").map((url) => url.trim()).filter(Boolean);\n if (!relays.length) return null;\n const bad = relays.map((url) => irohRelayProblem(url)).find(Boolean);\n if (bad) throw new CliError(\"usage\", `GHOSTLY_IROH_RELAYS: ${bad}`);\n return relays;\n}\n\n/**\n * The apps' public STUN servers (Google's, `RTC_CONFIG`), which every WebRTC connection asks for its public address:\n * `GHOSTLY_STUN=0` leaves them out (a private network, or tests on one machine, where host candidates connect). The\n * profile's own `iceServers` stay.\n */\nexport function publicStun(env = process.env): boolean { return env.GHOSTLY_STUN !== \"0\"; }\n\n/** An RTC configuration without the apps' public STUN servers. */\nexport function withoutPublicStun(config: RTCConfiguration | undefined): RTCConfiguration | undefined {\n if (!config?.iceServers) return config;\n const ours = new Set((RTC_CONFIG.iceServers ?? []).flatMap((server) => [server.urls].flat()));\n const iceServers = config.iceServers\n .map((server) => ({ ...server, urls: [server.urls].flat().filter((url) => !ours.has(url)) }))\n .filter((server) => server.urls.length);\n return { ...config, iceServers };\n}\n\n/**\n * The Mainline DHT, for Pkarr beside the relays (`RelaysAndDht`): `GHOSTLY_DHT=0` leaves it out (relays only, as the web\n * app), `GHOSTLY_DHT_BOOTSTRAP` (\"host:port,…\") replaces the public routers (a testnet; one all on loopback binds there).\n * `GHOSTLY_PKARR_RELAYS` without a bootstrap leaves it out too, as on the Desktop: a private network stays off the\n * public DHT.\n */\nexport function mainlineNetwork(env = process.env): { off: true } | { off: false; bootstrap?: string[]; host?: string } {\n if (env.GHOSTLY_DHT === \"0\") return { off: true };\n const bootstrap = (env.GHOSTLY_DHT_BOOTSTRAP ?? \"\").split(\",\").map((node) => node.trim()).filter(Boolean);\n if (!bootstrap.length && pkarrRelays(env)) return { off: true };\n if (!bootstrap.length) return { off: false };\n const loopback = bootstrap.every((node) => /^(127\\.0\\.0\\.1|localhost):\\d+$/.test(node));\n return { off: false, bootstrap, ...(loopback ? { host: \"127.0.0.1\" } : {}) };\n}\n\n/**\n * WebRTC for the engine (WISP 101): libdatachannel through node-datachannel's W3C polyfill. Chats go live over it with\n * browsers directly, and groups need it (their links use WebRTC alone). A native module: without it, or with\n * `GHOSTLY_WEBRTC=0`, the CLI runs without WebRTC and says so.\n */\nasync function installWebRtc(): Promise<boolean> {\n if (process.env.GHOSTLY_WEBRTC === \"0\") return false;\n try {\n const polyfill = await import(\"node-datachannel/polyfill\");\n const scope = globalThis as unknown as Record<string, unknown>;\n for (const name of [\"RTCPeerConnection\", \"RTCSessionDescription\", \"RTCIceCandidate\", \"RTCDataChannel\", \"RTCDataChannelEvent\", \"RTCPeerConnectionIceEvent\", \"RTCCertificate\"] as const) {\n scope[name] ??= (polyfill as unknown as Record<string, unknown>)[name];\n }\n refuseDataAnswers(scope);\n if (!publicStun()) leaveOutPublicStun(scope);\n return true;\n } catch (error) {\n process.stderr.write(`ghostly: WebRTC is unavailable (${error instanceof Error ? error.message : String(error)}); chats use HyperDHT, Iroh or the DHT\\n`);\n return false;\n }\n}\n\n/**\n * For tests: `GHOSTLY_TEST_REFUSE_DATA_ANSWERS=<n>` refuses one in every n answers this process applies to its offers\n * (the first, the n+1st, and so on), as libdatachannel 0.24.5 refuses one in its race (`ANSWER_REFUSED_REDIALS` in\n * packages/core/src/datalink.ts): the connection closes, and setRemoteDescription throws.\n */\nfunction refuseDataAnswers(scope: Record<string, unknown>): void {\n const every = Number(process.env.GHOSTLY_TEST_REFUSE_DATA_ANSWERS) || 0;\n if (every <= 0) return;\n let answers = 0;\n const Base = scope.RTCPeerConnection as typeof RTCPeerConnection;\n scope.RTCPeerConnection = class extends Base {\n override async setRemoteDescription(description: RTCSessionDescriptionInit): Promise<void> {\n if (description.type === \"answer\" && answers++ % every === 0) {\n this.close();\n throw new Error(\"libdatachannel error while adding remote description: Got a remote candidate without ICE transport (GHOSTLY_TEST_REFUSE_DATA_ANSWERS)\");\n }\n return super.setRemoteDescription(description);\n }\n };\n}\n\n/** `GHOSTLY_STUN=0`: every connection the engine makes leaves the apps' public STUN servers out of its configuration. */\nfunction leaveOutPublicStun(scope: Record<string, unknown>): void {\n const Base = scope.RTCPeerConnection as typeof RTCPeerConnection;\n scope.RTCPeerConnection = class extends Base {\n constructor(config?: RTCConfiguration) { super(withoutPublicStun(config)); }\n };\n}\n\n/** Server-sent events, which browsers have and Node does not: Arkade follows its server with them. */\nasync function installEventSource(): Promise<void> {\n const scope = globalThis as unknown as Record<string, unknown>;\n if (scope.EventSource) return;\n scope.EventSource = (await import(\"eventsource\")).EventSource;\n}\n\n/**\n * Arkade's descriptor library is CommonJS and `require`s `@scure/bip32`, which is ESM. When the engine's own imports\n * are still evaluating that module, Node's require(esm) hands the library a namespace without `HDKey` yet (\"reading\n * 'fromMasterSeed'\"). Loading it whole first avoids the race.\n */\nasync function preloadWalletModules(): Promise<void> {\n await import(\"@scure/bip32\").catch(() => {});\n await import(\"@bitcoinerlab/descriptors-scure\").catch(() => {});\n}\n\n/** Iroh's wasm, from next to the bundle (a package) or from the workspace (development and tests). */\nfunction irohWasmBytes(): Buffer {\n // Beside this chunk, wherever the bundler put it (the entry, or assets/); else the workspace's.\n for (const place of [\"./ghostly_iroh_web_bg.wasm\", \"./assets/ghostly_iroh_web_bg.wasm\"]) {\n const path = fileURLToPath(new URL(place, import.meta.url));\n if (existsSync(path)) return readFileSync(path);\n }\n return readFileSync(createRequire(import.meta.url).resolve(\"@ghostly/iroh-web/wasm\"));\n}\n\n/** How a runtime starts: `deferGroups` leaves the groups' sessions unstarted (a one-shot for one chat). */\nexport interface RuntimeOptions { deferGroups?: boolean }\n\n/**\n * Starts the app's engine on this profile (WISP 1100 § Runtime): IndexedDB on disk, WebRTC through libdatachannel,\n * HyperDHT native in this process, Iroh's wasm build (relay only, as the web app), Pkarr through the relays in the\n * settings and the Mainline DHT directly (read when the relays fail, written always).\n * The caller holds the profile's lock.\n */\n/** The files a light backup left out, as the restore wrote them in the mark (an empty mark: none). */\nfunction leftOutIn(mark: string): string[] {\n try {\n const ids = (JSON.parse(readFileSync(mark, \"utf8\") || \"{}\") as { leftOut?: unknown }).leftOut;\n return Array.isArray(ids) ? ids.filter((id): id is string => typeof id === \"string\") : [];\n } catch { return []; }\n}\n\nexport async function startRuntime(paths: ProfilePaths, options: RuntimeOptions = {}): Promise<Runtime> {\n // `GHOSTLY_LINK_TRACE=<file>`: each step of each chat's way to live, one JSON line (packages/core/src/linkTrace.ts),\n // as the Desktop writes to its log. For measuring, not needed to run.\n // Read first: a mistyped one stops here, before anything opens.\n const pinned = pkarrRelays();\n const pinnedIroh = irohRelays();\n const trace = process.env.GHOSTLY_LINK_TRACE;\n if (trace) {\n setLinkTraceSink(line => appendFileSync(trace, line + \"\\n\", { mode: 0o600 }));\n // A join through a group's link, step by step (what the door read, whom it let in), in the same file.\n (await import(\"@ghostly/browser/engine/joinTrace\")).setJoinTraceSink(line => appendFileSync(trace, line + \"\\n\", { mode: 0o600 }));\n }\n const store = await openPersistentIndexedDb(paths.db);\n // A profile a restore made, started for the first time: its store is the backup's, as old as the backup. Its ecash\n // is checked with the mints and its unfinished payment attempts authorize nothing, as in the app's restore. Done\n // before the engine reads any of it; the mark goes only once that is on disk, so a start cut short does it again.\n const restored = join(paths.dir, RESTORED_MARK);\n if (existsSync(restored)) {\n try {\n const rows = await import(\"@ghostly/browser/shared/restoredRows\");\n await rows.markRestoredWallet();\n // The files a light backup left out: their records say so, as in the app's restore.\n await rows.markLeftOutFiles(leftOutIn(restored));\n await store.compact();\n } catch (error) {\n await store.close().catch(() => {});\n throw error;\n }\n rmSync(restored, { force: true });\n }\n const webrtc = await installWebRtc();\n // Voice calls (WISP 1100 § Calls): offered to contacts (calls/1) only where their media can run.\n const stack = webrtc ? await loadCallStack() : \"Calls need WebRTC, which is off on this headless Ghostly\";\n const callsUnavailable = typeof stack === \"string\" ? stack : null;\n installFileFetch();\n // `GHOSTLY_TEST_MINTS=<url,…>`: mints on this machine that are test servers with a fake Lightning backend (a regtest\n // or e2e stack). Only through them does a Testnet wallet pay a Bitcoin (lnbc) invoice, as through the public test mint.\n const testMints = process.env.GHOSTLY_TEST_MINTS;\n if (testMints) (await import(\"@ghostly/browser/shared/mints\")).declareTestMints(testMints.split(\",\"));\n await installEventSource();\n await preloadWalletModules();\n // Loaded after IndexedDB is in place: nothing of the engine may open its database first.\n const iroh = await import(\"@ghostly/iroh-web\");\n iroh.initSync({ module: irohWasmBytes() });\n // Files sent and received are real files in the profile's folder, as on the Desktop.\n const [{ registerFileBytes }, { NodeFileBytes }] = await Promise.all([import(\"@ghostly/browser/shared/fileBytes\"), import(\"./fileBytes\")]);\n registerFileBytes(\"native\", async () => new NodeFileBytes(paths.files), true);\n const [{ EngineServer }, { createHyperEndpoint }] = await Promise.all([\n import(\"@ghostly/browser/engine/server\"),\n import(\"../../../../native/transports/hyperdht/endpoint.mjs\"),\n ]);\n const network = hyperdhtNetwork();\n const dht = mainlineNetwork();\n const mainline = dht.off ? null : new (await import(\"./mainline\")).Mainline({ bootstrap: dht.bootstrap, host: dht.host });\n // `GHOSTLY_PKARR_RELAYS`: those relays only, in place before the engine publishes anything (the setting is not read).\n const transport = (mainline || pinned) ? new (await import(\"./mainline\")).RelaysAndDht(mainline, pinned ? { relays: pinned } : {}, !!pinned) : null;\n const server = new EngineServer({\n ...(transport ? { transport } : {}),\n irohWeb: true,\n // `GHOSTLY_IROH_RELAYS`: those relays only (the setting is not read).\n ...(pinnedIroh ? { irohRelays: pinnedIroh } : {}),\n // A CLI profile is one device's (WISP 06): no device state is kept for it.\n singleDevice: true,\n nativeTransports: { \"hyperdht/1\": (seedB64: string) => createHyperEndpoint(fromBase64Url(seedB64), network) },\n // No wallet starts by itself: a bot has the wallets it made (WISP 1100 § Wallet SDKs on Node).\n automaticWallets: false,\n // Fedimint's client databases are files of the profile, each in a worker thread (./fedimint.ts).\n fedimintSdk: nodeFedimintSdk(join(paths.dir, \"fedimint\")),\n // Local web apps may be shared with a contact, reached on loopback only (src/services.ts).\n servicesSupport: true,\n // No mini-app runs here (WISP 1200 § Per client: CLI), and the bot side of apps/1 is phase 2: no apps/1 is offered\n // and the app calls are refused, whatever the build's APPS_ENABLED says.\n apps: false,\n // A daemon stays online: a hub of the large private groups it is in (WISP 902 · Group Mesh § Hubs), unless GHOSTLY_HUB=0.\n staysOnline: process.env.GHOSTLY_HUB !== \"0\",\n localFetch: nodeLocalFetch,\n // A wake-up goes to a push service only, on public addresses only (./pushSend.ts), as the Desktop's does.\n pushSend: (request) => nodePushSend(request),\n callsSupport: callsUnavailable === null,\n ...(callsUnavailable ? { callsUnavailable } : {}),\n ...(options.deferGroups ? { deferGroups: true } : {}),\n // Tests only: group links as an app from before native ones (test/groupCompat.test.ts).\n ...(process.env.GHOSTLY_TEST_WEBRTC_GROUP_LINKS === \"1\" ? { webrtcGroupLinks: true } : {}),\n });\n try {\n await server.ready;\n } catch (error) {\n await mainline?.destroy().catch(() => {});\n await store.close().catch(() => {});\n throw error;\n }\n let closing: Promise<void> | null = null;\n return {\n paths, server, store, webrtc, callsUnavailable,\n close: () => (closing ??= (async () => {\n await server.node.shutdown().catch(() => {});\n await mainline?.destroy().catch(() => {});\n await store.close();\n })()),\n };\n}\n","import { createServer, type Server, type Socket } from \"node:net\";\nimport { chmodSync, existsSync, rmSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { createInterface } from \"node:readline\";\nimport type { StoredMessage } from \"@ghostly/browser/shared/types\";\nimport { announceJoins } from \"./announce\";\nimport { callApi, type ApiContext } from \"./api\";\nimport { CallManager } from \"./calls/manager\";\nimport { asCliError, CliError } from \"./errors\";\nimport { EventHub, type GhostlyEvent } from \"./events\";\nimport { resumeHolds } from \"./holds\";\nimport { ownFolder, privateFolder } from \"./privateFolder\";\nimport { acquireLock, type ProfilePaths } from \"./profiles\";\nimport { startRuntime, type RuntimeOptions } from \"./runtime/engine\";\nimport { flushCardUpdates } from \"./statusCards\";\n\n/** The longest request line the daemon reads (WISP 1100 § Framing). */\nexport const MAX_LINE = 16 * 1024 * 1024;\n\nexport interface Host {\n ctx: ApiContext;\n close(): Promise<void>;\n}\n\n/**\n * Runs a profile in this process: takes its lock, starts the engine, and derives events from it. A daemon then\n * serves the socket (`serve`); a one-shot command calls the API directly and closes. A one-shot for one chat passes\n * `deferGroups`: the groups' sessions would spend the relays' budget its message needs.\n */\nexport async function openHost(paths: ProfilePaths, mode: ApiContext[\"mode\"], version: string, options: RuntimeOptions = {}): Promise<Host> {\n const release = acquireLock(paths);\n let runtime;\n try {\n runtime = await startRuntime(paths, options);\n } catch (error) {\n release();\n throw error;\n }\n const hub = new EventHub(paths.events);\n let calls!: CallManager;\n try {\n await hub.open();\n const node = runtime.server.node;\n const now = node.getState();\n // The histories read here are the attach's too: one that changes before the hub is attached is read again then.\n const mark = runtime.server.historyMark;\n const histories = new Map<string, readonly StoredMessage[]>();\n for (const link of now.links) histories.set(link.id, await node.getMessages(link.id));\n for (const group of now.groups) histories.set(`group:${group.id}`, await node.groupMessages({ groupId: group.id }));\n hub.baseline(now, histories);\n announceJoins(hub, node);\n // Calls (WISP 1100 § Calls): only a daemon answers and places them; a one-shot still reports one that rings.\n calls = new CallManager({ engine: node, emit: (type, id, fields) => hub.emit(type, id, fields), profileDir: paths.dir, answers: mode === \"daemon\" });\n const manager = calls;\n hub.onCallSignal((chat, signal) => manager.onSignal(chat, signal));\n runtime.server.attach(hub.sink, { mark, ids: histories.keys() });\n } catch (error) {\n await runtime.close().catch(() => {});\n release();\n throw error;\n }\n const ctx: ApiContext = { runtime, hub, mode, version, calls };\n resumeHolds(ctx);\n let closing: Promise<void> | null = null;\n return {\n ctx,\n close: () => (closing ??= (async () => {\n // Every call is hung up, and every card update still waiting for its time sent, while the engine can still tell the contact.\n await Promise.all([calls.stopAll(), flushCardUpdates(ctx)]).catch(() => {});\n runtime.server.detach(hub.sink);\n await runtime.close().catch(() => {});\n release();\n })()),\n };\n}\n\ninterface Request { id?: unknown; method?: unknown; params?: unknown }\n\n/**\n * The local control API over the profile's Unix socket (0600 in the 0700 profile folder): one JSON object per line\n * each way. `events.subscribe` turns a connection into a stream of `{\"event\": …}` lines.\n */\nexport interface Served {\n server: Server;\n /** Stops listening and ends every connection (subscribers too: they reconnect to the next daemon). */\n close(): Promise<void>;\n}\n\nexport async function serve(host: Host): Promise<Served> {\n const { ctx } = host;\n const path = ctx.runtime.paths.socket;\n // The folder is owner-only before the socket is in it: nobody else can reach the socket in the moment before its chmod.\n if (process.platform !== \"win32\") {\n if (dirname(path) !== ctx.runtime.paths.dir) privateFolder(dirname(path), \"the daemon will not put its socket there\");\n else ownFolder(ctx.runtime.paths.dir, \"the daemon will not put its socket there\");\n }\n // A socket file left by a daemon that died: the lock says nobody holds the profile, so it is stale.\n if (existsSync(path)) rmSync(path, { force: true });\n const sockets = new Set<Socket>();\n const server = createServer((socket) => {\n sockets.add(socket);\n socket.on(\"close\", () => sockets.delete(socket));\n connection(ctx, socket);\n });\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n // The socket file is made owner-only (the bind happens within listen), then chmod'ed below as well.\n const umask = ownerOnlyUmask();\n try { server.listen(path, () => { server.off(\"error\", reject); resolve(); }); } finally { umask?.(); }\n });\n if (process.platform !== \"win32\") chmodSync(path, 0o600);\n let closing: Promise<void> | null = null;\n return {\n server,\n close: () => (closing ??= new Promise<void>((resolve) => {\n server.close(() => resolve());\n for (const socket of sockets) socket.end();\n setTimeout(() => { for (const socket of sockets) socket.destroy(); }, 500).unref();\n })),\n };\n}\n\nlet guarded = false;\n\n/**\n * A daemon says an error nobody caught (with its stack, on stderr: the daemon's log when detached) and keeps serving:\n * one request gone wrong must not take the profile offline. One-shot commands do not call this; they still fail loudly.\n *\n * `outputs`: what it writes to. A write there that fails (a pipe whose reader is gone: the process that started the\n * daemon died) is dropped, not an error nobody caught: saying it would be one more failed write, and the next, with\n * no turn left for anything else (a full core, and no stop on SIGTERM).\n */\nexport function keepServing(\n proc: Pick<NodeJS.Process, \"on\"> = process,\n write: (text: string) => void = (text) => { process.stderr.write(text); },\n outputs: Pick<NodeJS.WriteStream, \"on\">[] = proc === process ? [process.stdout, process.stderr] : [],\n): void {\n if (proc === process) { if (guarded) return; guarded = true; }\n const log = (what: string) => (error: unknown) => {\n try { write(`ghostly: ${what}, the daemon keeps serving: ${error instanceof Error ? error.stack ?? error.message : String(error)}\\n`); } catch { /* stderr is gone */ }\n };\n for (const output of outputs) output.on(\"error\", () => { /* nobody reads it any more */ });\n proc.on(\"unhandledRejection\", log(\"unhandled rejection\"));\n proc.on(\"uncaughtException\", log(\"uncaught exception\"));\n}\n\n/** Sets a 077 umask; the function it returns puts the old one back. Null where there is none (Windows, a worker). */\nfunction ownerOnlyUmask(): (() => void) | null {\n if (process.platform === \"win32\") return null;\n try {\n const old = process.umask(0o077);\n return () => { process.umask(old); };\n } catch { return null; }\n}\n\nfunction connection(ctx: ApiContext, socket: Socket): void {\n const unsubscribes: (() => void)[] = [];\n const write = (value: unknown) => { if (!socket.destroyed) socket.write(JSON.stringify(value) + \"\\n\"); };\n socket.on(\"close\", () => { for (const off of unsubscribes) off(); });\n socket.on(\"error\", () => {});\n const lines = createInterface({ input: socket, crlfDelay: Infinity });\n let buffered = 0;\n socket.on(\"data\", (chunk) => { buffered += chunk.length; if (buffered > MAX_LINE) socket.destroy(); });\n lines.on(\"line\", (line) => {\n buffered = 0;\n if (!line.trim()) return;\n let request: Request;\n try { request = JSON.parse(line) as Request; } catch { write({ id: null, error: new CliError(\"bad_request\", \"Not JSON\").toJSON() }); return; }\n const id = typeof request.id === \"number\" || typeof request.id === \"string\" ? request.id : null;\n if (typeof request.method !== \"string\") { write({ id, error: new CliError(\"bad_request\", \"method is required\").toJSON() }); return; }\n if (request.method === \"events.subscribe\") {\n const params = (request.params ?? {}) as { since?: unknown };\n const since = typeof params.since === \"number\" ? params.since : ctx.hub.lastSeq;\n // Live events queue while the journal replays, so none falls between the two.\n const queued: GhostlyEvent[] = [];\n let replaying = true;\n unsubscribes.push(ctx.hub.onEvent((event) => { if (replaying) queued.push(event); else write({ event }); }));\n write({ id, result: { subscribed: true, lastSeq: ctx.hub.lastSeq } });\n let last = since;\n for (const event of ctx.hub.replay(since)) { write({ event }); last = event.seq; }\n replaying = false;\n for (const event of queued) if (event.seq > last) write({ event });\n return;\n }\n void callApi(ctx, request.method, request.params ?? {}).then(\n (result) => write({ id, result: result ?? null }),\n (error) => write({ id, error: asCliError(error).toJSON() }),\n );\n });\n}\n","import { spawn } from \"node:child_process\";\nimport { accessSync, constants, existsSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { CONTACT_KEY } from \"./apiKit\";\nimport { CliError } from \"./errors\";\nimport type { GhostlyEvent } from \"./events\";\nimport { turnRef } from \"./turnRefs\";\n\n/**\n * What `ghostly listen` does with each event: print it as a JSON line, run a command with it on stdin, or POST it to\n * a local bridge. Events are handled one at a time, in order; a cursor file acknowledges each one handled, so a\n * restarted listener resumes after the last.\n */\nexport interface ListenOptions {\n types: string[];\n exec?: string;\n webhook?: string;\n cursor?: string;\n print: boolean;\n write: (line: string) => void;\n /** `--from` and `--group`: events of anyone else stop here (they stay in the chat). */\n allow?: Allowlist;\n /** `--turns`: only agent turns, as `agent.turn` events. */\n turns?: boolean;\n}\n\n/**\n * Who `listen` passes on, checked before any hook or output: chats by id (a contact key is matched to its chat as\n * events come, so a chat made after the start counts too), groups by id. An event of a chat or group not on it is\n * dropped; one that names neither (`daemon.started`, `events.gap`, `identity.approval`) is this profile's own and\n * passes.\n */\nexport class Allowlist {\n private readonly peers = new Map<string, string>();\n\n /** `peerOf`: a chat's contact key (null when it has none yet, or the chat is gone). */\n constructor(private readonly chats: ReadonlySet<string>, private readonly keys: ReadonlySet<string>, private readonly groups: ReadonlySet<string>, private readonly peerOf: (chat: string) => Promise<string | null>) {}\n\n async allows(event: GhostlyEvent): Promise<boolean> {\n if (\"group\" in event) return typeof event.group === \"string\" && this.groups.has(event.group);\n if (!(\"chat\" in event)) return true;\n const chat = event.chat;\n if (typeof chat !== \"string\") return false;\n if (this.chats.has(chat)) return true;\n if (!this.keys.size) return false;\n // Kept once known: a chat's contact key does not change. One not paired yet is asked again next time.\n let key = this.peers.get(chat);\n if (!key) { key = (await this.peerOf(chat).catch(() => null)) || undefined; if (key) this.peers.set(chat, key); }\n return !!key && this.keys.has(key);\n }\n}\n\n/**\n * `--from` and `--group` as ids, resolved once at start: a contact key as that key (whatever any chat is named), else a\n * chat by id, prefix or name (`findChat`); a group by id, prefix or name. A name that later changes (or a contact who\n * renames themselves) moves nothing.\n */\nexport async function allowlist(from: readonly string[], groups: readonly string[], call: (method: string, params: Record<string, unknown>) => Promise<unknown>, peerOf: (chat: string) => Promise<string | null>): Promise<Allowlist> {\n const chats = new Set<string>(), keys = new Set<string>(), groupIds = new Set<string>();\n for (const ref of from) {\n if (CONTACT_KEY.test(ref)) keys.add(ref);\n else chats.add(((await call(\"chat.get\", { chat: ref })) as { id: string }).id);\n }\n for (const ref of groups) groupIds.add(((await call(\"group.get\", { group: ref })) as { id: string }).id);\n return new Allowlist(chats, keys, groupIds, peerOf);\n}\n\n/**\n * One agent turn (docs/CLI.md § Agent turns): a message a contact sent in a chat, or a group message that mentions\n * this profile. What the sender controls (the text, their name, a quoted snippet, a file's name, the id their message\n * is stored under) is under `untrusted` and nowhere else: an agent reads it as data, never as its instructions. `seq` is\n * the source event's, so `--cursor` and `--since` work as for any event; `id` is stable for the message (dedupe on it).\n */\nexport interface AgentTurn extends GhostlyEvent {\n type: \"agent.turn\";\n source: \"message.received\" | \"group.message\" | \"group.mentioned\";\n chat?: string;\n group?: string;\n /** A group's author (their key). */\n member?: string;\n /** What `send --reply` (or `group send --reply`) takes to answer it: an id made here (`turnRef`), never the sender's. */\n messageId: string;\n timestamp: number;\n /**\n * The message pressed a button of a question this profile sent (WISP 406 · Message Buttons), as this side's engine\n * matched it: the question's id, the button's id and its label, all this profile's own words.\n */\n press?: { messageId: string; button: string; label: string; inferred?: true };\n untrusted: { text: string; name: string | null; messageId: string; replyTo?: { id: string; snippet: string }; file?: { id: string; name: string; size: number; mime: string; voice: boolean } };\n}\n\ninterface TurnMessage { id: string; text: string; timestamp: number; nick: string | null; member?: string; mentioned?: boolean; replyTo?: { id: string; snippet: string }; press?: AgentTurn[\"press\"]; file?: { id: string; name: string; size: number; mime: string; voice?: unknown } }\n\n/** The turn an event starts, or null: only a contact's message, and a group message that mentions this profile. */\nexport function toTurn(event: GhostlyEvent): AgentTurn | null {\n const message = event.message as TurnMessage | undefined;\n if (!message || typeof message.text !== \"string\") return null;\n if (event.type === \"message.received\" && typeof event.chat === \"string\") return turn(event, \"message.received\", { chat: event.chat }, message);\n if (event.type === \"group.message\" && typeof event.group === \"string\" && message.mentioned) return turn(event, \"group.message\", { group: event.group, ...(message.member ? { member: message.member } : {}) }, message);\n // A mention learned after its message was reported (its `group.message` named nobody): the turn it did not start then.\n if (event.type === \"group.mentioned\" && typeof event.group === \"string\" && message.mentioned) return turn(event, \"group.mentioned\", { group: event.group, ...(message.member ? { member: message.member } : {}) }, message);\n return null;\n}\n\nfunction turn(event: GhostlyEvent, source: AgentTurn[\"source\"], where: Pick<AgentTurn, \"chat\" | \"group\" | \"member\">, message: TurnMessage): AgentTurn {\n const file = message.file;\n // The source event's id ends in the stored id, the sender's: this one is made of the same parts with the id made here.\n const place = where.chat ?? where.group ?? \"\", messageId = turnRef(place, message.id);\n return {\n seq: event.seq, id: `agent.turn:${source}:${place}:${messageId}`, type: \"agent.turn\", at: event.at, source, ...where, messageId, timestamp: message.timestamp,\n ...(message.press ? { press: message.press } : {}),\n untrusted: {\n text: message.text, name: message.nick ?? null, messageId: message.id,\n ...(message.replyTo ? { replyTo: { id: message.replyTo.id, snippet: message.replyTo.snippet } } : {}),\n ...(file ? { file: { id: file.id, name: file.name, size: file.size, mime: file.mime, voice: !!file.voice } } : {}),\n },\n };\n}\n\n/** Only loopback: events carry message text, and a webhook is a local bridge (WISP 1100 § Security). */\nexport function checkWebhook(url: string): URL {\n let parsed: URL;\n try { parsed = new URL(url); } catch { throw new CliError(\"usage\", `Not a URL: ${url}`); }\n if (![\"http:\", \"https:\"].includes(parsed.protocol)) throw new CliError(\"usage\", \"The webhook must be http:// or https://\");\n if (![\"127.0.0.1\", \"localhost\", \"[::1]\"].includes(parsed.hostname)) throw new CliError(\"usage\", \"The webhook must be on this machine (127.0.0.1, localhost or [::1])\");\n return parsed;\n}\n\nexport function readCursor(path: string | undefined): number | undefined {\n if (!path || !existsSync(path)) return undefined;\n const value = Number(readFileSync(path, \"utf8\").trim());\n return Number.isInteger(value) && value >= 0 ? value : undefined;\n}\n\n/**\n * Whether a cursor can be kept at `path`, asked before anything starts: its folder is there and takes a file. Before, a\n * folder that was not there was found at the first event: the listener ran, said \"event N failed: ENOENT\" for each one\n * and never wrote the cursor, so the next start began at the journal's end and skipped what came while it was down.\n * Throws what the file system says (`asCliError` names the path and the code: not_found, refused).\n */\nexport function checkCursor(path: string | undefined): void {\n if (!path) return;\n const folder = dirname(resolve(path));\n if (!statSync(folder).isDirectory()) throw new CliError(\"not_found\", `No such file or folder: ${folder}`);\n accessSync(folder, constants.W_OK | constants.X_OK);\n}\n\nfunction writeCursor(path: string, seq: number): void {\n // A new file each time (`wx`): a link left where the temporary file goes is removed, never written through.\n rmSync(path + \".tmp\", { force: true });\n writeFileSync(path + \".tmp\", String(seq) + \"\\n\", { mode: 0o600, flag: \"wx\" });\n renameSync(path + \".tmp\", path);\n}\n\nexport function matches(event: GhostlyEvent, types: readonly string[]): boolean {\n if (!types.length) return true;\n return types.some((type) => type.endsWith(\".\") || type.endsWith(\"*\") ? event.type.startsWith(type.replace(/\\*$/, \"\")) : event.type === type);\n}\n\n/** The environment a hook runs in: this one's, less the backup passphrase (a hook, and what it runs, never needs it). */\nexport function hookEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const rest = { ...env };\n delete rest.GHOSTLY_BACKUP_PASSPHRASE;\n return rest;\n}\n\n/** Runs `command` through the shell with the event on stdin (never in its arguments); resolves with its exit code. */\nfunction runExec(command: string, event: GhostlyEvent): Promise<number> {\n return new Promise((resolve) => {\n const child = spawn(command, {\n shell: true,\n stdio: [\"pipe\", \"inherit\", \"inherit\"],\n env: { ...hookEnv(process.env), GHOSTLY_EVENT_TYPE: event.type, GHOSTLY_EVENT_ID: event.id, GHOSTLY_EVENT_SEQ: String(event.seq) },\n });\n child.on(\"error\", () => resolve(127));\n child.on(\"close\", (code) => resolve(code ?? 1));\n child.stdin.on(\"error\", () => {});\n child.stdin.end(JSON.stringify(event) + \"\\n\");\n });\n}\n\nasync function post(url: URL, event: GhostlyEvent): Promise<boolean> {\n for (let attempt = 0; attempt < 3; attempt++) {\n try {\n // A redirect is not followed: it would post the event to wherever it points, off this machine too.\n const response = await fetch(url, { method: \"POST\", redirect: \"manual\", headers: { \"content-type\": \"application/json\", \"x-ghostly-event\": event.type, \"x-ghostly-seq\": String(event.seq) }, body: JSON.stringify(event), signal: AbortSignal.timeout(10_000) });\n if (response.ok) return true;\n } catch { /* retried */ }\n await new Promise((resolve) => setTimeout(resolve, 500 * 2 ** attempt));\n }\n return false;\n}\n\n/**\n * A handler that takes events in order, one at a time. A step that throws (the output closed, the allowlist's lookup,\n * the cursor's disk) is said on stderr and passed: the cursor moves on and the next event is handled.\n */\nexport function eventHandler(options: ListenOptions): (event: GhostlyEvent) => void {\n const webhook = options.webhook ? checkWebhook(options.webhook) : null;\n let chain = Promise.resolve();\n return (event) => {\n const seq = event.seq, type = event.type;\n chain = chain.then(async () => {\n const skip = () => { if (options.cursor) writeCursor(options.cursor, event.seq); };\n if (!matches(event, options.types)) return skip();\n if (options.allow && !(await options.allow.allows(event))) return skip();\n if (options.turns) {\n const next = toTurn(event);\n if (!next) return skip();\n event = next;\n }\n if (options.print) options.write(JSON.stringify(event));\n if (options.exec) {\n const code = await runExec(options.exec, event);\n if (code !== 0) process.stderr.write(`ghostly: --exec exited ${code} on event ${event.seq} (${event.type})\\n`);\n }\n if (webhook && !(await post(webhook, event))) process.stderr.write(`ghostly: the webhook did not take event ${event.seq} (${event.type})\\n`);\n // A failed handler still moves the cursor: one poisonous event must not stop the stream. The failure is on stderr.\n if (options.cursor) writeCursor(options.cursor, event.seq);\n }).catch((error: unknown) => {\n try {\n process.stderr.write(`ghostly: event ${seq} (${type}) failed: ${error instanceof Error ? error.message : String(error)}\\n`);\n if (options.cursor) writeCursor(options.cursor, seq);\n } catch { /* the next event moves the cursor */ }\n });\n };\n}\n","import { CliError } from \"./errors\";\n\n/**\n * Secrets a command takes (a Cashu token, an API key, a card's form) come on stdin: on the command line they show in\n * `ps` to every user of the machine while the command runs, and stay in the shell's history. Still taken there, with\n * a warning. Fills `params` in place; the warning to print on stderr, if any.\n */\nexport async function secretsFromStdin(method: string, params: Record<string, unknown>, read: () => Promise<string>): Promise<string | undefined> {\n const warning = (what: string) => `${what} on the command line shows in ps and the shell's history: give it on stdin instead`;\n if (method === \"wallet.redeem\") {\n if (params.token !== undefined) return warning(\"A token\");\n const token = (await read()).trim();\n if (!token) throw new CliError(\"usage\", \"Missing <cashu-token>: give it as an argument or on stdin\");\n params.token = token;\n return undefined;\n }\n if (method === \"wallet.create\") {\n // A form's fields are often plain (a network, a script type): only the API key is warned about.\n const onLine = params.apiKey !== undefined;\n if (params.stdin === true) {\n const values = { ...(params.values as Record<string, string> | undefined) };\n for (const line of (await read()).split(/\\r?\\n/)) {\n if (!line.trim()) continue;\n const eq = line.indexOf(\"=\");\n if (eq < 1) throw new CliError(\"usage\", \"--stdin takes name=value lines (api-key=… for the API key)\");\n const name = line.slice(0, eq).trim(), value = line.slice(eq + 1);\n if (name === \"api-key\") params.apiKey = value;\n else values[name] = value;\n }\n if (Object.keys(values).length) params.values = values;\n }\n delete params.stdin;\n return onLine ? warning(\"An API key\") : undefined;\n }\n return undefined;\n}\n","import { spawn } from \"node:child_process\";\nimport { connect } from \"node:net\";\nimport { openSync, readFileSync } from \"node:fs\";\nimport packageJson from \"../package.json\" with { type: \"json\" };\nimport { GLOBAL_OPTIONS, globalOption, liftGlobals, parseArgs, type OptionSpec, type Parsed } from \"./args\";\nimport { callApi, hideStateSecrets, SETTABLE } from \"./api\";\nimport { connectDaemon, type DaemonClient } from \"./client\";\nimport { COMMANDS, idSlot, noMoreThan, positionals, TEXT_COMMANDS } from \"./commands\";\nimport { ENGINE_METHODS, ENGINE_READS, SECRET_RESULTS } from \"./engineMethods\";\nimport { asCliError, CliError, EXIT } from \"./errors\";\nimport type { GhostlyEvent } from \"./events\";\nimport { keepServing, openHost, serve, type Host } from \"./host\";\nimport { allowlist, checkCursor, checkWebhook, eventHandler, readCursor } from \"./listen\";\nimport { resolve } from \"node:path\";\nimport { format } from \"node:util\";\nimport { backupFileProtection, restoreProfile } from \"./backup\";\nimport { buttonsOf } from \"./buttons\";\nimport { secretsFromStdin } from \"./secretInput\";\nimport {\n checkProfileName, createProfile, currentProfile, DEFAULT_PROFILE, ghostlyHome, listProfiles, lockOwner, profileExists, profilePaths, selectProfile,\n type ProfilePaths,\n} from \"./profiles\";\n\nconst VERSION = packageJson.version;\n\ninterface Globals { home: string; profile: string; paths: ProfilePaths; pretty: boolean }\n\nlet pretty = false;\nfunction print(value: unknown): void {\n process.stdout.write(JSON.stringify(value, null, pretty ? 2 : undefined) + \"\\n\");\n}\n\nfunction globals(parsed: Parsed): Globals {\n const home = ghostlyHome(parsed.options.home as string | undefined);\n const profile = currentProfile(home, parsed.options.profile as string | undefined);\n return { home, profile, paths: profilePaths(home, profile), pretty: parsed.options.pretty === true };\n}\n\n/** The profile must exist; `default` is made on first use, any other with `profile create`. */\nfunction requireProfile(g: Globals): void {\n if (profileExists(g.home, g.profile)) return;\n if (g.profile === DEFAULT_PROFILE) { createProfile(g.home, g.profile); return; }\n throw noProfile(g);\n}\n\nfunction noProfile(g: Globals): CliError {\n return new CliError(\"not_found\", `No profile ${g.profile}: make it with \\`ghostly profile create ${g.profile}\\``);\n}\n\n/** For a command that only reads: a mistyped profile is named as one, and `default` is not made by asking about it. */\nfunction knownProfile(g: Globals): void {\n if (g.profile !== DEFAULT_PROFILE && !profileExists(g.home, g.profile)) throw noProfile(g);\n}\n\n/** A way to call the API: the daemon's socket when one runs the profile, else the profile in this process. */\ninterface Session { mode: \"daemon\" | \"one-shot\"; call(method: string, params?: unknown): Promise<unknown>; close(): Promise<void> }\n\n/** Said on stderr when the daemon runs another release than this command (it started before an upgrade). */\nexport function versionWarning(daemon: unknown, cli: string): string | null {\n if (typeof daemon !== \"string\" || daemon === cli) return null;\n return `ghostly: the daemon runs ${daemon} and this command is ${cli}: \\`ghostly daemon restart\\` runs the new code`;\n}\n\nasync function warnVersion(client: DaemonClient): Promise<void> {\n const status = await client.call(\"status\").catch(() => null) as { version?: unknown } | null;\n const warning = versionWarning(status?.version, VERSION);\n if (warning) process.stderr.write(warning + \"\\n\");\n}\n\n/**\n * A command about one 1:1 chat: a one-shot for it leaves the groups' sessions unstarted. A fresh process starts an edge\n * per group member, and their first polls spent the relays' 30 requests a minute in seconds: the chat's message then\n * waited for the next minute (60 s and more instead of 3 to 5).\n */\nexport function chatOnly(method: string, params: Record<string, unknown>): boolean {\n // A status card's `chat` may name a group as well (WISP 405 · Status Cards): its sessions must start.\n if (method.startsWith(\"task.\") || method.startsWith(\"routine.\") || method.startsWith(\"button.\")) return false;\n // A usage report to every 1:1 chat (`--all`) needs no group; one to a named chat may name a group.\n if (method === \"usage.send\") return params.all === true;\n return method.startsWith(\"chat.\") || (params.chat !== undefined && params.group === undefined && !method.startsWith(\"group.\"));\n}\n\nasync function session(g: Globals, chat = false): Promise<Session> {\n requireProfile(g);\n const client = await connectDaemon(g.paths.socket);\n if (client) await warnVersion(client);\n if (client) return { mode: \"daemon\", call: (m, p) => client.call(m, p), close: async () => client.close() };\n const host = await openHost(g.paths, \"one-shot\", VERSION, { deferGroups: chat });\n return { mode: \"one-shot\", call: (m, p) => callApi(host.ctx, m, p ?? {}), close: () => host.close() };\n}\n\n/** `chat`: the command is about one 1:1 chat (`chatOnly`), and a one-shot needs nothing of the groups. */\nasync function withSession<T>(g: Globals, work: (s: Session) => Promise<T>, chat = false): Promise<T> {\n const s = await session(g, chat);\n try { return await work(s); } finally { await s.close(); }\n}\n\nasync function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) chunks.push(chunk as Buffer);\n return Buffer.concat(chunks).toString(\"utf8\");\n}\n\n// ---------- help ----------\n\nconst o = (type: OptionSpec[\"type\"], description: string): OptionSpec => ({ type, description });\n\n/** Commands with their own flow (main.ts), as help shows them. */\nconst SPECIAL: [usage: string, summary: string, options?: Record<string, OptionSpec>][] = [\n [\"profile create <name> [--use] [--name <shown name>]\", \"Make a profile (its own keys, chats and wallets)\", { use: o(\"boolean\", \"Make it the current profile\"), name: o(\"string\", \"The name contacts see\") }],\n [\"profile list\", \"Profiles, and which one is current\"],\n [\"profile use <name>\", \"Make a profile the current one\"],\n [\"profile show\", \"The name contacts see, and whether it is shared\"],\n [\"profile set [--name <name>] [--share-profile | --no-share-profile]\", \"Change the name contacts see\", { name: o(\"string\", \"The name contacts see\"), \"share-profile\": o(\"boolean\", \"Share the name and picture with contacts (--no-share-profile: do not)\") }],\n [\"profile backup --out <file> [--passphrase-file f | --no-passphrase] [--light]\", \"A backup of the profile, encrypted with a passphrase (from a file or GHOSTLY_BACKUP_PASSPHRASE); --no-passphrase makes one anyone can read\", { out: o(\"string\", \"The backup file to write\"), \"passphrase-file\": o(\"string\", \"The passphrase, from this file (else GHOSTLY_BACKUP_PASSPHRASE)\"), passphrase: { ...o(\"boolean\", \"Do not encrypt. The file then holds the profile's keys and wallet secrets in the clear\"), negatedOnly: true }, light: o(\"boolean\", \"Leave out files over 1 MB (voice messages over 4 MB); their messages stay\") }],\n [\"profile restore <file> <new profile> [--passphrase-file f] [--use]\", \"A backup into a new profile (a backup made with --no-passphrase needs none)\", { \"passphrase-file\": o(\"string\", \"The passphrase, from this file (else GHOSTLY_BACKUP_PASSPHRASE)\"), use: o(\"boolean\", \"Make it the current profile\") }],\n [\"daemon [--detach]\", \"Keep the profile online (foreground; --detach runs it in the background)\", { detach: o(\"boolean\", \"Run in the background (log in the profile folder)\"), timeout: o(\"number\", \"Seconds --detach waits for it to start (default 60)\") }],\n [\"daemon status\", \"Whether a daemon runs the profile, its version, and its socket (for the socket API)\"],\n [\"daemon stop\", \"Stop the profile's daemon\", { timeout: o(\"number\", \"Seconds to wait for it to stop (default 20)\") }],\n [\"daemon restart\", \"Stop the profile's daemon and start it again in the background (after an upgrade: the new code)\", { timeout: o(\"number\", \"Seconds to wait for each step\") }],\n [\"listen [--since seq] [--cursor file] [--type t]... [--turns] [--from <chat|key>]... [--group <group>]... [--exec cmd] [--webhook url] [--print]\", \"Stream events as JSON lines (starts the profile here if no daemon runs it)\", {\n since: o(\"number\", \"Replay events after this seq first\"), cursor: o(\"string\", \"A file that keeps the last seq handled (read at start, written after each event)\"),\n type: o(\"list\", \"Only events of this type, or starting with it (repeat for more)\"),\n turns: o(\"boolean\", \"Only agent turns: each message received, and each group message that mentions you, as one agent.turn event\"),\n from: o(\"list\", \"Only this chat's events (id, prefix, name or the contact's key; again for more)\"),\n group: o(\"list\", \"Only this group's events (again for more); with --from or --group, nobody else's event gets through\"),\n exec: o(\"string\", \"Run this command per event, the event as JSON on stdin\"),\n webhook: o(\"string\", \"POST each event to this URL\"), print: o(\"boolean\", \"Print events too when --exec or --webhook handles them\"),\n }],\n [\"settings get [--show-secret]\", \"The profile's settings\", { \"show-secret\": o(\"boolean\", \"Show credentials too\") }],\n [\"settings set <key> <json-value>\", \"Change one: relays, irohRelays, hyperdhtRelay, readRelays, iceServers, publicProfiles, online, shareProfile, sendTyping, autoDownloads, serveFiles, nick\"],\n [\"engine <method> [json-params | -] [--confirm-real] [--show-secret]\", \"Any call of the app's engine, with its own parameters\", { \"confirm-real\": o(\"boolean\", \"Confirm a Mainnet (real money) spend\"), \"show-secret\": o(\"boolean\", \"Print a call's secrets\") }],\n [\"engine --list\", \"The engine's calls\"],\n [\"call pipe [<chat|call>]\", \"A call's audio on stdin/stdout: raw s16le mono PCM at the call's rate (e.g. with sox or ffmpeg)\"],\n [\"identity add <provider> [subject] [--signer id] [--field name=value]... [--days n]\", \"Make an identity proof (a tool's or a published one finishes with identity complete)\", {\n signer: o(\"string\", \"Which signer makes the proof (identity providers lists them)\"), field: o(\"list\", \"A field of the provider's form, name=value\"),\n days: o(\"number\", \"How long the proof holds\"), timeout: o(\"number\", \"Seconds to wait for an approval\"),\n }],\n [\"version\", \"This CLI's version (also ghostly --version)\"],\n];\n\nfunction help(): string {\n const rows: [string, string][] = [\n ...SPECIAL.slice(0, 7).map(([usage, summary]) => [usage, summary] as [string, string]),\n ...Object.values(COMMANDS).filter((c) => c.method !== \"events.replay\").map((c) => [c.usage, c.summary] as [string, string]),\n ...Object.values(TEXT_COMMANDS).map((c) => [c.usage, c.summary] as [string, string]),\n ...SPECIAL.slice(7).map(([usage, summary]) => [usage, summary] as [string, string]),\n [COMMANDS.events.usage, COMMANDS.events.summary],\n ];\n const width = 58;\n return [\n `ghostly ${VERSION}: Ghostly for bots and terminals, the app's own engine without a screen.`,\n \"\",\n \"Usage: ghostly <command> [options] (JSON on stdout; errors as {\\\"error\\\":{code,message}} with an exit code)\",\n \"\",\n ...rows.map(([usage, summary]) => ` ${usage.length > width ? usage + \"\\n \" + \" \".repeat(width) : usage.padEnd(width)} ${summary}`),\n \"\",\n \"Options for every command:\",\n ...Object.entries(GLOBAL_OPTIONS).map(([name, o]) => ` --${name}${o.short ? `, -${o.short}` : \"\"}`.padEnd(24) + o.description),\n \"\",\n \"A <chat> is its id, a unique prefix of it, or its name; a <group> the same. Exit codes: 0 done, 1 failed,\",\n \"2 usage, 3 not found, 4 timed out, 5 needs --force/--yes/--confirm-real. See packages/cli/README.md.\",\n ].join(\"\\n\");\n}\n\n/** What `ghostly help help` answers. */\nconst HELP_USAGE = [\n \"Usage: ghostly help [<command>...]\", \"\",\n \" Help for every command, or for one command or a group of them (also ghostly <command> --help)\", \"\",\n \"Options for every command: --profile, -p <name> --home <dir> --pretty --help, -h\",\n].join(\"\\n\");\n\n/** Every command's help row: usage, summary and, for the table and text commands, their options. */\nfunction helpRows(): { usage: string; summary: string; options?: Record<string, OptionSpec> }[] {\n return [\n ...SPECIAL.map(([usage, summary, options]) => ({ usage, summary, options })),\n ...Object.values(COMMANDS),\n ...Object.values(TEXT_COMMANDS),\n ];\n}\n\n/** The words that name a command in its usage line: up to the first argument or option. */\nfunction commandWords(usage: string): string[] {\n const words: string[] = [];\n for (const word of usage.split(\" \")) {\n if (!/^[a-z][a-z-]*$/.test(word)) break;\n words.push(word);\n }\n return words;\n}\n\n/**\n * Help for one command (`ghostly help file send`, `ghostly file send --help`): its usage, what it does and its\n * options; for a group (`ghostly help file`), the group's commands.\n */\nexport function commandHelp(words: readonly string[]): string {\n // `help` is no command of the tables: its own usage, rather than \"Unknown command: help\".\n if (words[0] === \"help\") return HELP_USAGE;\n const named = (list: readonly string[]) => helpRows().filter((row) => { const own = commandWords(row.usage); return list.every((word, i) => own[i] === word); });\n // Arguments after the command's words (`ghostly send bob hi --help`) are not part of its name.\n let rows = named(words);\n while (!rows.length && words.length > 1) rows = named(words = words.slice(0, -1));\n const asked = words.join(\" \");\n if (!rows.length) throw new CliError(\"usage\", `Unknown command: ${asked} (ghostly help)`);\n const exact = rows.filter((row) => commandWords(row.usage).join(\" \") === asked);\n const lines: string[] = [];\n if (exact.length && exact.length === rows.length) {\n for (const row of exact) {\n lines.push(`Usage: ghostly ${row.usage}`, \"\", ` ${row.summary}`);\n const options = Object.entries(row.options ?? {}).filter(([, o]) => o.description);\n if (options.length) {\n lines.push(\"\", \"Options:\");\n const labels = options.map(([name, o]) => ` --${o.negatedOnly ? \"no-\" : \"\"}${name}${o.type === \"boolean\" ? \"\" : ` <${o.type === \"number\" ? \"n\" : \"value\"}>`}`);\n // At least two spaces between an option and its text, however long the option.\n const width = Math.max(26, ...labels.map((label) => label.length + 2));\n options.forEach(([, o], i) => lines.push(labels[i].padEnd(width) + o.description));\n }\n // A text that starts with a dash goes after `--`, and so does everything else on the line.\n if (Object.values(TEXT_COMMANDS).includes(row as (typeof TEXT_COMMANDS)[string]))\n lines.push(\"\", \" -- ends the options: everything after it is the text, options included (\\\"-- -1 --wait sent\\\" sends\", \" all of it). Put options before --.\");\n lines.push(\"\");\n }\n } else {\n const width = Math.min(58, Math.max(...rows.map((row) => row.usage.length)));\n lines.push(`ghostly ${asked}: ${rows.length} commands (ghostly help ${asked} <command> for one)`, \"\");\n for (const row of rows) lines.push(` ${row.usage.length > width ? row.usage + \"\\n \" + \" \".repeat(width) : row.usage.padEnd(width)} ${row.summary}`);\n lines.push(\"\");\n }\n lines.push(\"Options for every command: --profile, -p <name> --home <dir> --pretty --help, -h\");\n return lines.join(\"\\n\");\n}\n\n// ---------- commands with their own flow ----------\n\nasync function profileCommand(sub: string | undefined, argv: string[]): Promise<void> {\n const specs: Record<string, Record<string, OptionSpec>> = {\n create: { use: { type: \"boolean\", description: \"\" }, name: { type: \"string\", description: \"\" } },\n backup: { out: { type: \"string\", description: \"\" }, \"passphrase-file\": { type: \"string\", description: \"\" }, passphrase: { type: \"boolean\", description: \"\" }, light: { type: \"boolean\", description: \"\" } },\n restore: { \"passphrase-file\": { type: \"string\", description: \"\" }, use: { type: \"boolean\", description: \"\" } },\n set: { name: { type: \"string\", description: \"\" }, \"share-profile\": { type: \"boolean\", description: \"\" } },\n };\n const parsed = parseArgs(argv, specs[sub ?? \"\"] ?? {});\n const g = globals(parsed);\n pretty = g.pretty;\n switch (sub) {\n case \"create\": {\n const [name] = parsed.positionals;\n if (!name || parsed.positionals.length > 1) throw new CliError(\"usage\", \"ghostly profile create <name>\");\n const paths = createProfile(g.home, name);\n if (parsed.options.use) selectProfile(g.home, name);\n let shown: unknown = null;\n if (typeof parsed.options.name === \"string\") {\n shown = await withSession({ ...g, profile: name, paths }, (s) => s.call(\"profile.set\", { name: parsed.options.name }));\n }\n print({ created: name, folder: paths.dir, current: currentProfile(g.home) === name, ...(shown ? { profile: shown } : {}) });\n return;\n }\n case \"list\": {\n noMoreThan(0, parsed.positionals, \"profile list\");\n const current = currentProfile(g.home);\n print({ profiles: listProfiles(g.home).map((name) => ({ name, current: name === current, running: lockOwner(profilePaths(g.home, name)) !== null })), home: g.home });\n return;\n }\n case \"use\": {\n const [name] = parsed.positionals;\n if (!name) throw new CliError(\"usage\", \"ghostly profile use <name>\");\n noMoreThan(1, parsed.positionals, \"profile use <name>\");\n selectProfile(g.home, name);\n print({ current: name });\n return;\n }\n case \"show\":\n noMoreThan(0, parsed.positionals, \"profile show\");\n print(await withSession(g, (s) => s.call(\"profile.get\")));\n return;\n case \"set\": {\n noMoreThan(0, parsed.positionals, \"profile set [--name <name>] [--share-profile | --no-share-profile]\");\n const params: Record<string, unknown> = {};\n if (parsed.options.name !== undefined) params.name = parsed.options.name;\n if (parsed.options[\"share-profile\"] !== undefined) params.shareProfile = parsed.options[\"share-profile\"];\n if (!Object.keys(params).length) throw new CliError(\"usage\", \"ghostly profile set [--name <name>] [--share-profile | --no-share-profile]\");\n print(await withSession(g, (s) => s.call(\"profile.set\", params)));\n return;\n }\n case \"backup\": {\n noMoreThan(0, parsed.positionals, \"profile backup --out <file> [--passphrase-file f | --no-passphrase] [--light]\");\n const out = parsed.options.out;\n if (typeof out !== \"string\") throw new CliError(\"usage\", \"ghostly profile backup --out <file> [--passphrase-file f | --no-passphrase] [--light] (or GHOSTLY_BACKUP_PASSPHRASE)\");\n // Encrypted unless --no-passphrase says otherwise, and never a guess: a passphrase and --no-passphrase together are refused.\n const unsealed = parsed.options.passphrase === false;\n if (parsed.options.passphrase === true) throw new CliError(\"usage\", \"The passphrase comes from --passphrase-file or GHOSTLY_BACKUP_PASSPHRASE, never the command line\");\n if (unsealed && (parsed.options[\"passphrase-file\"] !== undefined || process.env.GHOSTLY_BACKUP_PASSPHRASE)) throw new CliError(\"usage\", \"A passphrase is set (--passphrase-file or GHOSTLY_BACKUP_PASSPHRASE) and --no-passphrase was given: choose one\");\n const light = parsed.options.light === true ? { light: true } : {};\n const made = await withSession(g, (s) => s.call(\"profile.backup\", unsealed ? { path: resolve(out), noPassphrase: true, ...light } : { path: resolve(out), passphrase: backupPassphrase(parsed.options[\"passphrase-file\"]), ...light }));\n if (unsealed) process.stderr.write(\"Not encrypted: this file holds the profile's keys, chats and wallet secrets in the clear. Anyone who gets it gets everything in it, including any money in its wallets.\\n\");\n print(made);\n return;\n }\n case \"restore\": {\n const [file, name] = parsed.positionals;\n if (!file || !name) throw new CliError(\"usage\", \"ghostly profile restore <file> <new profile> [--passphrase-file f] [--use]\");\n noMoreThan(2, parsed.positionals, \"profile restore <file> <new profile> [--passphrase-file f] [--use]\");\n // A backup made without a passphrase says so in its header: none is asked for, and the answer says it was not protected.\n const protection = await backupFileProtection(file);\n const paths = await restoreProfile(g.home, checkProfileName(name), file, protection === \"none\" ? undefined : backupPassphrase(parsed.options[\"passphrase-file\"], RESTORE_PASSPHRASE));\n if (parsed.options.use) selectProfile(g.home, name);\n print({ restored: name, folder: paths.dir, protection });\n return;\n }\n default:\n throw new CliError(\"usage\", \"ghostly profile create|list|use|show|set|picture|backup|restore\");\n }\n}\n\n/**\n * A clean stop (peers told goodbye, the store folded), once; a second signal, or a stop that takes longer than\n * 20 s, leaves at once. The store's journal makes that safe.\n */\nfunction stopper(work: () => Promise<void>): () => void {\n let started = false;\n return () => {\n if (started) process.exit(1);\n started = true;\n setTimeout(() => process.exit(1), 20_000).unref();\n void work().then(() => process.exit(0), () => process.exit(1));\n };\n}\n\n/**\n * Ctrl-C, a service manager's stop, and the terminal or SSH session it ran in closing (SIGHUP, which would otherwise\n * end it on the spot): each a clean stop.\n */\nfunction onStopSignals(stop: () => void): void {\n for (const signal of [\"SIGINT\", \"SIGTERM\", \"SIGHUP\"] as const) process.on(signal, stop);\n}\n\nconst BACKUP_PASSPHRASE = \"The backup passphrase comes from --passphrase-file or GHOSTLY_BACKUP_PASSPHRASE (at least 12 characters). For a backup that is not encrypted, say so with --no-passphrase\";\n/** Restore takes no --no-passphrase: a backup made without one says so itself. */\nconst RESTORE_PASSPHRASE = \"This backup is encrypted: its passphrase comes from --passphrase-file or GHOSTLY_BACKUP_PASSPHRASE\";\n\n/** The backup passphrase: from a file or the environment, never the command line (it would sit in shell history). */\nfunction backupPassphrase(file: unknown, missing = BACKUP_PASSPHRASE): string {\n const value = typeof file === \"string\" ? readFileSync(file, \"utf8\").replace(/\\r?\\n$/, \"\") : process.env.GHOSTLY_BACKUP_PASSPHRASE;\n if (!value) throw new CliError(\"usage\", missing);\n return value;\n}\n\nasync function runDaemon(g: Globals): Promise<void> {\n requireProfile(g);\n if (await connectDaemon(g.paths.socket)) throw new CliError(\"busy\", `A daemon already runs profile ${g.profile}`);\n const host = await openHost(g.paths, \"daemon\", VERSION);\n let served: Awaited<ReturnType<typeof serve>>;\n try { served = await serve(host); } catch (error) { await host.close(); throw error; }\n keepServing();\n const stop = stopper(async () => { await served.close(); await host.close(); });\n host.ctx.stop = stop;\n onStopSignals(stop);\n host.ctx.hub.emit(\"daemon.started\", `daemon.started:${process.pid}:${Date.now()}`, { pid: process.pid, version: VERSION });\n print({ daemon: \"ready\", profile: g.profile, pid: process.pid, socket: g.paths.socket, lastSeq: host.ctx.hub.lastSeq });\n}\n\nasync function daemonCommand(argv: string[]): Promise<void> {\n const sub = argv[0] && !argv[0].startsWith(\"-\") ? argv[0] : undefined;\n const parsed = parseArgs(sub ? argv.slice(1) : argv, { detach: { type: \"boolean\", description: \"\" }, timeout: { type: \"number\", description: \"\" } });\n const g = globals(parsed);\n pretty = g.pretty;\n if (!sub || [\"status\", \"stop\", \"restart\"].includes(sub)) noMoreThan(0, parsed.positionals, sub ? `daemon ${sub}` : \"daemon [--detach]\");\n if (sub === \"status\") {\n knownProfile(g);\n const client = await connectDaemon(g.paths.socket);\n // The socket, running or not: a program on the socket API (examples/*.mjs) finds it here. It is in the profile's\n // folder, or in /tmp/ghostly-<hash>/ when that path is too long for a socket, so a program cannot guess it.\n if (!client) { print({ running: false, profile: g.profile, socket: g.paths.socket }); return; }\n try { print({ running: true, ...(await client.call(\"status\") as object), socket: g.paths.socket }); } finally { client.close(); }\n return;\n }\n if (sub === \"stop\") {\n knownProfile(g);\n const stopFor = seconds(parsed, 20);\n const client = await connectDaemon(g.paths.socket);\n if (!client) throw new CliError(\"not_found\", `No daemon runs profile ${g.profile}`);\n print({ stopped: true, pid: await stopDaemon(g, client, stopFor) });\n return;\n }\n if (sub === \"restart\") {\n // The running one stops (if one runs), and this command's own code starts in the background.\n const stopFor = seconds(parsed, 20), startFor = seconds(parsed, 60);\n const client = await connectDaemon(g.paths.socket);\n const before = client ? (await client.call(\"status\").catch(() => null) as { version?: string } | null)?.version ?? null : null;\n const stopped = client ? await stopDaemon(g, client, stopFor) : null;\n const started = await startDetached(g, startFor);\n print({ restarted: true, stopped, before, version: VERSION, ...started });\n return;\n }\n if (sub) throw new CliError(\"usage\", \"ghostly daemon [--detach] | daemon status | daemon stop | daemon restart\");\n const startFor = seconds(parsed, 60);\n if (!parsed.options.detach) { await runDaemon(g); return; }\n print(await startDetached(g, startFor));\n}\n\n/** `--timeout <s>`, or `fallback` without one: seconds above 0 (0 or less would not wait at all). */\nfunction seconds(parsed: Parsed, fallback: number): number {\n const value = parsed.options.timeout;\n if (value === undefined) return fallback;\n if (typeof value !== \"number\" || !(value > 0)) throw new CliError(\"usage\", \"--timeout takes a number of seconds above 0\");\n return value;\n}\n\n/**\n * Asks the daemon to stop and waits until its process is gone, not only until it let go of the profile (it releases\n * the lock a moment before it exits); its pid.\n */\nasync function stopDaemon(g: Globals, client: DaemonClient, seconds: number): Promise<number> {\n const { pid } = await client.call(\"daemon.stop\") as { pid: number };\n client.close();\n const until = Date.now() + seconds * 1000;\n const running = () => lockOwner(g.paths) === pid || processAlive(pid);\n while (running() && Date.now() < until) await new Promise((resolve) => setTimeout(resolve, 100));\n if (running()) throw new CliError(\"timeout\", `The daemon (process ${pid}) has not stopped yet`);\n return pid;\n}\n\n/** Throws busy, with its pid, when a process other than `except` holds the profile. */\nfunction heldBy(g: Globals, except?: number): void {\n const owner = lockOwner(g.paths);\n if (owner !== null && owner !== except) throw new CliError(\"busy\", `Profile ${g.profile} is in use by process ${owner}`, { pid: owner });\n}\n\nfunction processAlive(pid: number): boolean {\n try { process.kill(pid, 0); return true; } catch (error) { return (error as NodeJS.ErrnoException).code === \"EPERM\"; }\n}\n\n/** A daemon in the background, running this command's code, once its socket answers. */\nasync function startDetached(g: Globals, seconds: number) {\n requireProfile(g);\n if (await connectDaemon(g.paths.socket)) throw new CliError(\"busy\", `A daemon already runs profile ${g.profile}`);\n // A one-shot command (or a listen) that opened the profile in process holds it: the child would only log that.\n heldBy(g);\n const log = openSync(g.paths.log, \"a\", 0o600);\n const child = spawn(process.execPath, [...process.execArgv, process.argv[1], \"daemon\", \"--home\", g.home, \"--profile\", g.profile], { detached: true, stdio: [\"ignore\", log, log] });\n child.unref();\n const until = Date.now() + seconds * 1000;\n let client: DaemonClient | null = null;\n while (!client && Date.now() < until) {\n if (child.exitCode !== null) break;\n await new Promise((resolve) => setTimeout(resolve, 200));\n client = await connectDaemon(g.paths.socket, 500);\n }\n if (!client) {\n heldBy(g, child.pid); // another process took the profile between the check and the child's start\n // Alive still: it is starting (a large profile takes seconds), not failed, and the profile is its own now.\n if (child.exitCode === null && child.pid !== undefined && processAlive(child.pid)) {\n throw new CliError(\"timeout\", `The daemon (process ${child.pid}) is still starting; ghostly daemon status shows when it is up`, { pid: child.pid, log: g.paths.log });\n }\n throw new CliError(\"engine\", `The daemon did not start; see ${g.paths.log}`);\n }\n client.close();\n return { daemon: \"started\", profile: g.profile, pid: child.pid, socket: g.paths.socket, log: g.paths.log };\n}\n\nasync function listenCommand(argv: string[]): Promise<void> {\n const parsed = parseArgs(argv, {\n since: { type: \"number\", description: \"\" }, cursor: { type: \"string\", description: \"\" }, type: { type: \"list\", description: \"\" },\n exec: { type: \"string\", description: \"\" }, webhook: { type: \"string\", description: \"\" }, print: { type: \"boolean\", description: \"\" },\n from: { type: \"list\", description: \"\" }, group: { type: \"list\", description: \"\" }, turns: { type: \"boolean\", description: \"\" },\n });\n // Before the profile opens: `listen bob` (meant as `--from bob`) streams nobody's events instead of everybody's.\n noMoreThan(0, parsed.positionals, SPECIAL.find(([usage]) => usage.startsWith(\"listen \"))![0]);\n const g = globals(parsed);\n pretty = false; // one event per line\n if (parsed.options.webhook) checkWebhook(String(parsed.options.webhook));\n const types = (parsed.options.type as string[] | undefined) ?? [];\n const turns = parsed.options.turns === true;\n if (turns && types.length) throw new CliError(\"usage\", \"--turns takes the place of --type: a turn is a message received, or a group message that mentions you\");\n const from = (parsed.options.from as string[] | undefined) ?? [], groups = (parsed.options.group as string[] | undefined) ?? [];\n const cursor = parsed.options.cursor as string | undefined;\n // Before the profile opens: a cursor that cannot be written is no per-event warning.\n checkCursor(cursor);\n let since = (parsed.options.since as number | undefined) ?? readCursor(cursor);\n let handle: ((event: GhostlyEvent) => void) | null = null;\n const start = async (call: (method: string, params: Record<string, unknown>) => Promise<unknown>, peerOf: (chat: string) => Promise<string | null>) => {\n handle = eventHandler({\n types, turns,\n exec: parsed.options.exec as string | undefined,\n webhook: parsed.options.webhook as string | undefined,\n cursor,\n // With a hook, events go to the hook; --print shows them too.\n print: parsed.options.print === true || (!parsed.options.exec && !parsed.options.webhook),\n write: (line) => process.stdout.write(line + \"\\n\"),\n allow: from.length || groups.length ? await allowlist(from, groups, call, peerOf) : undefined,\n });\n };\n const onEvent = (event: GhostlyEvent) => { since = event.seq; handle?.(event); };\n requireProfile(g);\n const client = await connectDaemon(g.paths.socket);\n if (client) await warnVersion(client);\n if (client) {\n // The subscription takes the connection: a chat's contact key is asked on a connection of its own.\n await start((m, p) => client.call(m, p), async (chat) => {\n const c = await connectDaemon(g.paths.socket, 1000);\n if (!c) return null;\n try { return ((await c.call(\"chat.get\", { chat })) as { peer?: string }).peer || null; } finally { c.close(); }\n }).catch((error) => { client.close(); throw error; });\n // Followed until stopped; a daemon that restarts is picked up again after the last event seen.\n let current: DaemonClient | null = client;\n const follow = async (c: DaemonClient) => {\n await c.subscribe(since, onEvent, () => {\n current = null;\n void (async () => {\n while (!current) {\n await new Promise((resolve) => setTimeout(resolve, 1000));\n const next = await connectDaemon(g.paths.socket, 1000);\n if (next) { current = next; await follow(next).catch(() => { current = null; }); }\n }\n })();\n });\n };\n await follow(client);\n process.on(\"SIGINT\", () => { current?.close(); process.exit(0); });\n process.on(\"SIGTERM\", () => { current?.close(); process.exit(0); });\n return;\n }\n // No daemon: this process becomes it (the socket too, so hooks can answer with `ghostly send`).\n const host: Host = await openHost(g.paths, \"daemon\", VERSION);\n const call = (m: string, p: Record<string, unknown>) => callApi(host.ctx, m, p);\n await start(call, async (chat) => ((await call(\"chat.get\", { chat })) as { peer?: string }).peer || null).catch(async (error) => { await host.close(); throw error; });\n const served = await serve(host).catch(async (error) => { await host.close(); throw error; });\n keepServing();\n const stop = stopper(async () => { await served.close(); await host.close(); });\n host.ctx.stop = stop;\n onStopSignals(stop);\n for (const event of host.ctx.hub.replay(since ?? host.ctx.hub.lastSeq)) onEvent(event);\n host.ctx.hub.onEvent(onEvent);\n host.ctx.hub.emit(\"daemon.started\", `daemon.started:${process.pid}:${Date.now()}`, { pid: process.pid, version: VERSION, listen: true });\n}\n\nasync function engineCommand(argv: string[]): Promise<void> {\n const parsed = parseArgs(argv, { \"confirm-real\": { type: \"boolean\", description: \"\" }, \"show-secret\": { type: \"boolean\", description: \"\" }, list: { type: \"boolean\", description: \"\" } });\n const g = globals(parsed);\n pretty = g.pretty;\n if (parsed.options.list) { noMoreThan(0, parsed.positionals, \"engine --list\"); print({ methods: [...ENGINE_METHODS, ...ENGINE_READS].sort() }); return; }\n const [method, raw] = parsed.positionals;\n if (!method || parsed.positionals.length > 2) throw new CliError(\"usage\", \"ghostly engine <method> [json-params | -]\");\n if (!ENGINE_METHODS.includes(method) && !ENGINE_READS.includes(method)) throw new CliError(\"not_found\", `The engine has no call ${method} (ghostly engine --list)`);\n const showSecret = parsed.options[\"show-secret\"] === true;\n if (SECRET_RESULTS.has(method) && !showSecret) throw new CliError(\"confirm\", `${method} answers with secrets: add --show-secret to print them`);\n let params: unknown;\n const text = raw === \"-\" ? await readStdin() : raw;\n if (text !== undefined && text.trim()) {\n try { params = JSON.parse(text); } catch { throw new CliError(\"usage\", \"The parameters must be JSON (an object)\"); }\n }\n const confirmReal = parsed.options[\"confirm-real\"] === true;\n if (confirmReal && params && typeof params === \"object\") (params as Record<string, unknown>).confirmedReal = true;\n let result = await withSession(g, (s) => s.call(\"engine.call\", { method, params, confirmReal }));\n if (method === \"getState\" && !showSecret && result && typeof result === \"object\") {\n result = hideStateSecrets(result as Parameters<typeof hideStateSecrets>[0]);\n }\n print(result);\n}\n\nasync function settingsCommand(sub: string | undefined, argv: string[]): Promise<void> {\n const parsed = parseArgs(argv, { \"show-secret\": { type: \"boolean\", description: \"\" } });\n const g = globals(parsed);\n pretty = g.pretty;\n if (sub === \"get\") { noMoreThan(0, parsed.positionals, \"settings get [--show-secret]\"); print(await withSession(g, (s) => s.call(\"settings.get\", { showSecret: parsed.options[\"show-secret\"] === true }))); return; }\n if (sub === \"set\") {\n const [key, value] = parsed.positionals;\n if (!key || value === undefined || parsed.positionals.length > 2) throw new CliError(\"usage\", \"ghostly settings set <key> <json-value>\");\n let parsedValue: unknown;\n try { parsedValue = JSON.parse(value); } catch { parsedValue = value; }\n // A text setting (nick) keeps the word as given when it reads as JSON of another kind (2024, true, null).\n if (SETTABLE[key] === \"string\" && typeof parsedValue !== \"string\") parsedValue = value;\n print(await withSession(g, (s) => s.call(\"settings.set\", { [key]: parsedValue })));\n return;\n }\n throw new CliError(\"usage\", \"ghostly settings get | settings set <key> <json-value>\");\n}\n\nasync function textCommand(name: string, argv: string[]): Promise<void> {\n const spec = TEXT_COMMANDS[name];\n const parsed = parseArgs(argv, spec.options, idSlot(spec));\n const g = globals(parsed);\n pretty = g.pretty;\n const [target, ...rest] = parsed.positionals;\n // `edit` names the message before the text.\n const [message, ...words] = spec.message ? rest : [undefined, ...rest];\n if (!target || (spec.message && !message)) throw new CliError(\"usage\", `ghostly ${spec.usage}`);\n let text = words.join(\" \");\n if (parsed.options.text !== undefined) {\n if (text || parsed.options.stdin) throw new CliError(\"usage\", \"Give the text once: as arguments, --text or stdin\");\n text = String(parsed.options.text);\n } else if (parsed.options.stdin || (!text && !process.stdin.isTTY)) {\n if (text) throw new CliError(\"usage\", \"Give the text as arguments or on stdin, not both\");\n text = (await readStdin()).replace(/\\n$/, \"\");\n }\n if (!text.trim()) throw new CliError(\"usage\", `No text: ghostly ${spec.usage}`);\n const params: Record<string, unknown> = { [spec.target]: target, text, force: parsed.options.force === true, ...(message !== undefined && { message }) };\n if (spec.target === \"group\") params.mentions = parsed.options.mention ?? [];\n if (parsed.options.reply !== undefined) params.reply = parsed.options.reply;\n if (!spec.message) Object.assign(params, buttonsOf(parsed.options));\n print(await withSession(g, (s) => {\n if (spec.target === \"chat\") {\n // A one-shot leaves once its command is done: by default it stays until the message went out.\n params.wait = parsed.options.wait ?? (s.mode === \"one-shot\" ? (spec.message ? \"confirmed\" : \"sent\") : \"none\");\n if (parsed.options.timeout !== undefined) params.timeout = parsed.options.timeout;\n } else {\n // A group has no receipts: `--wait sent` waits until an edge took it. None by default.\n if (parsed.options.wait !== undefined) params.wait = parsed.options.wait;\n if (parsed.options.timeout !== undefined) params.timeout = parsed.options.timeout;\n }\n return s.call(spec.method, params);\n }, spec.target === \"chat\"));\n}\n\n/**\n * `identity add`: a proof. An in-app signer finishes in this one command (what it waits on is printed to stderr as it\n * comes); a tool or a published record answers with the statement, and `identity complete` finishes it later, in the\n * same daemon.\n */\nasync function identityAddCommand(argv: string[]): Promise<void> {\n const parsed = parseArgs(argv, {\n signer: { type: \"string\", description: \"\" }, field: { type: \"list\", description: \"\" }, days: { type: \"number\", description: \"\" }, timeout: { type: \"number\", description: \"\" },\n });\n const g = globals(parsed);\n pretty = g.pretty;\n const [provider, ...subject] = parsed.positionals;\n if (!provider) throw new CliError(\"usage\", \"ghostly identity add <provider> [subject] [--signer id] [--field name=value]... [--days n]\");\n const fields: Record<string, string> = {};\n for (const item of (parsed.options.field as string[] | undefined) ?? []) {\n const eq = item.indexOf(\"=\");\n if (eq < 1) throw new CliError(\"usage\", `--field takes name=value, not ${JSON.stringify(item)}`);\n fields[item.slice(0, eq)] = item.slice(eq + 1);\n }\n const params = { provider, subject: subject.join(\" \") || undefined, signer: parsed.options.signer, fields, days: parsed.options.days, timeout: parsed.options.timeout };\n const tell = (event: GhostlyEvent) => {\n if (event.type === \"identity.approval\" || event.type === \"identity.progress\") process.stderr.write(JSON.stringify(event) + \"\\n\");\n };\n requireProfile(g);\n const client = await connectDaemon(g.paths.socket);\n if (client) {\n await warnVersion(client);\n const watcher = await connectDaemon(g.paths.socket);\n await watcher?.subscribe(undefined, tell);\n try { print(await client.call(\"identity.add\", params)); } finally { client.close(); watcher?.close(); }\n return;\n }\n const host = await openHost(g.paths, \"one-shot\", VERSION);\n const off = host.ctx.hub.onEvent(tell);\n try {\n const result = await callApi(host.ctx, \"identity.add\", params) as { done: boolean; draft?: string };\n if (!result.done) {\n await callApi(host.ctx, \"identity.cancel\", { draft: result.draft }).catch(() => {});\n throw new CliError(\"unavailable\", \"A proof made in two steps waits in the daemon between them: start `ghostly daemon --detach`, then run this again\");\n }\n print(result);\n } finally { off(); await host.close(); }\n}\n\nasync function tableCommand(name: string, argv: string[]): Promise<void> {\n const command = COMMANDS[name];\n const parsed = parseArgs(argv, command.options ?? {}, idSlot(command));\n const g = globals(parsed);\n pretty = g.pretty;\n const args = positionals(command, parsed.positionals);\n if (command.run) {\n const result = await command.run(parsed, args);\n if (typeof result.warning === \"string\") { process.stderr.write(`ghostly: ${result.warning}\\n`); delete result.warning; }\n print(result);\n return;\n }\n const params = command.params?.(parsed, args) ?? {};\n for (const key of Object.keys(params)) if (params[key] === undefined) delete params[key];\n // Evidence is read here, where the file is: the daemon runs elsewhere.\n if (command.method === \"identity.complete\") {\n if (params.evidenceFile) params.evidence = readFileSync(params.evidenceFile as string, \"utf8\");\n else if (params.stdin) params.evidence = await readStdin();\n delete params.evidenceFile; delete params.stdin;\n }\n const secretWarning = await secretsFromStdin(command.method, params, readStdin);\n if (secretWarning) process.stderr.write(`ghostly: ${secretWarning}\\n`);\n const result = await withSession(g, async (s) => {\n const result = await s.call(command.method, params) as Record<string, unknown>;\n // A one-shot join leaves once the contact can be reached: its answer has to be out first.\n if (command.method === \"invite.join\" && s.mode === \"one-shot\") {\n await s.call(\"chat.wait\", { chat: result.chat, until: \"paired\", timeout: 60 }).catch(() => {});\n }\n return result;\n }, chatOnly(command.method, params));\n // Done, with something to know (a voice note sent without its waveform): said on stderr, the JSON stays the answer.\n if (typeof result?.warning === \"string\") {\n process.stderr.write(`ghostly: ${result.warning}\\n`);\n delete result.warning;\n }\n print(result);\n}\n\n/**\n * `call pipe`: a call's audio socket bridged to stdin and stdout, for shell pipelines. stdout carries audio only (the\n * contact's, raw PCM); what the command says goes to stderr. It ends when the call does.\n */\nasync function callPipeCommand(argv: string[]): Promise<void> {\n const parsed = parseArgs(argv, {}, idSlot({ args: [\"call...?\"] }));\n const g = globals(parsed);\n const ref = parsed.positionals.join(\" \") || undefined;\n const client = await connectDaemon(g.paths.socket);\n if (!client) throw new CliError(\"unavailable\", `No daemon runs profile ${g.profile}: calls need one (ghostly daemon --detach)`);\n let call: { call: string; audio: { socket: string; rate: number } | null };\n try { call = await client.call(\"call.get\", ref ? { call: ref } : {}) as typeof call; } finally { client.close(); }\n const audio = call.audio;\n if (!audio) throw new CliError(\"unavailable\", \"That call has no audio yet: answer it first\");\n const socket = connect(audio.socket);\n await new Promise<void>((resolve, reject) => { socket.once(\"connect\", resolve); socket.once(\"error\", reject); });\n process.stderr.write(`ghostly: call ${call.call}: s16le mono ${audio.rate} Hz on stdin/stdout\\n`);\n // A clip that ends (`ffmpeg -t 3 … | ghostly call pipe`) is queued and played, and the contact's audio goes on\n // coming: the end of stdin never closes the socket, whose daemon side would close the call's audio with it.\n process.stdin.pipe(socket, { end: false });\n socket.pipe(process.stdout);\n socket.on(\"close\", () => exit(0));\n socket.on(\"error\", () => exit(1));\n}\n\n// ---------- entry ----------\n\n/** The command's words when the line asks for help (`help <words>`, or `--help`/`-h` before any `--`), else null. */\nfunction helpAsked(argv: readonly string[]): string[] | null {\n const end = argv.indexOf(\"--\");\n const head = end === -1 ? argv : argv.slice(0, end);\n const words = (list: readonly string[]) => { const out: string[] = []; for (const word of list) { if (word.startsWith(\"-\")) break; out.push(word); } return out; };\n if (head[0] === \"help\") return words(head.slice(1));\n if (head.includes(\"--help\") || head.includes(\"-h\")) return words(head);\n return null;\n}\n\nexport async function main(input: string[]): Promise<number> {\n const argv = liftGlobals(input);\n // Global options alone (`ghostly --pretty`, `ghostly -p bot`) name no command: lifted, one of them comes first.\n const helpFor = argv.length === 0 || globalOption(argv[0]) ? [] : helpAsked(argv);\n if (helpFor) { process.stdout.write((helpFor.length ? commandHelp(helpFor) : help()) + \"\\n\"); return 0; }\n if (argv[0] === \"--version\" || argv[0] === \"version\") {\n // Global options come after the command once lifted: `--pretty` indents this answer too.\n pretty = argv.slice(1).some((word) => word === \"--pretty\" || word === \"--pretty=true\");\n print({ version: VERSION });\n return 0;\n }\n const [first, second] = argv;\n const two = second && !second.startsWith(\"-\") ? `${first} ${second}` : \"\";\n // A command is one its table lists itself: every object has `constructor` and `toString`, which are no commands.\n const text = (name: string) => Object.hasOwn(TEXT_COMMANDS, name), table = (name: string) => Object.hasOwn(COMMANDS, name);\n if (first === \"profile\" && !table(two)) { await profileCommand(second, argv.slice(2)); return 0; }\n if (first === \"daemon\") { await daemonCommand(argv.slice(1)); return -1; }\n if (first === \"listen\") { await listenCommand(argv.slice(1)); return -1; }\n if (first === \"engine\") { await engineCommand(argv.slice(1)); return 0; }\n if (first === \"settings\") { await settingsCommand(second, argv.slice(2)); return 0; }\n if (two === \"identity add\") { await identityAddCommand(argv.slice(2)); return 0; }\n if (two === \"call pipe\") { await callPipeCommand(argv.slice(2)); return -1; }\n if (two && text(two)) { await textCommand(two, argv.slice(2)); return 0; }\n if (two && table(two)) { await tableCommand(two, argv.slice(2)); return 0; }\n if (text(first)) { await textCommand(first, argv.slice(1)); return 0; }\n if (table(first)) { await tableCommand(first, argv.slice(1)); return 0; }\n throw unknownCommand(first, two ? second : undefined);\n}\n\n/**\n * A command line that names no command. The first word of a group (`ghostly chat`, `ghostly wallet frob`) is answered\n * with the group's commands, as `ghostly help chat` lists them; before, it was only \"Unknown command: chat\".\n */\nexport function unknownCommand(first: string | undefined, second?: string): CliError {\n const asked = [first, second].filter(Boolean).join(\" \");\n const subs = [...new Set(helpRows().map((row) => commandWords(row.usage)).filter((words) => words[0] === first && words.length > 1).map((words) => words[1]))];\n if (!subs.length) return new CliError(\"usage\", `Unknown command: ${asked} (ghostly help)`);\n const list = `${subs.slice(0, -1).join(\", \")}${subs.length > 1 ? \" or \" : \"\"}${subs.at(-1)}`;\n return new CliError(\"usage\", second ? `Unknown command: ${asked}: ghostly ${first} takes ${list} (ghostly help ${first})` : `ghostly ${first} takes a command: ${list} (ghostly help ${first})`);\n}\n\n/** Leaves once what was printed reached stdout (a pipe drains asynchronously). */\nfunction exit(code: number): void {\n process.stdout.write(\"\", () => process.exit(code));\n}\n\n/**\n * Stdout is the command's answer and nothing else: one JSON object, or a stream of JSON lines. What the engine notes\n * as it works (a relay left alone for a minute, a wallet moved to its network's key) is written with `console.info`\n * and the like, which Node sends to stdout: a script reading the answer (`ghostly wallet list | jq`) then failed on a\n * line that is not JSON. Those notes go to stderr, where a daemon's log and a terminal still show them.\n */\nexport function notesToStderr(): void {\n for (const level of [\"log\", \"info\", \"debug\"] as const) console[level] = (...args: unknown[]) => { process.stderr.write(`${format(...args)}\\n`); };\n}\n\n/** Runs the command line; `-1` from `main` means the process stays (a daemon, a listener). */\nexport async function run(argv: string[]): Promise<void> {\n notesToStderr();\n try {\n const code = await main(argv);\n if (code >= 0) exit(code);\n } catch (error) {\n const failure = asCliError(error);\n print({ error: failure.toJSON() });\n exit(EXIT[failure.code]);\n }\n}\n","/**\n * Node 25 and newer define `globalThis.localStorage` as a getter that, without `--localstorage-file`, answers\n * `undefined` and writes an ExperimentalWarning on stderr the first time it is read. The engine reads it where a\n * browser keeps a setting (`globalThis.localStorage?.getItem(...)`), so every command that opens a profile printed the\n * warning. Without the flag the CLI has no localStorage, as on Node 22: the getter is replaced by `undefined` before\n * anything reads it. With the flag, Node's storage stays.\n */\nexport function quietWebStorage(scope: object = globalThis, execArgv: readonly string[] = process.execArgv, nodeOptions: string | undefined = process.env.NODE_OPTIONS): void {\n const own = Object.getOwnPropertyDescriptor(scope, \"localStorage\");\n if (!own?.get || !own.configurable) return;\n const flag = (arg: string) => arg === \"--localstorage-file\" || arg.startsWith(\"--localstorage-file=\");\n if (execArgv.some(flag) || (nodeOptions ?? \"\").split(/\\s+/).some(flag)) return;\n Object.defineProperty(scope, \"localStorage\", { value: undefined, writable: true, configurable: true, enumerable: false });\n}\n","#!/usr/bin/env node\nimport { run } from \"./main\";\nimport { quietWebStorage } from \"./runtime/webStorage\";\n\n// Before any command reads it: Node 25+ warns on stderr at the first read of a localStorage it was given no file for.\nquietWebStorage();\nvoid run(process.argv.slice(2));\n"],"mappings":"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"}
1
+ {"version":3,"file":"ghostly.mjs","names":[],"sources":["../src/runtime/webStorage.ts","../src/bin.ts"],"sourcesContent":["/**\n * Node 25 and newer define `globalThis.localStorage` as a getter that, without `--localstorage-file`, answers\n * `undefined` and writes an ExperimentalWarning on stderr the first time it is read. The engine reads it where a\n * browser keeps a setting (`globalThis.localStorage?.getItem(...)`), so every command that opens a profile printed the\n * warning. Without the flag the CLI has no localStorage, as on Node 22: the getter is replaced by `undefined` before\n * anything reads it. With the flag, Node's storage stays.\n */\nexport function quietWebStorage(scope: object = globalThis, execArgv: readonly string[] = process.execArgv, nodeOptions: string | undefined = process.env.NODE_OPTIONS): void {\n const own = Object.getOwnPropertyDescriptor(scope, \"localStorage\");\n if (!own?.get || !own.configurable) return;\n const flag = (arg: string) => arg === \"--localstorage-file\" || arg.startsWith(\"--localstorage-file=\");\n if (execArgv.some(flag) || (nodeOptions ?? \"\").split(/\\s+/).some(flag)) return;\n Object.defineProperty(scope, \"localStorage\", { value: undefined, writable: true, configurable: true, enumerable: false });\n}\n","#!/usr/bin/env node\nimport { enableCompileCache } from \"node:module\";\nimport { quietWebStorage } from \"./runtime/webStorage\";\n\n// Before any command reads it: Node 25+ warns on stderr at the first read of a localStorage it was given no file for.\nquietWebStorage();\n// Node keeps the compiled code of the CLI's modules on disk (its own cache folder), so the next command parses none of\n// it again: about 30 ms of every command. Before the CLI's own modules load, which is why main comes in after it.\nenableCompileCache();\nconst { run } = await import(\"./main\");\nvoid run(process.argv.slice(2));\n"],"mappings":";;;;;;;;;;AAOA,SAAgB,gBAAgB,QAAgB,YAAY,WAA8B,QAAQ,UAAU,cAAkC,QAAQ,IAAI,cAAoB;CAC5K,MAAM,MAAM,OAAO,yBAAyB,OAAO,cAAc;CACjE,IAAI,CAAC,KAAK,OAAO,CAAC,IAAI,cAAc;CACpC,MAAM,QAAQ,QAAgB,QAAQ,yBAAyB,IAAI,WAAW,sBAAsB;CACpG,IAAI,SAAS,KAAK,IAAI,MAAM,eAAe,GAAA,CAAI,MAAM,KAAK,CAAC,CAAC,KAAK,IAAI,GAAG;CACxE,OAAO,eAAe,OAAO,gBAAgB;EAAE,OAAO,KAAA;EAAW,UAAU;EAAM,cAAc;EAAM,YAAY;CAAM,CAAC;AAC1H;;;ACRA,gBAAgB;AAGhB,mBAAmB;AACnB,IAAM,EAAE,QAAQ,MAAM,OAAO;AACxB,IAAI,QAAQ,KAAK,MAAM,CAAC,CAAC"}