@ghostlytools/cli 1.3.5 → 1.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{api-CgPyYfjH.js → api-Br3msjtR.js} +2 -2
- package/dist/assets/{api-Do7NjRg2.js → api-z9mlywrn.js} +9 -9
- package/dist/assets/{api-Do7NjRg2.js.map → api-z9mlywrn.js.map} +1 -1
- package/dist/assets/{apiKit-eh5qBKIl.js → apiKit-lX3Xx_hY.js} +2 -2
- package/dist/assets/{apiKit-eh5qBKIl.js.map → apiKit-lX3Xx_hY.js.map} +1 -1
- package/dist/assets/{apps-Cl9MtELY.js → apps-BUK6bEKW.js} +2 -2
- package/dist/assets/{apps-Cl9MtELY.js.map → apps-BUK6bEKW.js.map} +1 -1
- package/dist/assets/{apps-B83dRFga.js → apps-Dn7rCafG.js} +1 -1
- package/dist/assets/{arkade-B5Xn40bC.js → arkade-uqAXA5BT.js} +2 -2
- package/dist/assets/{arkade-B5Xn40bC.js.map → arkade-uqAXA5BT.js.map} +1 -1
- package/dist/assets/{backup-BjZcDFjL.js → backup-CJQK5Miu.js} +4 -4
- package/dist/assets/{backup-BjZcDFjL.js.map → backup-CJQK5Miu.js.map} +1 -1
- package/dist/assets/{backup-CDOh2tuw.js → backup-_JnqeFZb.js} +1 -1
- package/dist/assets/buttons-DDB9Xdha.js +2 -0
- package/dist/assets/{buttons-CUKHafux.js → buttons-DDvTuViY.js} +3 -3
- package/dist/assets/{buttons-CUKHafux.js.map → buttons-DDvTuViY.js.map} +1 -1
- package/dist/assets/{envelope-wspXCjle.js → envelope-BqvBb6hC.js} +2 -2
- package/dist/assets/{envelope-wspXCjle.js.map → envelope-BqvBb6hC.js.map} +1 -1
- package/dist/assets/{groupFiles-Cfrjo3YS.js → groupFiles-B1lL6wch.js} +3 -3
- package/dist/assets/{groupFiles-Cfrjo3YS.js.map → groupFiles-B1lL6wch.js.map} +1 -1
- package/dist/assets/{host-BdS-7nRW.js → host-DFzlKU3P.js} +8 -8
- package/dist/assets/{host-BdS-7nRW.js.map → host-DFzlKU3P.js.map} +1 -1
- package/dist/assets/{hyperdhtRelay-H97Vz73W.js → hyperdhtRelay-DwlOY6aD.js} +2 -2
- package/dist/assets/{hyperdhtRelay-H97Vz73W.js.map → hyperdhtRelay-DwlOY6aD.js.map} +1 -1
- package/dist/assets/{irohWeb-jErC-V0J.js → irohWeb-DZDCyL9b.js} +2 -2
- package/dist/assets/{irohWeb-jErC-V0J.js.map → irohWeb-DZDCyL9b.js.map} +1 -1
- package/dist/assets/{main-C14lA5aK.js → main-DgbdDDif.js} +14 -14
- package/dist/assets/{main-C14lA5aK.js.map → main-DgbdDDif.js.map} +1 -1
- package/dist/assets/{mainline-CYI1tZT7.js → mainline-BHE5-R3A.js} +2 -2
- package/dist/assets/{mainline-CYI1tZT7.js.map → mainline-BHE5-R3A.js.map} +1 -1
- package/dist/assets/{networkReason-CiHrd-kG.js → networkReason-Btv4b2C4.js} +2 -2
- package/dist/assets/{networkReason-CiHrd-kG.js.map → networkReason-Btv4b2C4.js.map} +1 -1
- package/dist/assets/{nwc-0JYm8Vys.js → nwc-z0pa5zql.js} +3 -3
- package/dist/assets/{nwc-0JYm8Vys.js.map → nwc-z0pa5zql.js.map} +1 -1
- package/dist/assets/{nwcProvider-2coJAsqr.js → nwcProvider-C1AhPoFW.js} +2 -2
- package/dist/assets/{nwcProvider-2coJAsqr.js.map → nwcProvider-C1AhPoFW.js.map} +1 -1
- package/dist/assets/{registry-sML83UvL.js → registry-C09xXyx-.js} +1 -1
- package/dist/assets/{registry-BptCF3tL.js → registry-JrnwUWmh.js} +3 -3
- package/dist/assets/{registry-BptCF3tL.js.map → registry-JrnwUWmh.js.map} +1 -1
- package/dist/assets/{resolve-2hGrvTen.js → resolve-Ssq1nDy4.js} +2 -2
- package/dist/assets/{resolve-2hGrvTen.js.map → resolve-Ssq1nDy4.js.map} +1 -1
- package/dist/assets/{server-BbPiCghe.js → server-D6FSznK5.js} +19 -13
- package/dist/assets/server-D6FSznK5.js.map +1 -0
- package/dist/assets/{src-BLzG4Sfv.js → src-XhjSWnp-.js} +7 -2
- package/dist/assets/{src-BLzG4Sfv.js.map → src-XhjSWnp-.js.map} +1 -1
- package/dist/assets/{turnRefs-ClWSgjb0.js → turnRefs-Cle33IoV.js} +2 -2
- package/dist/assets/{turnRefs-ClWSgjb0.js.map → turnRefs-Cle33IoV.js.map} +1 -1
- package/dist/assets/{usdt-zwyn_AQK.js → usdt-DO86yko2.js} +3 -3
- package/dist/assets/{usdt-zwyn_AQK.js.map → usdt-DO86yko2.js.map} +1 -1
- package/dist/ghostly.mjs +1 -1
- package/package.json +1 -1
- package/dist/assets/buttons-CFabREDv.js +0 -2
- package/dist/assets/server-BbPiCghe.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buttons-CUKHafux.js","names":[],"sources":["../../../../apps/ui/src/lib/parse/secrets.ts","../../src/waits.ts","../../src/buttons.ts"],"sourcesContent":["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","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 { 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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"buttons-DDvTuViY.js","names":[],"sources":["../../../../apps/ui/src/lib/parse/secrets.ts","../../src/waits.ts","../../src/buttons.ts"],"sourcesContent":["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","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 { 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"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "./src-
|
|
1
|
+
import "./src-XhjSWnp-.js";
|
|
2
2
|
//#region ../browser/src/backup/codec.ts
|
|
3
3
|
function fromBase64Url(text) {
|
|
4
4
|
if (!/^[A-Za-z0-9_-]*$/.test(text)) throw new Error("Invalid base64url");
|
|
@@ -87,4 +87,4 @@ async function open(text, passphrase) {
|
|
|
87
87
|
//#endregion
|
|
88
88
|
export { fromBase64Url as i, MAX_BACKUP_BYTES as n, open as r, BACKUP_MEDIA_TYPE as t };
|
|
89
89
|
|
|
90
|
-
//# sourceMappingURL=envelope-
|
|
90
|
+
//# sourceMappingURL=envelope-BqvBb6hC.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"envelope-wspXCjle.js","names":[],"sources":["../../../browser/src/backup/codec.ts","../../../browser/src/backup/envelope.ts"],"sourcesContent":["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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"envelope-BqvBb6hC.js","names":[],"sources":["../../../browser/src/backup/codec.ts","../../../browser/src/backup/envelope.ts"],"sourcesContent":["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"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { $n as GROUP_FILE_LIMITS, Cl as randomBytes, Da as RateWindow, Fn as resolveLightningDestination, Ga as replySnippet, In as decodeBolt11, Lr as ChatFiles, Pn as requestLnurlInvoice, Xn as engineError, Zn as engineText, ar as isGroupMessageId, cr as readGroupWant, er as GROUP_HAVE_FRAME, fn as WALLET_NETWORKS, ir as groupFileFallback, lr as readGroupWantNo, nr as GROUP_WANT_NO_FRAME, rr as fetchesByItself, sr as readGroupHave, vn as walletNetworkOf, wl as toBase64Url, ya as fileMessageText, zr as transferEnded } from "./src-
|
|
1
|
+
import { $n as GROUP_FILE_LIMITS, Cl as randomBytes, Da as RateWindow, Fn as resolveLightningDestination, Ga as replySnippet, In as decodeBolt11, Lr as ChatFiles, Pn as requestLnurlInvoice, Xn as engineError, Zn as engineText, ar as isGroupMessageId, cr as readGroupWant, er as GROUP_HAVE_FRAME, fn as WALLET_NETWORKS, ir as groupFileFallback, lr as readGroupWantNo, nr as GROUP_WANT_NO_FRAME, rr as fetchesByItself, sr as readGroupHave, vn as walletNetworkOf, wl as toBase64Url, ya as fileMessageText, zr as transferEnded } from "./src-XhjSWnp-.js";
|
|
2
2
|
import { a as describeProvider, c as offeredIn, d as sourceProblem, n as PROVIDER_PLATFORMS, o as isNothingSpentError, s as networkMode, t as NothingSpentError, u as redact } from "./types-C4_KafL_.js";
|
|
3
3
|
import { c as fileStore, f as store, i as STORES, m as wrap, o as databaseName, p as transact } from "./idb-B5L8wuOm.js";
|
|
4
|
-
import { a as sealSeed, i as newDeviceKey, o as unsealSeed, t as networkReason } from "./networkReason-
|
|
4
|
+
import { a as sealSeed, i as newDeviceKey, o as unsealSeed, t as networkReason } from "./networkReason-Btv4b2C4.js";
|
|
5
5
|
import { t as BITCOIN_INVOICE_ON_TESTNET, u as mintNetwork } from "./mints-BrlY773j.js";
|
|
6
6
|
import { f as FILE_BYTES_STEP, o as fileBytes, s as fileBytesOf } from "./fileBytes-Di7VZtmU.js";
|
|
7
7
|
import { sha256 } from "@noble/hashes/sha2.js";
|
|
@@ -3279,4 +3279,4 @@ function messageFile(kept) {
|
|
|
3279
3279
|
//#endregion
|
|
3280
3280
|
export { setSingleWriterGate as $, LightningCards as A, breezDatabaseInUse as B, FileAppender as C, streamStored as D, storedSize as E, ProviderSources as F, ModeChanged as G, dropBreezDatabase as H, sourceKey as I, networkLabel as J, ModeGate as K, CASHU_MINT_SOURCE as L, isRecoveryPhrase as M, normalizePhrase as N, waitingGroupFile as O, lightningPaying as P, setAwayWallets as Q, cashuMint as R, WALLET_TYPES as S, removeStored as T, iceServerProblem as U, breezDatabasesFor as V, iceServerSetting as W, refuseAway as X, awayFrom as Y, requireTurn as Z, crossNetwork as _, storedGroupFiles as a, walletInstances as b, receivedPairedReply as c, paymentAlias as d, paymentWireId as f, createTiming as g, createFailure as h, groupOfFile as i, cardsKey as j, CASHU_CARD as k, replyRef as l, assertConfirmedReal as m, groupFileId as n, groupReply as o, WALLET_NAMES as p, WrongNetworkError as q, groupFileText as r, pairedWireReply as s, GroupFileDesk as t, replyTo as u, paymentNetwork as v, readStored as w, MESSAGE_DETAILS_MAX_BYTES as x, paymentNetworksOf as y, breezDatabase as z };
|
|
3281
3281
|
|
|
3282
|
-
//# sourceMappingURL=groupFiles-
|
|
3282
|
+
//# sourceMappingURL=groupFiles-B1lL6wch.js.map
|