@ghostlytools/cli 1.3.1 → 1.3.3
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/README.md +6 -6
- package/dist/assets/{api-Dq2QXQDk.js → api-B1fADtqd.js} +65 -20
- package/dist/assets/api-B1fADtqd.js.map +1 -0
- package/dist/assets/{api-TiB5QSG9.js → api-Bg7bh_6E.js} +2 -2
- package/dist/assets/{apiKit-CA2x5GJF.js → apiKit-BsUZPHLn.js} +44 -33
- package/dist/assets/apiKit-BsUZPHLn.js.map +1 -0
- package/dist/assets/{apps-BxV9i_Y4.js → apps-BaUNt-5c.js} +91 -24
- package/dist/assets/apps-BaUNt-5c.js.map +1 -0
- package/dist/assets/{apps-C-T8jeji.js → apps-DepomCiX.js} +1 -1
- package/dist/assets/{arkade-C_qDmHnm.js → arkade-CNnMaoEQ.js} +4 -4
- package/dist/assets/arkade-CNnMaoEQ.js.map +1 -0
- package/dist/assets/{backup-Bugnn3Ue.js → backup-B5Q1W-Ey.js} +1 -1
- package/dist/assets/{backup-CnMN2oUK.js → backup-Bbz3lyC5.js} +6 -6
- package/dist/assets/{backup-CnMN2oUK.js.map → backup-Bbz3lyC5.js.map} +1 -1
- package/dist/assets/buttons-CUd7j0ZA.js +2 -0
- package/dist/assets/{buttons-CvNBzh2S.js → buttons-DS1iURp8.js} +4 -4
- package/dist/assets/{buttons-CvNBzh2S.js.map → buttons-DS1iURp8.js.map} +1 -1
- package/dist/assets/{envelope-WjingYSc.js → envelope-fNC941z4.js} +2 -2
- package/dist/assets/{envelope-WjingYSc.js.map → envelope-fNC941z4.js.map} +1 -1
- package/dist/assets/{errors-DO5z6h_l.js → errors-B-zMxbcd.js} +6 -5
- package/dist/assets/{errors-DO5z6h_l.js.map → errors-B-zMxbcd.js.map} +1 -1
- package/dist/assets/{ghostly_iroh_web-Dvzh3Y6E.js → ghostly_iroh_web-BsAEFIjl.js} +30 -36
- package/dist/assets/ghostly_iroh_web-BsAEFIjl.js.map +1 -0
- package/dist/assets/ghostly_iroh_web_bg.wasm +0 -0
- package/dist/assets/{groupFiles-IEUW3DQz.js → groupFiles-6qnLCnOm.js} +82 -29
- package/dist/assets/groupFiles-6qnLCnOm.js.map +1 -0
- package/dist/assets/{host-CWt-jtqz.js → host-Dj3C1fn3.js} +305 -68
- package/dist/assets/host-Dj3C1fn3.js.map +1 -0
- package/dist/assets/{hyperdhtRelay-CPYwSLLS.js → hyperdhtRelay-CrgYQN-g.js} +2 -2
- package/dist/assets/{hyperdhtRelay-CPYwSLLS.js.map → hyperdhtRelay-CrgYQN-g.js.map} +1 -1
- package/dist/assets/{irohWeb-4oChIOIp.js → irohWeb-DSyLp5Re.js} +3 -3
- package/dist/assets/{irohWeb-4oChIOIp.js.map → irohWeb-DSyLp5Re.js.map} +1 -1
- package/dist/assets/{main-DbqcdShN.js → main-BDd11Yd1.js} +159 -54
- package/dist/assets/main-BDd11Yd1.js.map +1 -0
- package/dist/assets/{mainline-ZkiOnevs.js → mainline-CLTo0nGa.js} +2 -2
- package/dist/assets/{mainline-ZkiOnevs.js.map → mainline-CLTo0nGa.js.map} +1 -1
- package/dist/assets/{networkReason-CetKkTPo.js → networkReason-DwcadZz5.js} +2 -2
- package/dist/assets/{networkReason-CetKkTPo.js.map → networkReason-DwcadZz5.js.map} +1 -1
- package/dist/assets/{nwc-CgkqyvsE.js → nwc-BNFyyECH.js} +3 -3
- package/dist/assets/{nwc-CgkqyvsE.js.map → nwc-BNFyyECH.js.map} +1 -1
- package/dist/assets/{nwcProvider-4eVz_8HP.js → nwcProvider-BbjJOfLD.js} +2 -2
- package/dist/assets/{nwcProvider-4eVz_8HP.js.map → nwcProvider-BbjJOfLD.js.map} +1 -1
- package/dist/assets/{profiles-BHc9pEYg.js → profiles-gPCjLOQq.js} +2 -2
- package/dist/assets/{profiles-BHc9pEYg.js.map → profiles-gPCjLOQq.js.map} +1 -1
- package/dist/assets/{registry-DlAKO0Fy.js → registry-CoLHpxkb.js} +17 -32
- package/dist/assets/registry-CoLHpxkb.js.map +1 -0
- package/dist/assets/{registry-OBJBnHVQ.js → registry-DoTb6uEr.js} +1 -1
- package/dist/assets/{resolve-CouHhCpJ.js → resolve-BIDJ-UUl.js} +18 -3
- package/dist/assets/resolve-BIDJ-UUl.js.map +1 -0
- package/dist/assets/{server-Ss0gJ-nw.js → server-Bx3RGkBC.js} +380 -116
- package/dist/assets/server-Bx3RGkBC.js.map +1 -0
- package/dist/assets/{src-DDzSERyc.js → src-D7gcJElT.js} +470 -242
- package/dist/assets/src-D7gcJElT.js.map +1 -0
- package/dist/assets/{turnRefs-B87dtrHM.js → turnRefs-PN644M9o.js} +2 -2
- package/dist/assets/{turnRefs-B87dtrHM.js.map → turnRefs-PN644M9o.js.map} +1 -1
- package/dist/assets/{usdt-Fj8CQO2z.js → usdt-B2TLRlYa.js} +3 -3
- package/dist/assets/{usdt-Fj8CQO2z.js.map → usdt-B2TLRlYa.js.map} +1 -1
- package/dist/ghostly.mjs +1 -1
- package/package.json +1 -1
- package/dist/assets/api-Dq2QXQDk.js.map +0 -1
- package/dist/assets/apiKit-CA2x5GJF.js.map +0 -1
- package/dist/assets/apps-BxV9i_Y4.js.map +0 -1
- package/dist/assets/arkade-C_qDmHnm.js.map +0 -1
- package/dist/assets/buttons-Bj7Vf7Pn.js +0 -2
- package/dist/assets/ghostly_iroh_web-Dvzh3Y6E.js.map +0 -1
- package/dist/assets/groupFiles-IEUW3DQz.js.map +0 -1
- package/dist/assets/host-CWt-jtqz.js.map +0 -1
- package/dist/assets/main-DbqcdShN.js.map +0 -1
- package/dist/assets/registry-DlAKO0Fy.js.map +0 -1
- package/dist/assets/resolve-CouHhCpJ.js.map +0 -1
- package/dist/assets/server-Ss0gJ-nw.js.map +0 -1
- package/dist/assets/src-DDzSERyc.js.map +0 -1
package/README.md
CHANGED
|
@@ -93,8 +93,8 @@ The rows are in alphabetical order of their first command, and every command of
|
|
|
93
93
|
|---|---|
|
|
94
94
|
| `app init <dir> [--name <name>] [--title <title>] [--force]` | Start an app (WISP 1200, Apps), run here with no profile or daemon: a small working one in `<dir>` (made when missing), which `app publish <dir>` signs as it is. It writes `ghostly-app.json` (`view: chat`, the `chat` permission), an `index.html` that says hello to the same app on the contact's side, typed against `@ghostlytools/sdk/app`, and a `README.md` with build, publish, verify and how to list it in a store. The name is `--name` (a lowercase letter, then up to 31 lowercase letters, digits or hyphens), else the folder's (`My App` gives `my-app`); the title is `--title` (one line, up to 40 characters), else the name's. When one of the three files is there nothing is written and it is `refused` (`details.reason` `exists`, `details.files`), unless `--force`, which replaces those three only. Answers `{dir, name, title, files, replaced?, next}` |
|
|
95
95
|
| `app open <chat> <ref\|name> [--no-card]`, `app close <chat> <ref\|name>` | A served app in a chat, as a bot (WISP 1200, Apps; needs the daemon, and `app serve` first). `app open` says the app is open on this side, in the version served, and sends the contact the app's card ("opened Chess", with what their app needs to get the same app and check it), unless their app is open in that chat already or `--no-card`. The two sides talk once both opened it; while the app stays open here, the engine says so again on every session that comes back. It answers `{chat, app, version, card, peer, live}`: `card` is the card's message id or null, `peer` the version the contact has open or null. A card that cannot go leaves nothing open. `app close` ends it: `{chat, app, closed}`. What is open is kept in memory: a restarted daemon opens again. `unavailable` in a one-shot, or on a daemon that started before the first app was served (`daemon restart`); `not_found` for an app not served; `refused`, in the engine's words, for a chat that is not a paired 1:1 chat |
|
|
96
|
-
| `app publish <dir> --key <file> [--out <file>] [--sequence n]`, `app verify <bundle\|url>` | The publisher tools of WISP 1200 (Apps), run here with no profile or daemon. `app publish` bundles and signs an app: `<dir>/ghostly-app.json` holds the manifest's fields but `publisher`, `sequence` and `files`, and every other file in `<dir>` goes in (dot files are left out and listed in `skipped`). It writes `<dir>/app.ghostlyapp` (or `--out`), its `sequence` one more than the bundle already there, which only the same key may follow (`--sequence n` sets it, refused unless higher). `--key` names the publisher key file, a PKCS #8 PEM: made owner-only on first use (back it up: an app is updated only with its key), refused when others may read it or when it is inside `<dir>`, never printed; a folder holding any private key file is refused (`details.reason` `private-key`). `app verify` checks a bundle from a file or an `https` URL as a client does (jsDelivr only at a full commit, 16 MiB at most) and answers its manifest and digest, or `refused` (exit 1) with the WISP's reason in `details.reason` |
|
|
97
|
-
| `app revoke <dir> --key <file> (--digest <digest>… \| --up-to <sequence>) [--reason <text>] [--bundle <file>]` | Revoke versions of your app (WISP 1200 · Revocation): by digest (base64url, as `app verify` prints it; `--digest` again for each, up to 64) or every version up to a `sequence`, with an optional one-line reason. The statement names the app of `<dir>/app.ghostlyapp` (or `--bundle`), is signed with that app's publisher key (any other key is refused, and none is made here), and is added to `<dir>/ghostly-revoke.json`, published beside the bundle; a client that reads it stops those versions. Answers `{app, digests \| upTo, current, added, revocations, file}`; `current` says whether the bundle in `<dir>` is among them |
|
|
96
|
+
| `app publish <dir> --key <file> [--out <file>] [--sequence n]`, `app verify <bundle\|url>` | The publisher tools of WISP 1200 (Apps), run here with no profile or daemon. `app publish` bundles and signs an app: `<dir>/ghostly-app.json` holds the manifest's fields but `publisher`, `sequence` and `files`, and every other file in `<dir>` goes in (dot files are left out and listed in `skipped`). It writes `<dir>/app.ghostlyapp` (or `--out`), its `sequence` one more than the bundle already there, which only the same key may follow (`--sequence n` sets it, refused unless higher); a sequence that `<dir>/ghostly-revoke.json` already revokes is `refused` (`details.reason` `revoked`, `details.sequence` the first one it leaves alone). `--key` names the publisher key file, a PKCS #8 PEM: made owner-only on first use (back it up: an app is updated only with its key), refused when others may read it or when it is inside `<dir>`, never printed; a folder holding any private key file is refused (`details.reason` `private-key`). `app verify` checks a bundle from a file or an `https` URL as a client does (jsDelivr only at a full commit, 16 MiB at most) and answers its manifest and digest, or `refused` (exit 1) with the WISP's reason in `details.reason` |
|
|
97
|
+
| `app revoke <dir> --key <file> (--digest <digest>… \| --up-to <sequence>) [--reason <text>] [--bundle <file>] [--force]` | Revoke versions of your app (WISP 1200 · Revocation): by digest (base64url, as `app verify` prints it; `--digest` again for each, up to 64) or every version up to a `sequence`, with an optional one-line reason. Clients keep a revocation for good, so `--up-to` past the bundle's own sequence is a usage error unless `--force` (versions published from elsewhere), and `app publish` refuses a sequence the folder's `ghostly-revoke.json` already stops, naming the `--sequence` that clears it. The statement names the app of `<dir>/app.ghostlyapp` (or `--bundle`), is signed with that app's publisher key (any other key is refused, and none is made here), and is added to `<dir>/ghostly-revoke.json`, published beside the bundle; a client that reads it stops those versions. Answers `{app, digests \| upTo, current, added, revocations, file}`; `current` says whether the bundle in `<dir>` is among them |
|
|
98
98
|
| `app send <chat> <ref\|name> <json\|->` | One message of a served app's own protocol to the same app on the contact's side (WISP 1200, Apps; needs the daemon): any JSON value, on the command line or `-` for stdin, at most 32 KiB, at most 48 a second. It goes on the live session only, while both sides have the app open, and is never kept for later: `{chat, app, sent: true}`, or an error whose `details.reason` is the WISP's name for why it did not go: `not-open` (`refused`: `app open` first), `too-large` (`bad_request`), `offline` and `peer-closed` (`unavailable`: the chat is not live, or the contact's app is not open; wait for `app.opened`), `too-fast` (`refused`). The contact's messages come as `app.message` events |
|
|
99
99
|
| `app serve <bundle\|url> --grant chat [--grant name] [--url <url>]`, `app served`, `app unserve <ref\|name>` | Serve an app as a bot (WISP 1200, Apps): the profile speaks the app's messages in its chats itself, and no app runs here. `app serve` reads the signed bundle from a file or an `https` URL and checks it as `app verify` does (`refused` with the WISP's reason in `details.reason`). Each permission the manifest asks needs its `--grant`: `chat` (talk to the same app on the contact's side) and `name` (the profile's name in that chat); one left out is `confirm` (exit 5), naming it in `details.missing`. `internet` cannot be granted (no page runs here), a permission the app does not ask is `bad_request`, and an app that does not ask for `chat` is `refused`. A newer version replaces the one served; an older one, or another bundle under the same `sequence`, is `refused`. It answers `{served: {ref, title, version, sequence, digest, url?, permissions, granted, servedAt}, offered, restart?}`; `url` is where a contact's app reads the bundle (`--url`, else the URL served from, else the manifest's first source). The list is kept in the profile's folder (`apps.json`). A profile's engine offers `apps/1` to contacts only while it serves an app, and reads the list when it starts: `offered` says whether it does now, and `restart: true` that `daemon restart` is needed for it. `app served` answers `{apps, offered, restart?}`; `app unserve` removes one by its reference or its name |
|
|
100
100
|
| `button press <chat\|group> <message> <button> [--wait none\|sent\|delivered] [--timeout s]` | Press a button of someone else's question, as a tap in the app does (WISP 406 · Message Buttons): the reply carries the button's label, and the asker hears `button.pressed`. `<message>` is the question's id (its event or `chat history`), `<button>` a button's id from its `card`. Refused (exit 1) like a tap: closed buttons, a second answer past `--once`, a second press within a second. A one-shot in a chat waits until it went out, as `send`. Answers `{chat \| group, messageId, button, label, replyId, delivery \| edges}` |
|
|
@@ -123,7 +123,7 @@ The rows are in alphabetical order of their first command, and every command of
|
|
|
123
123
|
| `file send <chat\|group> <path> [--name n] [--mime t] [--voice [ms] [--peaks …]] [--reply <message>]` | A file, or a voice note (its length and waveform measured from the file unless given); `--reply` quotes a message, as `send --reply` does. To a group (`group:<id>` when a chat has the same name), up to 100 MiB: every member sees it at once and each member's app fetches it from a member who has it (WISP 503) |
|
|
124
124
|
| `file wait [<chat>] <file> [--timeout s]` | Wait until a transfer ends: exit `0` when the file is all here, `1` with the transfer's error when it failed (`details.retry`: `file resend` can go on), `4` on timeout (default 300 s) |
|
|
125
125
|
| `forward <chat\|group> <message>… --to <chat\|group>… [--force] [--wait none\|sent] [--timeout s]` | Forward messages to up to 5 chats and groups (`--to` again for each; `group:<id>` names a group when a chat has the same name; an id that starts with a dash is taken as it is, `--to -Ab…` or `--to=-Ab…`). Each is a new message of yours that says it was forwarded (`forwarded`, the hop count) and nothing of who wrote it. Files go from the bytes this profile holds, never fetched again; a group takes texts only. A text that looks like a seed, a key or ecash needs `--force`. Answers `{from, results: [{to, kind, messageIds, error}]}`; any refusal fails the command (exit 1) with the results in `details`. `--wait sent`: each text on its way (a group's taken by an edge), each file's transfer done |
|
|
126
|
-
| `group create <name> [--mesh]`, `group join <link
|
|
126
|
+
| `group create <name> [--mesh]`, `group join [<link> \| -]` | A community (a link anyone can open), or a private mesh. `group join` reads the link from stdin when none is given or `-` (on the command line it shows in `ps` and the shell's history, and the CLI warns). `group join` with the link of a community the profile was removed from asks to be let in again, and keeps its history |
|
|
127
127
|
| `group edit <group> <message> [text… \| --text <text> \| --stdin] [--mention <member>]… [--force]` | Replace the text of a message you sent to the group (`<message>` is the `messageId` `group send` gave). Every member sees it in place, marked edited; mentions whose `@name` is still in the text stay, `--mention` adds more. Answers `{group, messageId, edits, sent, edges}`; `--wait sent` and `--timeout` as for `group send`. The text the message already has is no edit: `sent: false, unchanged: true`. A private group's members whose edge is down get it when it opens, a community's with the catch-up; a member on an older app keeps the old text |
|
|
128
128
|
| `group hub <group> <member> [--pin \| --exclude \| --auto]` | Past 16 members: pin a member as a hub, keep one from being a hub, or leave it to their app (a daemon offers itself) |
|
|
129
129
|
| `group invite <group> <chat>`, `group remove <group> <member>`, `group admin <group> <member>` | Membership, for the admin (each prints the group; `--show-secret` for its link) |
|
|
@@ -139,7 +139,7 @@ The rows are in alphabetical order of their first command, and every command of
|
|
|
139
139
|
| `identity providers`, `identity list` | Kinds of proof and their signers; this profile's proofs |
|
|
140
140
|
| `identity share\|withdraw <chat> <id>`, `identity contact <chat>`, `identity recheck <chat> <id>`, `identity remove <id>` | Show a proof to a contact; what a contact showed, as checked here |
|
|
141
141
|
| `invite create [--label <name>]` | A new chat's `ghostly1…` invite and its link |
|
|
142
|
-
| `invite join <invite-or-link> [--label <name>]` | Join a chat (your own invite is refused) |
|
|
142
|
+
| `invite join [<invite-or-link> \| -] [--label <name>]` | Join a chat (your own invite is refused). The invite comes from stdin when none is given or `-`, out of `ps` and the shell's history; on the command line the CLI warns |
|
|
143
143
|
| `lightning default <card>`, `lightning rename <card> <name>` | Lightning cards: which receives by default |
|
|
144
144
|
| `listen [--since seq] [--cursor file] [--type t]… [--turns] [--from <chat\|key>]… [--group <group>]… [--exec cmd] [--webhook url] [--print]` | The event stream; `--from` and `--group` an allowlist, `--turns` one `agent.turn` per message to answer ([Events](#events)) |
|
|
145
145
|
| `message retry\|delete\|details <chat> <message>` | One message |
|
|
@@ -157,12 +157,12 @@ The rows are in alphabetical order of their first command, and every command of
|
|
|
157
157
|
| `service peer <chat>`, `service open <chat> <service> [--port p]`, `service close <chat> <service>` | A contact's app on a loopback port here (daemon). `url` is a link for your browser, `http://<random>.localhost:<port>/.ghostly-open/<token>`: it sets the service's cookie and shows the app on a host name of its own; any other host name, or a request without that cookie, gets 404. Chrome, Edge and curl reach `*.localhost` names by themselves; Safari and Node's `fetch` on macOS may not (add the name to `/etc/hosts` for them) |
|
|
158
158
|
| `settings get [--show-secret]`, `settings set <key> <json>` | Relays, Iroh relays, the HyperDHT relay, ICE servers, `sendTyping` (false: contacts are never told you type), … |
|
|
159
159
|
| `status` | The profile, its chats, whether WebRTC and calls run, the last event seq |
|
|
160
|
-
| `store sign <index> --key <file> [--out <dir>]` | Sign a store index (WISP 1200 · Stores) with the store key file (kept offline; made when missing
|
|
160
|
+
| `store sign <index> --key <file> [--out <dir>]` | Sign a store index (WISP 1200 · Stores) with the store key file (kept offline; made when missing, the index names no `key` and no signed index is in the output folder): writes `ghostly-store.json`, canonical, and `ghostly-store.sig` beside the index or in `--out`. Refused, with the reason, when a client would refuse the index (`expires` more than 90 days ahead, a bad field), or when the index already there has a higher `sequence` (`rollback`), the same one with other content (`equivocation`), or is signed by another key (`store-key`: another store is another `--out`) |
|
|
161
161
|
| `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]`, `task update <chat\|group> <task> [fields…]` | A task card for a bot's work (WISP 405 · Status Cards): a title, a status (queued, running, blocked, done, failed, cancelled), progress, the step it is on, up to 20 steps or log lines (`--item done:Codec`; a lowercase word before the colon must be an item state: pending, running, done, failed or skipped) and its pull request, shown as a small card that apps without cards read as text. Optional, and ignored by apps from before them: where the pull request stands (`--pr-state draft`, `open`, `merged` or `closed`; an open one puts the task in the board's Review column) and its checks (`--pr-checks passing`, `failing` or `pending`), up to 3 tags of 24 characters (`--tag core --tag web`; an update's tags replace the card's), and `--parent <task>`, a task of yours in the same chat that this one is a part of (sent first; its parts stack under it on the board). `send` prints the task's id and message; `update` merges the fields given over the card (an update's `--item` list replaces the card's: give all of them) and edits its message, at most once per 2.5 s (sooner updates merge into the next). `--json` takes the whole card; `null` in it removes a field. Only the CLI sends cards: the app shows them |
|
|
162
162
|
| `typing <chat> [--kind typing\|recording\|thinking] [--status "<text>"] [--for s] [--stop]` | Show the contact you are writing, recording or thinking, or a short status line in its place ("Transcribing your audio…", 40 characters, no links): live chats only, it holds 6 s there; `--for s` keeps it on that long (up to 600 s; a one-shot stays that long once the chat is live, and stops with `live: false` when it is not live within 15 s); a new kind or status shows at once; a message to the chat or `--stop` ends it |
|
|
163
163
|
| `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]` | How much of a quota the bot has left (WISP 405 § Usage): the percent (or `--used` of `--limit`), what it is of, the account, the window, when it resets, and up to 3 other windows (`--also week=80@2026-10-10T00:00:00Z`). The app shows a small meter on the chat's row and in its header, amber under 20%, red under 5%, muted once 6 h old or past the reset. One card per chat: the first report sends it, later ones edit it in place (quiet, paced as `task update`'s); each report is whole, so a field it leaves out goes. `--all` reports to every 1:1 chat with a contact. See [docs/AI-AGENTS.md](../../docs/AI-AGENTS.md#reporting-usage) for where a bot gets its numbers |
|
|
164
164
|
| `version` | This CLI's version, `{"version":"…"}` (also `ghostly --version`) |
|
|
165
|
-
| `wallet create <type> [--network testnet] [--provider id] [--value name=value]… [--stdin] [--invite code] [--api-key key]` | A wallet: `cashu`, `lightning` (a card: its source's form in `--value`), `arkade`, `spark` (on Mainnet, `--api-key` is your Breez API key), `bitcoin` (BDK), `fedimint` (`--invite` is the federation's invite code), `usdt`. With `--stdin`, secret fields come as `name=value` lines on stdin (`api-key=…` too), out of `ps` and the shell's history |
|
|
165
|
+
| `wallet create <type> [--network testnet] [--provider id] [--value name=value]… [--stdin] [--invite code] [--api-key key]` | A wallet: `cashu`, `lightning` (a card: its source's form in `--value`), `arkade`, `spark` (on Mainnet, `--api-key` is your Breez API key), `bitcoin` (BDK), `fedimint` (`--invite` is the federation's invite code), `usdt`. With `--stdin`, secret fields come as `name=value` lines on stdin (`api-key=…` too), out of `ps` and the shell's history; a `--value` the source's form marks secret (an NWC URI, a macaroon, a rune, an RPC password) is warned about |
|
|
166
166
|
| `wallet faucet <type>`, `wallet add-mint <url> [--primary]` | Test coins; another Cashu mint |
|
|
167
167
|
| `wallet list [--network n]` | Wallets and balances, and what `wallet create` can make on each network |
|
|
168
168
|
| `wallet receive <sats>`, `wallet address <type>`, `wallet redeem [<token>]`, `wallet history` | Receive, and what came and went. `wallet redeem` reads the token from stdin when none is given, which keeps it out of `ps` and the shell's history |
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $n as GROUP_FILE_LIMITS, Bo as GhostlyHttpError, Cl as randomBytes$1, Fo as AWAITING_PEER_MS, Hr as TYPING_REFRESH_MS, Qa as appCardId, Sc as ownInviteRefusal, Vr as TYPING_KINDS, Wo as LIMITS, Wr as sanitizeTypingStatus, Xi as identityStatement, Za as STATUS_CARD_LIMITS, _a as VOICE_LIMITS, _l as MAX_AVATAR_LENGTH, _o as isAppRef, at as groupName, ba as parseVoiceMeta, bc as inviteLink, ci as APP_DATA_MAX_BYTES, eo as cardLine, ga as PLAYABLE_AUDIO, ha as sanitizeMime, m as APP_PERMISSIONS, ma as sanitizeFileName, no as checkStatusCard, po as appRef, va as baseMime, vl as sanitizeAvatar, wa as readImageMeta, wl as toBase64Url, xa as voicePeaksOf, y as isAppUrl } from "./src-D7gcJElT.js";
|
|
2
2
|
import { c as fileStore } from "./idb-B5L8wuOm.js";
|
|
3
|
-
import { O as waitingGroupFile, T as removeStored, U as iceServerProblem, W as iceServerSetting, i as groupOfFile, l as replyRef, n as groupFileId, v as paymentNetwork } from "./groupFiles-
|
|
3
|
+
import { O as waitingGroupFile, T as removeStored, U as iceServerProblem, W as iceServerSetting, i as groupOfFile, l as replyRef, n as groupFileId, v as paymentNetwork } from "./groupFiles-6qnLCnOm.js";
|
|
4
4
|
import { f as FILE_BYTES_STEP, o as fileBytes, s as fileBytesOf } from "./fileBytes-Di7VZtmU.js";
|
|
5
|
-
import { t as IDENTITY_PROVIDERS } from "./registry-
|
|
6
|
-
import { a as waitForEdit, c as findSecret, i as pressable, n as buttonsCard, o as waitForGroupFrame, s as waitForMessage, t as BUTTON_METHODS } from "./buttons-
|
|
7
|
-
import { i as ENGINE_READS, n as withStoredIds, r as ENGINE_METHODS } from "./turnRefs-
|
|
8
|
-
import { t as CliError } from "./errors-
|
|
9
|
-
import { _ as str, a as findChat, c as groupOf, d as messageOf, f as node, g as state, h as sentAt, i as chatOrGroup, l as inviteTaken, m as oneOf, n as bool, o as findChatOrGroup, p as num, r as chatOf, s as findGroup, u as list, v as waitForState } from "./apiKit-
|
|
10
|
-
import { t as BACKUP_METHODS } from "./backup-
|
|
11
|
-
import { o as checkedBundle } from "./apps-
|
|
5
|
+
import { t as IDENTITY_PROVIDERS } from "./registry-CoLHpxkb.js";
|
|
6
|
+
import { a as waitForEdit, c as findSecret, i as pressable, n as buttonsCard, o as waitForGroupFrame, s as waitForMessage, t as BUTTON_METHODS } from "./buttons-DS1iURp8.js";
|
|
7
|
+
import { i as ENGINE_READS, n as withStoredIds, r as ENGINE_METHODS } from "./turnRefs-PN644M9o.js";
|
|
8
|
+
import { i as localFileError, r as asCliError, t as CliError } from "./errors-B-zMxbcd.js";
|
|
9
|
+
import { _ as str, a as findChat, c as groupOf, d as messageOf, f as node, g as state, h as sentAt, i as chatOrGroup, l as inviteTaken, m as oneOf, n as bool, o as findChatOrGroup, p as num, r as chatOf, s as findGroup, u as list, v as waitForState } from "./apiKit-BsUZPHLn.js";
|
|
10
|
+
import { t as BACKUP_METHODS } from "./backup-Bbz3lyC5.js";
|
|
11
|
+
import { o as checkedBundle } from "./apps-BaUNt-5c.js";
|
|
12
12
|
import { generateMnemonic } from "@scure/bip39";
|
|
13
13
|
import { wordlist } from "@scure/bip39/wordlists/english.js";
|
|
14
14
|
import { openAsBlob, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
@@ -1273,7 +1273,10 @@ async function reportUsage(ctx, ref, fields, params) {
|
|
|
1273
1273
|
updated: true
|
|
1274
1274
|
};
|
|
1275
1275
|
}
|
|
1276
|
-
/**
|
|
1276
|
+
/**
|
|
1277
|
+
* `usage send`: one chat or group, or (`all`) every 1:1 chat with a contact; a chat that refuses is said (its
|
|
1278
|
+
* `{code, message}`), not fatal, unless every chat refused: then nothing was reported and the call fails.
|
|
1279
|
+
*/
|
|
1277
1280
|
async function sendUsage(ctx, params) {
|
|
1278
1281
|
const fields = fieldsOf(params);
|
|
1279
1282
|
if (!bool(params, "all")) return reportUsage(ctx, str(params, "chat", true), fields, params);
|
|
@@ -1284,16 +1287,31 @@ async function sendUsage(ctx, params) {
|
|
|
1284
1287
|
kind: "usage",
|
|
1285
1288
|
id: typeof fields.id === "string" && fields.id ? fields.id : USAGE_CARD_ID
|
|
1286
1289
|
});
|
|
1290
|
+
oneOf(params, "wait", ["none", "sent"], "none");
|
|
1291
|
+
num(params, "timeout", 30, {
|
|
1292
|
+
min: 1,
|
|
1293
|
+
max: 3600
|
|
1294
|
+
});
|
|
1287
1295
|
const chats = state(ctx).links.filter((link) => link.profile && link.peerPubKeyZ32);
|
|
1288
1296
|
const results = [];
|
|
1297
|
+
const failed = [];
|
|
1289
1298
|
for (const link of chats) try {
|
|
1290
1299
|
results.push(await reportUsage(ctx, link.id, fields, params));
|
|
1291
1300
|
} catch (error) {
|
|
1301
|
+
const refusal = {
|
|
1302
|
+
chat: link.id,
|
|
1303
|
+
error: asCliError(error)
|
|
1304
|
+
};
|
|
1305
|
+
failed.push(refusal);
|
|
1292
1306
|
results.push({
|
|
1293
1307
|
chat: link.id,
|
|
1294
|
-
error: error
|
|
1308
|
+
error: refusal.error.toJSON()
|
|
1295
1309
|
});
|
|
1296
1310
|
}
|
|
1311
|
+
if (failed.length && failed.length === chats.length) {
|
|
1312
|
+
const codes = new Set(failed.map((f) => f.error.code));
|
|
1313
|
+
throw new CliError(codes.size === 1 ? failed[0].error.code : "refused", `No chat took the usage card: ${failed.map((f) => `${f.chat}: ${f.error.message}`).join("; ")}`, { chats: results });
|
|
1314
|
+
}
|
|
1297
1315
|
return { chats: results };
|
|
1298
1316
|
}
|
|
1299
1317
|
var STATUS_CARD_METHODS = {
|
|
@@ -1622,7 +1640,15 @@ function findMember(group, ref) {
|
|
|
1622
1640
|
const byKey = others.filter((m) => m.key === ref);
|
|
1623
1641
|
const matches = byKey.length ? byKey : others.filter((m) => m.key.startsWith(ref) || m.nick?.trim().toLowerCase() === lower);
|
|
1624
1642
|
if (matches.length === 1) return matches[0];
|
|
1625
|
-
throw
|
|
1643
|
+
throw matches.length ? new CliError("bad_request", `${JSON.stringify(ref)} names more than one member`) : noMember(group, ref);
|
|
1644
|
+
}
|
|
1645
|
+
/**
|
|
1646
|
+
* A ref no other member answers to. When it is this profile's own key (or a prefix of it), it says so: "No member"
|
|
1647
|
+
* read as if this profile were not in the group.
|
|
1648
|
+
*/
|
|
1649
|
+
function noMember(group, ref) {
|
|
1650
|
+
if (group.members.some((m) => m.me && m.key.startsWith(ref))) return new CliError("bad_request", `${JSON.stringify(ref)} is you: name another member of ${group.name}`);
|
|
1651
|
+
return new CliError("not_found", `No member ${JSON.stringify(ref)} in ${group.name}`);
|
|
1626
1652
|
}
|
|
1627
1653
|
/**
|
|
1628
1654
|
* A picture as Ghostly sends it: a square JPEG data URL, checked as a contact would check it. The app scales and
|
|
@@ -1632,8 +1658,8 @@ async function pictureFrom(path) {
|
|
|
1632
1658
|
let bytes;
|
|
1633
1659
|
try {
|
|
1634
1660
|
bytes = await readFile(resolve(path));
|
|
1635
|
-
} catch {
|
|
1636
|
-
throw new CliError("not_found", `No file ${path}`);
|
|
1661
|
+
} catch (error) {
|
|
1662
|
+
throw localFileError(error, path) ?? new CliError("not_found", `No file ${path}`);
|
|
1637
1663
|
}
|
|
1638
1664
|
const url = `data:image/jpeg;base64,${bytes.toString("base64")}`;
|
|
1639
1665
|
const checked = sanitizeAvatar(url);
|
|
@@ -1894,7 +1920,12 @@ var IDENTITY_METHODS = {
|
|
|
1894
1920
|
};
|
|
1895
1921
|
}
|
|
1896
1922
|
if (signer.kind === "redirect") throw new CliError("unavailable", `${signer.label} needs a browser window: make this proof in the app`);
|
|
1897
|
-
const
|
|
1923
|
+
const subject = str(params, "subject");
|
|
1924
|
+
if (!subject) {
|
|
1925
|
+
const what = p.subject ? `, its ${p.subject.label.toLowerCase()}${p.subject.placeholder ? ` (${p.subject.placeholder})` : ""}` : "";
|
|
1926
|
+
throw new CliError("bad_request", `${p.label} takes a subject${what}: ghostly identity add ${p.id} <subject>`);
|
|
1927
|
+
}
|
|
1928
|
+
const { draftId, binding } = await begin(ctx, p, subject, params);
|
|
1898
1929
|
const statement = identityStatement(binding);
|
|
1899
1930
|
drafts.set(draftId, {
|
|
1900
1931
|
draftId,
|
|
@@ -3123,6 +3154,7 @@ var WALLET_METHODS = Object.fromEntries(Object.entries({
|
|
|
3123
3154
|
const nets = params.networks === void 0 ? void 0 : (() => {
|
|
3124
3155
|
const value = params.networks;
|
|
3125
3156
|
if (!Array.isArray(value) || !value.every((v) => v === "mainnet" || v === "testnet")) throw new CliError("bad_request", "networks must list mainnet and/or testnet");
|
|
3157
|
+
if (value.length === 0) throw new CliError("bad_request", "networks must list mainnet and/or testnet: to stop taking it, --off");
|
|
3126
3158
|
return value;
|
|
3127
3159
|
})();
|
|
3128
3160
|
await node(ctx).setChatPaymentMethods({
|
|
@@ -3249,6 +3281,18 @@ var TRANSPORT_NAMES = {
|
|
|
3249
3281
|
"iroh/1": "iroh/1",
|
|
3250
3282
|
"hyperdht/1": "hyperdht/1"
|
|
3251
3283
|
};
|
|
3284
|
+
/** The engine keeps a chat's name to 48 UTF-16 units (renameLink). */
|
|
3285
|
+
var CHAT_NAME_UNITS = 48;
|
|
3286
|
+
/**
|
|
3287
|
+
* A chat's name on this side (`chat rename`, an invite's `--label`): what the engine keeps whole, on one line, refused
|
|
3288
|
+
* with the rule as a profile's or a group's name is. Taken as given, the engine cut it at 48 UTF-16 units without a
|
|
3289
|
+
* word (half an emoji left as a lone surrogate) and kept line breaks. Counted as the engine counts: an emoji is two.
|
|
3290
|
+
*/
|
|
3291
|
+
function chatName(params, name) {
|
|
3292
|
+
const value = str(params, name);
|
|
3293
|
+
if (value !== void 0 && (value.trim().length > CHAT_NAME_UNITS || /\p{Cc}/u.test(value.trim()))) throw new CliError("bad_request", `A chat's name is up to ${CHAT_NAME_UNITS} characters on one line (an emoji counts as two)`);
|
|
3294
|
+
return value;
|
|
3295
|
+
}
|
|
3252
3296
|
/** Code points before `index` (UTF-16) in `text`: mentions count code points (WISP 902 § Mentions). */
|
|
3253
3297
|
var codePoints = (text, index) => [...text.slice(0, index)].length;
|
|
3254
3298
|
/**
|
|
@@ -3266,7 +3310,8 @@ function mentionsFor(text, refs, group) {
|
|
|
3266
3310
|
const lower = ref.toLowerCase();
|
|
3267
3311
|
const byKey = group.members.filter((m) => !m.me && m.key === ref);
|
|
3268
3312
|
const matches = byKey.length ? byKey : group.members.filter((m) => !m.me && (m.key.startsWith(ref) || m.nick?.trim().toLowerCase() === lower));
|
|
3269
|
-
if (matches.length
|
|
3313
|
+
if (matches.length > 1) throw new CliError("bad_request", `${JSON.stringify(ref)} names more than one member`);
|
|
3314
|
+
if (!matches.length) throw noMember(group, ref);
|
|
3270
3315
|
key = matches[0].key;
|
|
3271
3316
|
spelled = [
|
|
3272
3317
|
matches[0].nick?.trim(),
|
|
@@ -3409,7 +3454,7 @@ var METHODS = {
|
|
|
3409
3454
|
};
|
|
3410
3455
|
},
|
|
3411
3456
|
async "invite.create"(ctx, params) {
|
|
3412
|
-
const label =
|
|
3457
|
+
const label = chatName(params, "label");
|
|
3413
3458
|
const { linkId, inviteCode } = await node(ctx).createLink();
|
|
3414
3459
|
if (label) await node(ctx).renameLink({
|
|
3415
3460
|
linkId,
|
|
@@ -3433,6 +3478,7 @@ var METHODS = {
|
|
|
3433
3478
|
},
|
|
3434
3479
|
async "invite.join"(ctx, params) {
|
|
3435
3480
|
let invite = str(params, "invite", true).trim();
|
|
3481
|
+
const label = chatName(params, "label");
|
|
3436
3482
|
const hash = invite.indexOf("#");
|
|
3437
3483
|
if (/^https?:\/\//i.test(invite) && hash !== -1) invite = invite.slice(hash + 1);
|
|
3438
3484
|
let linkId;
|
|
@@ -3445,7 +3491,6 @@ var METHODS = {
|
|
|
3445
3491
|
if (/does not look like/i.test(message)) throw new CliError("bad_request", message);
|
|
3446
3492
|
throw error;
|
|
3447
3493
|
}
|
|
3448
|
-
const label = str(params, "label");
|
|
3449
3494
|
if (label) await node(ctx).renameLink({
|
|
3450
3495
|
linkId,
|
|
3451
3496
|
label
|
|
@@ -3718,7 +3763,7 @@ var METHODS = {
|
|
|
3718
3763
|
const link = chatNotGroup(ctx, params, "group rename", "<name...>");
|
|
3719
3764
|
await node(ctx).renameLink({
|
|
3720
3765
|
linkId: link.id,
|
|
3721
|
-
label:
|
|
3766
|
+
label: chatName(params, "name") ?? ""
|
|
3722
3767
|
});
|
|
3723
3768
|
return chatJson(findChat(state(ctx).links, link.id));
|
|
3724
3769
|
},
|
|
@@ -4169,4 +4214,4 @@ async function callApi(ctx, method, params = {}) {
|
|
|
4169
4214
|
//#endregion
|
|
4170
4215
|
export { flushCardUpdates as _, hideStateSecrets as a, paymentJson as c, nodeLocalFetch as d, chatJson as f, resumeHolds as g, groupMessageJson as h, groupSendRefused as i, reportAppFrames as l, groupJson as m, SETTABLE as n, mentionsFor as o, chatMessageJson as p, callApi as r, redactSettings as s, API_METHODS as t, servedApps as u };
|
|
4171
4216
|
|
|
4172
|
-
//# sourceMappingURL=api-
|
|
4217
|
+
//# sourceMappingURL=api-B1fADtqd.js.map
|