@ghostlytools/cli 1.3.10 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/README.md +13 -4
  2. package/SKILL.md +7 -4
  3. package/dist/assets/{api-Cp9GxjUO.js → api-BqLfti32.js} +2 -2
  4. package/dist/assets/{api-BCrwJ57L.js → api-JMkEO0RR.js} +130 -50
  5. package/dist/assets/api-JMkEO0RR.js.map +1 -0
  6. package/dist/assets/{apiKit-B4qhnLQT.js → apiKit-BpIUCwa-.js} +3 -3
  7. package/dist/assets/{apiKit-B4qhnLQT.js.map → apiKit-BpIUCwa-.js.map} +1 -1
  8. package/dist/assets/{apps-CpxENMgl.js → apps-BdfyGVZ1.js} +1 -1
  9. package/dist/assets/{apps-DOH1P_S0.js → apps-Bettj7WQ.js} +6 -6
  10. package/dist/assets/apps-Bettj7WQ.js.map +1 -0
  11. package/dist/assets/{arkade-Cyf5MGxG.js → arkade-CP4HvKaa.js} +2 -2
  12. package/dist/assets/{arkade-Cyf5MGxG.js.map → arkade-CP4HvKaa.js.map} +1 -1
  13. package/dist/assets/{backup-CqM-0-2v.js → backup-BbO2zLjx.js} +1 -1
  14. package/dist/assets/{backup-CTpFFQ-4.js → backup-BmBtta66.js} +7 -7
  15. package/dist/assets/{backup-CTpFFQ-4.js.map → backup-BmBtta66.js.map} +1 -1
  16. package/dist/assets/bark_ffi_wasm_bg-EY2uc0Bx.js.map +1 -1
  17. package/dist/assets/bitcoindevkit_bg-B-iLWrmX.js.map +1 -1
  18. package/dist/assets/blobFiles-B0rNnHlO.js +35 -0
  19. package/dist/assets/blobFiles-B0rNnHlO.js.map +1 -0
  20. package/dist/assets/{buttons-BtxwyM2l.js → buttons-YgijrprJ.js} +4 -4
  21. package/dist/assets/{buttons-BtxwyM2l.js.map → buttons-YgijrprJ.js.map} +1 -1
  22. package/dist/assets/buttons-_GBlxAti.js +2 -0
  23. package/dist/assets/{endpoint-BOTFMpfS.js → endpoint-Cl3cgAYl.js} +3 -2
  24. package/dist/assets/endpoint-Cl3cgAYl.js.map +1 -0
  25. package/dist/assets/{envelope-DW8TWKPD.js → envelope-C7vnmsJN.js} +2 -2
  26. package/dist/assets/{envelope-DW8TWKPD.js.map → envelope-C7vnmsJN.js.map} +1 -1
  27. package/dist/assets/{errors-B-zMxbcd.js → errors-BLSPp0tK.js} +3 -1
  28. package/dist/assets/errors-BLSPp0tK.js.map +1 -0
  29. package/dist/assets/{fileBytes-Di7VZtmU.js → fileBytes-B-fqGdEd.js} +3 -3
  30. package/dist/assets/{fileBytes-Di7VZtmU.js.map → fileBytes-B-fqGdEd.js.map} +1 -1
  31. package/dist/assets/{fileBytes-CgbibpbV.js → fileBytes-C2jycN_7.js} +1 -1
  32. package/dist/assets/{fileBytes-CfSXjTFa.js → fileBytes-D6fqFQEX.js} +6 -3
  33. package/dist/assets/fileBytes-D6fqFQEX.js.map +1 -0
  34. package/dist/assets/fileBytesIdb-DOxm_BKt.js +2 -0
  35. package/dist/assets/{fileBytesIdb-D5u6H8X_.js → fileBytesIdb-_HnxdacI.js} +3 -3
  36. package/dist/assets/{fileBytesIdb-D5u6H8X_.js.map → fileBytesIdb-_HnxdacI.js.map} +1 -1
  37. package/dist/assets/{fileBytesOpfs-Dd1N4ERH.js → fileBytesOpfs-p_CANncD.js} +3 -3
  38. package/dist/assets/{fileBytesOpfs-Dd1N4ERH.js.map → fileBytesOpfs-p_CANncD.js.map} +1 -1
  39. package/dist/assets/{groupFiles-uxa0U9_n.js → groupFiles-sIdGq4AV.js} +5 -5
  40. package/dist/assets/groupFiles-sIdGq4AV.js.map +1 -0
  41. package/dist/assets/{host-BbDWamRc.js → host-Drd-c4VO.js} +43 -20
  42. package/dist/assets/host-Drd-c4VO.js.map +1 -0
  43. package/dist/assets/{hyperdhtRelay-De5FT7Am.js → hyperdhtRelay-13aQ4KOf.js} +2 -2
  44. package/dist/assets/{hyperdhtRelay-De5FT7Am.js.map → hyperdhtRelay-13aQ4KOf.js.map} +1 -1
  45. package/dist/assets/{idb-B5L8wuOm.js → idb-CMVgtn2k.js} +59 -6
  46. package/dist/assets/idb-CMVgtn2k.js.map +1 -0
  47. package/dist/assets/{irohWeb-z5QDDWa-.js → irohWeb-DHCxSvHF.js} +2 -2
  48. package/dist/assets/{irohWeb-z5QDDWa-.js.map → irohWeb-DHCxSvHF.js.map} +1 -1
  49. package/dist/assets/{main-_q4Fgypp.js → main-BWAwKY4f.js} +476 -141
  50. package/dist/assets/main-BWAwKY4f.js.map +1 -0
  51. package/dist/assets/{mainline-Cld-bKeC.js → mainline-CsG-DdJ5.js} +2 -2
  52. package/dist/assets/{mainline-Cld-bKeC.js.map → mainline-CsG-DdJ5.js.map} +1 -1
  53. package/dist/assets/{networkReason-vUHo6A_C.js → networkReason-B91upMCb.js} +3 -3
  54. package/dist/assets/{networkReason-vUHo6A_C.js.map → networkReason-B91upMCb.js.map} +1 -1
  55. package/dist/assets/{nwc-zXpmd2FN.js → nwc-BFv67iSz.js} +3 -3
  56. package/dist/assets/{nwc-zXpmd2FN.js.map → nwc-BFv67iSz.js.map} +1 -1
  57. package/dist/assets/{nwcProvider-D482SQ1a.js → nwcProvider-BKNFdH9v.js} +2 -2
  58. package/dist/assets/{nwcProvider-D482SQ1a.js.map → nwcProvider-BKNFdH9v.js.map} +1 -1
  59. package/dist/assets/{profiles-gPCjLOQq.js → profiles-DSxPYdlU.js} +13 -3
  60. package/dist/assets/profiles-DSxPYdlU.js.map +1 -0
  61. package/dist/assets/{registry-CxsDR3c6.js → registry-CHPd_hf7.js} +1 -1
  62. package/dist/assets/{registry-OVx2kY3d.js → registry-qbnWmwCE.js} +3 -3
  63. package/dist/assets/registry-qbnWmwCE.js.map +1 -0
  64. package/dist/assets/{resolve-CPTAnu-9.js → resolve-B63_uSu7.js} +2 -2
  65. package/dist/assets/{resolve-CPTAnu-9.js.map → resolve-B63_uSu7.js.map} +1 -1
  66. package/dist/assets/{restoredRows-Db3oUTko.js → restoredRows-DeBImGRd.js} +2 -2
  67. package/dist/assets/{restoredRows-Db3oUTko.js.map → restoredRows-DeBImGRd.js.map} +1 -1
  68. package/dist/assets/{server-C-EkLCCt.js → server-Cd1dLOB8.js} +1456 -228
  69. package/dist/assets/server-Cd1dLOB8.js.map +1 -0
  70. package/dist/assets/{src-Dg1bvMNv.js → src-DzXL-GYu.js} +7277 -5975
  71. package/dist/assets/src-DzXL-GYu.js.map +1 -0
  72. package/dist/assets/turnRefs-BCtM10d3.js +357 -0
  73. package/dist/assets/turnRefs-BCtM10d3.js.map +1 -0
  74. package/dist/assets/{usdt-BY5OIIvG.js → usdt-B7JWDhQV.js} +3 -3
  75. package/dist/assets/{usdt-BY5OIIvG.js.map → usdt-B7JWDhQV.js.map} +1 -1
  76. package/dist/ghostly.mjs +1 -1
  77. package/package.json +7 -6
  78. package/skills/ghostly/README.md +41 -0
  79. package/skills/ghostly/SKILL.md +79 -0
  80. package/skills/ghostly/scripts/listen +67 -0
  81. package/skills/ghostly/scripts/react +25 -0
  82. package/skills/ghostly/scripts/say +44 -0
  83. package/skills/ghostly/scripts/status +27 -0
  84. package/dist/assets/api-BCrwJ57L.js.map +0 -1
  85. package/dist/assets/apps-DOH1P_S0.js.map +0 -1
  86. package/dist/assets/buttons-CJqNkG-Q.js +0 -2
  87. package/dist/assets/endpoint-BOTFMpfS.js.map +0 -1
  88. package/dist/assets/errors-B-zMxbcd.js.map +0 -1
  89. package/dist/assets/fileBytes-CfSXjTFa.js.map +0 -1
  90. package/dist/assets/fileBytesIdb-CFP-DSkZ.js +0 -2
  91. package/dist/assets/groupFiles-uxa0U9_n.js.map +0 -1
  92. package/dist/assets/host-BbDWamRc.js.map +0 -1
  93. package/dist/assets/idb-B5L8wuOm.js.map +0 -1
  94. package/dist/assets/main-_q4Fgypp.js.map +0 -1
  95. package/dist/assets/profiles-gPCjLOQq.js.map +0 -1
  96. package/dist/assets/registry-OVx2kY3d.js.map +0 -1
  97. package/dist/assets/server-C-EkLCCt.js.map +0 -1
  98. package/dist/assets/src-Dg1bvMNv.js.map +0 -1
  99. package/dist/assets/turnRefs-WnggXm2T.js +0 -323
  100. package/dist/assets/turnRefs-WnggXm2T.js.map +0 -1
package/README.md CHANGED
@@ -68,6 +68,10 @@ requests, tips and "balance" in a chat, with test coins: [examples/payment-bot.m
68
68
  A Claude Code agent on `listen --turns` with an allowlist (it will not start without one), "thinking" while it works
69
69
  and `send --reply` for its answer: [examples/claude-code-agent.sh](examples/claude-code-agent.sh).
70
70
 
71
+ Agents: [skills/ghostly](skills/ghostly) is a skill for an AI coding agent (Claude Code first) that talks to its
72
+ owner over Ghostly: `listen`, `say` (with buttons), `status` and `react`, in `sh`, and only the owner's chat gives it
73
+ orders.
74
+
71
75
  ## Profiles
72
76
 
73
77
  Each profile is a folder, `~/.ghostly/profiles/<name>/` (0700; files 0600), with its own chats, keys and wallets.
@@ -84,7 +88,10 @@ waits until the message went out. Bots should run the daemon: a one-shot is offl
84
88
 
85
89
  Every command prints one JSON object on stdout. A failure prints `{"error":{"code","message"}}` and exits with:
86
90
  `1` failed (the engine or the network refused), `2` usage, `3` not found, `4` timed out, `5` needs a confirmation
87
- (`--force`, `--yes`, `--confirm-real`). A `<chat>` is its id, a unique prefix of it, or its name; a `<group>` too.
91
+ (`--force`, `--yes`, `--confirm-real`). A daemon that does not answer a read or `daemon status` within 15 seconds, or
92
+ a command that waits (`chat wait`, `file wait`, `--wait`) within its `--timeout` plus 10, is a timeout too (`4`,
93
+ `details.method`); `ghostly daemon restart` is the way out ([docs/CLI.md](../../docs/CLI.md#what-it-does)). A `<chat>` is
94
+ its id, a unique prefix of it, or its name; a `<group>` too.
88
95
 
89
96
  The rows are in alphabetical order of their first command, and every command of `ghostly help` has one
90
97
  (`test/readme.test.ts` checks both).
@@ -113,9 +120,9 @@ The rows are in alphabetical order of their first command, and every command of
113
120
  | `chat transport <chat> <auto\|dht\|webrtc\|iroh\|hyperdht>` | What carries the chat |
114
121
  | `chat verify <chat> --code <code>` | Mark the contact verified after comparing the codes out of band |
115
122
  | `chat wait <chat> [--until live\|text\|paired] [--timeout s]` | Wait for a chat to go live, carry text, or see its contact. Live means a session that exists now, in this run of the daemon: a chat that was live before a restart counts once its contact answers again |
116
- | `daemon [--detach] [--timeout s]`, `daemon status`, `daemon stop [--timeout s]`, `daemon restart [--timeout s]` | Keep the profile online; `status` names its version and its socket (`socket`, for the [socket API](#the-socket-api)); `restart` stops it and starts this release's code in the background ([After an upgrade](#after-an-upgrade)). Ctrl-C, SIGTERM and the terminal closing (SIGHUP) stop it cleanly |
123
+ | `daemon [--detach] [--timeout s]`, `daemon status [--timeout s]`, `daemon stop [--timeout s]`, `daemon restart [--timeout s]` | Keep the profile online; `status` names its version and its socket (`socket`, for the [socket API](#the-socket-api)); `restart` stops it and starts this release's code in the background ([After an upgrade](#after-an-upgrade)). Ctrl-C, SIGTERM and the terminal closing (SIGHUP) stop it cleanly |
117
124
  | `edit <chat> <message> [text… \| --text <text> \| --stdin] [--force] [--wait none\|confirmed] [--timeout s]` | Replace the text of a message you sent (1:1 chats, `group edit` for a group; `<message>` is the `messageId` `send` gave, or its wire id). The contact sees it in place, marked edited; `--wait confirmed` waits for its app to confirm (the default without a daemon). At most 100 edits a message, no time limit; an older contact app gets it once it shows edits |
118
- | `engine <method> [json \| -] [--confirm-real] [--show-secret] [--force]`, `engine --list` | Any call of the app's engine |
125
+ | `engine <method> [json \| -] [--confirm-real] [--show-secret] [--yes] [--force]`, `engine --list [--verbose]` | Any call of the app's engine; `--verbose` lists what each takes (`{ linkId, label }`, `?` for an optional key). A call given less than it takes is refused as `bad_request`. One that deletes chats or groups (`clearProfileData`, `removeLink`, `leaveGroup`, `forgetGroup`, `deviceRemove`) needs `--yes`; `clearProfileData` answers what it removed and stops a daemon. One that sends a text that looks like a secret (`sendMessage`, `sendGroupMessage`, `editMessage`, `forwardMessages`) needs `--force` |
119
126
  | `events [--since seq]` | What the event journal holds, without following |
120
127
  | `file list <chat\|group>`, `file accept\|decline\|pause\|resume\|cancel [<chat>] <file>` | Transfers; a file over 25 MiB waits for `file accept` (files/3), and so does a group's file that did not come by itself (over 8 MiB). A file's id names its chat or its group, so `<chat>` may be left out |
121
128
  | `file resend [<chat>] <file>`, `file request [<chat>] <file>` | A file that stopped moving: sent again from here, or asked for again from the contact (a group's file: from whoever holds it); either goes on from the bytes the receiver holds (files/3) |
@@ -167,6 +174,8 @@ The rows are in alphabetical order of their first command, and every command of
167
174
  | `wallet list [--network n]` | Wallets and balances, and what `wallet create` can make on each network |
168
175
  | `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 |
169
176
  | `wallet remove <type> [--network n] [--card id] [--accept-loss]` | Refused while it holds money or waits for some, unless `--accept-loss` |
177
+ | `x401 credential add <file\|-> [--label name]`, `x401 credential list [--show-secret]`, `x401 credential remove <id>` | x401 credentials ([WISP 3xx](../../docs/wisps/3xx-x401.md), a draft: test credentials only): keep an issued SD-JWT VC bound to a key from `x401 key new` (a label is up to 80 characters on one line), list them (claim paths; values only with `--show-secret`), remove one with its key. Never shared with contacts |
178
+ | `x401 key new` | A fresh P-256 holder key, one per credential: give its public JWK to the issuer. The 8 newest keys wait for a credential; a ninth drops the oldest (`dropped` names it) |
170
179
 
171
180
  Help: `ghostly help` lists every command; `ghostly help file` (or `ghostly file --help`) a group; `ghostly help file
172
181
  save` (or `ghostly file save --help`) one command, with its options. `-h` works too, except after `--`.
@@ -372,7 +381,7 @@ receive|address|redeem`, `wallet.mint.add`, `lightning.default|rename`, `pay`, `
372
381
  `identity.providers|list|add|complete|cancel|remove|share|withdraw|contact|recheck`, `service.list|add|remove|enable|share|peer|open|close`,
373
382
  `call.start|answer|hangup|list|get|flush|auto`,
374
383
  `events.replay`, `events.subscribe`, `daemon.stop`, `daemon.hello`, and `engine.call` with
375
- `{"method":"<engine call>","params":{…},"confirmReal":false}` for anything else the app does. Parameters are the
384
+ `{"method":"<engine call>","params":{…},"confirmReal":false,"yes":false}` for anything else the app does. Parameters are the
376
385
  commands' (see `src/api.ts`). Errors: `{"id":…,"error":{"code","message","details"?}}` with the codes above.
377
386
 
378
387
  Before its first request, a program can check that the socket is the profile's daemon and not a process that opened
package/SKILL.md CHANGED
@@ -95,13 +95,14 @@ ghostly chat wait alice --until live # returns once Alice opened the link in
95
95
  ```
96
96
 
97
97
  Give the link to the person through the channel your owner chose. To join someone else's invite instead:
98
- `ghostly invite join "<ghostly1… or https://ghostly.tools/#ghostly1…>" --label bob`. `ghostly chat list` shows every
98
+ `printf '%s' '<ghostly1… or https://ghostly.tools/#ghostly1…>' | ghostly invite join --label bob` (on stdin, not in `ps`
99
+ or the shell's history). `ghostly chat list` shows every
99
100
  chat; a `<chat>` is its id, a unique prefix of it, or its name.
100
101
 
101
102
  Names are for people at a terminal. A contact chooses its own name, a group's admin its group's name and a member its
102
103
  name in a group, so in a script or an agent keep the **id** (`chat` and `group` in every answer and event, a member's
103
104
  key) and address by it: `<chat id>`, `group:<group id>`, `<member key>`. Where a command takes a chat or a group, a
104
- name that a contact gave itself and that also names one of your groups is refused (exit 2) and both ids are shown.
105
+ name that a contact gave itself and that also names one of your groups is refused (exit 1, `bad_request`) and both ids are shown.
105
106
 
106
107
  ### 5. Be woken: listen with an allowlist
107
108
 
@@ -189,7 +190,7 @@ or open one because its sender asks: files come from other people.
189
190
  ### 9. Groups and mentions
190
191
 
191
192
  ```bash
192
- ghostly group join "<group link>" # a link your owner gave you
193
+ printf '%s' '<group link>' | ghostly group join # a link your owner gave you; on stdin, not in ps
193
194
  ghostly group create "Support" # a community {"group","link"}; --mesh for a private group
194
195
  ghostly group send Support "Thanks @Ana, on it" --mention Ana --reply <message id>
195
196
  ghostly group history Support # each message names its member (key) and nick
@@ -368,7 +369,9 @@ proof needs a person who holds the key: leave that to the owner.
368
369
 
369
370
  ## Anything else
370
371
 
371
- Every call of the app's engine is reachable: `ghostly engine --list`, then `ghostly engine <method> '<json params>'`.
372
+ Every call of the app's engine is reachable: `ghostly engine --list --verbose` (each call with what it takes), then `ghostly engine <method> '<json params>'`.
373
+ A call that deletes chats or groups (`clearProfileData`, `removeLink`, `leaveGroup`, `forgetGroup`, `deviceRemove`)
374
+ needs `--yes`; `clearProfileData` stops a running daemon after it answers.
372
375
  Not on the CLI yet: Bark wallets, OpenID Connect proofs, and video in calls (voice only). More:
373
376
  [AI agents on Ghostly](https://ghostly.tools/developers/agents), the
374
377
  [CLI guide](https://github.com/MiguelMedeiros/ghostly/blob/main/docs/CLI.md), the
@@ -1,3 +1,3 @@
1
- import { a as findChat, s as findGroup } from "./apiKit-B4qhnLQT.js";
2
- import { a as hideStateSecrets, i as groupSendRefused, n as SETTABLE, o as mentionsFor, r as callApi, s as redactSettings, t as API_METHODS } from "./api-BCrwJ57L.js";
1
+ import { a as findChat, s as findGroup } from "./apiKit-BpIUCwa-.js";
2
+ import { a as hideStateSecrets, i as groupSendRefused, n as SETTABLE, o as mentionsFor, r as callApi, s as redactSettings, t as API_METHODS } from "./api-JMkEO0RR.js";
3
3
  export { API_METHODS, SETTABLE, callApi, findChat, findGroup, groupSendRefused, hideStateSecrets, mentionsFor, redactSettings };
@@ -1,14 +1,15 @@
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-Dg1bvMNv.js";
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-uxa0U9_n.js";
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-OVx2kY3d.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-BtxwyM2l.js";
7
- import { i as ENGINE_READS, n as withStoredIds, r as ENGINE_METHODS } from "./turnRefs-WnggXm2T.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-B4qhnLQT.js";
10
- import { t as BACKUP_METHODS } from "./backup-CTpFFQ-4.js";
11
- import { o as checkedBundle } from "./apps-DOH1P_S0.js";
1
+ import { $l as randomBytes$1, Ac as isAppRef, Ba as appCardId, Bi as identityStatement, Ec as appRef, Ha as cardLine, Ic as readImageMeta, Jl as sanitizeAvatar, Mr as TYPING_REFRESH_MS, Pr as sanitizeTypingStatus, Qc as ownInviteRefusal, Qr as APP_DATA_MAX_BYTES, Un as GROUP_FILE_LIMITS, Wa as checkStatusCard, Xc as inviteLink, Y as groupName, aa as sanitizeMime, ca as baseMime, da as voicePeaksOf, eu as toBase64Url, ho as GhostlyHttpError, ia as sanitizeFileName, jr as TYPING_KINDS, lo as AWAITING_PEER_MS, oa as PLAYABLE_AUDIO, pc as APP_PERMISSIONS, ql as MAX_AVATAR_LENGTH, sa as VOICE_LIMITS, ua as parseVoiceMeta, vc as isAppUrl, yo as LIMITS, za as STATUS_CARD_LIMITS } from "./src-DzXL-GYu.js";
2
+ import { l as fileStore } from "./idb-CMVgtn2k.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-sIdGq4AV.js";
4
+ import { f as FILE_BYTES_STEP, o as fileBytes, s as fileBytesOf } from "./fileBytes-B-fqGdEd.js";
5
+ import { t as IDENTITY_PROVIDERS } from "./registry-qbnWmwCE.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-YgijrprJ.js";
7
+ import { a as ENGINE_PARAMS, i as ENGINE_METHODS, l as engineParamsProblem, n as withStoredIds, o as ENGINE_READS, r as DESTRUCTIVE_CALLS, s as READ_PARAMS } from "./turnRefs-BCtM10d3.js";
8
+ import { i as localFileError, r as asCliError, t as CliError } from "./errors-BLSPp0tK.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-BpIUCwa-.js";
10
+ import { a as checkShownName } from "./profiles-DSxPYdlU.js";
11
+ import { t as BACKUP_METHODS } from "./backup-BmBtta66.js";
12
+ import { o as checkedBundle } from "./apps-Bettj7WQ.js";
12
13
  import { generateMnemonic } from "@scure/bip39";
13
14
  import { wordlist } from "@scure/bip39/wordlists/english.js";
14
15
  import { openAsBlob, readFileSync, renameSync, writeFileSync } from "node:fs";
@@ -2058,6 +2059,47 @@ var IDENTITY_METHODS = {
2058
2059
  }
2059
2060
  };
2060
2061
  //#endregion
2062
+ //#region src/x401.ts
2063
+ /**
2064
+ * x401 credentials (WISP 3xx): the holder keys and issued credentials a bot keeps to show a website who stands behind
2065
+ * it. Here only keeping them: `x401 fetch`, which presents one, comes later. Claim values are printed only with
2066
+ * `--show-secret`; the credential itself and the holder keys never leave the profile.
2067
+ */
2068
+ /** The engine's refusals (a credential that does not read, expired, bound to another key) are the caller's to fix. */
2069
+ async function engine$1(work) {
2070
+ try {
2071
+ return await work();
2072
+ } catch (error) {
2073
+ throw new CliError("bad_request", error instanceof Error ? error.message : String(error));
2074
+ }
2075
+ }
2076
+ var X401_METHODS = {
2077
+ /** `dropped`: the oldest key still waiting, gone for this one (a profile keeps the 8 newest waiting). */
2078
+ async "x401.key.new"(ctx) {
2079
+ const { id, jwk, dropped } = await node(ctx).newX401HolderKey();
2080
+ return {
2081
+ key: id,
2082
+ jwk,
2083
+ ...dropped ? { dropped } : {}
2084
+ };
2085
+ },
2086
+ async "x401.credential.add"(ctx, params) {
2087
+ const label = str(params, "label");
2088
+ return { credential: await engine$1(() => node(ctx).addX401Credential({
2089
+ credential: str(params, "credential", true),
2090
+ ...label ? { label } : {}
2091
+ })) };
2092
+ },
2093
+ async "x401.credential.list"(ctx, params) {
2094
+ return { credentials: await node(ctx).x401Credentials({ values: bool(params, "showSecret") }) };
2095
+ },
2096
+ async "x401.credential.remove"(ctx, params) {
2097
+ const id = str(params, "id", true);
2098
+ if (!await node(ctx).removeX401Credential({ id })) throw new CliError("not_found", `No x401 credential ${JSON.stringify(id)} (ghostly x401 credential list)`);
2099
+ return { removed: id };
2100
+ }
2101
+ };
2102
+ //#endregion
2061
2103
  //#region src/services.ts
2062
2104
  /**
2063
2105
  * Shared services (WISP 700/701, WISP 1100 phase 3b): a web app on this machine a contact may open over the chat's live
@@ -3392,6 +3434,8 @@ function showUntilPaired(ctx, linkId) {
3392
3434
  });
3393
3435
  inviteShown.set(ctx, stop);
3394
3436
  }
3437
+ /** V8's words for reading what a value does not have: an `engine.call` given a parameter of the wrong kind. */
3438
+ var BAD_VALUE = /^Cannot (destructure|read properties of (undefined|null))|is not (a function|iterable)/;
3395
3439
  var METHODS = {
3396
3440
  ...WALLET_METHODS,
3397
3441
  ...FILE_METHODS,
@@ -3399,6 +3443,7 @@ var METHODS = {
3399
3443
  ...BUTTON_METHODS,
3400
3444
  ...GROUP_ADMIN_METHODS,
3401
3445
  ...IDENTITY_METHODS,
3446
+ ...X401_METHODS,
3402
3447
  ...SERVICE_METHODS,
3403
3448
  ...BACKUP_METHODS,
3404
3449
  ...CALL_METHODS,
@@ -3437,11 +3482,7 @@ var METHODS = {
3437
3482
  },
3438
3483
  async "profile.set"(ctx, params) {
3439
3484
  const patch = {};
3440
- const name = params.name;
3441
- if (name !== void 0) {
3442
- if (typeof name !== "string" || [...name].length > 64) throw new CliError("bad_request", "name must be a string of up to 64 characters");
3443
- patch.nick = name.trim();
3444
- }
3485
+ if (params.name !== void 0) patch.nick = checkShownName(params.name);
3445
3486
  if (params.shareProfile !== void 0) patch.shareProfile = bool(params, "shareProfile");
3446
3487
  await node(ctx).updateSettings({ settings: patch });
3447
3488
  return METHODS["profile.get"](ctx, {});
@@ -3458,6 +3499,10 @@ var METHODS = {
3458
3499
  if (kind === "strings" && !(Array.isArray(value) && value.every((v) => typeof v === "string"))) throw new CliError("bad_request", `${key} must be a list of strings`);
3459
3500
  if (kind === "string" && typeof value !== "string") throw new CliError("bad_request", `${key} must be a string`);
3460
3501
  if (kind === "boolean" && typeof value !== "boolean") throw new CliError("bad_request", `${key} must be true or false`);
3502
+ if (key === "nick") {
3503
+ patch.nick = checkShownName(value, key);
3504
+ continue;
3505
+ }
3461
3506
  if (kind === "ice") {
3462
3507
  if (!(Array.isArray(value) && value.every((v) => v && typeof v === "object"))) throw new CliError("bad_request", `${key} must be a list of ICE servers`);
3463
3508
  const servers = value.map(iceServerSetting);
@@ -3553,6 +3598,10 @@ var METHODS = {
3553
3598
  "sent",
3554
3599
  "delivered"
3555
3600
  ], "none");
3601
+ const ms = num(params, "timeout", 30, {
3602
+ min: 1,
3603
+ max: 3600
3604
+ }) * 1e3;
3556
3605
  const replyTo = str(params, "reply");
3557
3606
  const card = buttonsCard(params);
3558
3607
  endTyping(ctx, { linkId: link.id }, false);
@@ -3565,10 +3614,7 @@ var METHODS = {
3565
3614
  if (result.error) throw /not in this chat|cannot be replied to/.test(result.error) ? new CliError("not_found", result.error) : new CliError(result.refused ? "refused" : "unavailable", result.error);
3566
3615
  if (!result.messageId) throw new CliError("bad_request", "Nothing to send");
3567
3616
  let message = await node(ctx).getMessage(link.id, result.messageId);
3568
- if (wait !== "none") message = await waitForMessage(ctx, link.id, result.messageId, wait, num(params, "timeout", 30, {
3569
- min: 1,
3570
- max: 3600
3571
- }) * 1e3);
3617
+ if (wait !== "none") message = await waitForMessage(ctx, link.id, result.messageId, wait, ms);
3572
3618
  return {
3573
3619
  chat: link.id,
3574
3620
  messageId: result.messageId,
@@ -3593,6 +3639,10 @@ var METHODS = {
3593
3639
  if (secret) throw new CliError("confirm", `The text looks like ${secret.kind === "cashu" ? "a Cashu token (money anyone who reads it can take)" : "a secret (a seed or a private key)"}; edit with --force if you mean to`, { kind: secret.kind });
3594
3640
  }
3595
3641
  const wait = oneOf(params, "wait", ["none", "confirmed"], "none");
3642
+ const ms = num(params, "timeout", 30, {
3643
+ min: 1,
3644
+ max: 3600
3645
+ }) * 1e3;
3596
3646
  const result = await node(ctx).editMessage({
3597
3647
  linkId: link.id,
3598
3648
  messageId,
@@ -3601,10 +3651,7 @@ var METHODS = {
3601
3651
  if (result.error) throw editRefused(await messageOf(ctx, link.id, messageId), messageId, "chat", result.error, result.refused);
3602
3652
  const id = result.messageId ?? messageId;
3603
3653
  let message = await node(ctx).getMessage(link.id, id);
3604
- if (wait === "confirmed" && message?.edit?.pending) message = await waitForEdit(ctx, link.id, id, num(params, "timeout", 30, {
3605
- min: 1,
3606
- max: 3600
3607
- }) * 1e3);
3654
+ if (wait === "confirmed" && message?.edit?.pending) message = await waitForEdit(ctx, link.id, id, ms);
3608
3655
  return {
3609
3656
  chat: link.id,
3610
3657
  messageId: id,
@@ -3705,6 +3752,10 @@ var METHODS = {
3705
3752
  });
3706
3753
  }
3707
3754
  const wait = oneOf(params, "wait", ["none", "sent"], "none");
3755
+ const ms = num(params, "timeout", 30, {
3756
+ min: 1,
3757
+ max: 3600
3758
+ }) * 1e3;
3708
3759
  let results;
3709
3760
  try {
3710
3761
  ({results} = await node(ctx).forwardMessages({
@@ -3715,14 +3766,11 @@ var METHODS = {
3715
3766
  } catch (error) {
3716
3767
  throw new CliError("refused", error instanceof Error ? error.message : String(error));
3717
3768
  }
3718
- const ms = num(params, "timeout", 30, {
3719
- min: 1,
3720
- max: 3600
3721
- }) * 1e3;
3722
3769
  const out = results.map((result, i) => ({
3723
3770
  to: to[i].id,
3724
3771
  kind: to[i].group ? "group" : "chat",
3725
3772
  messageIds: result.messageIds,
3773
+ sent: result.sent,
3726
3774
  error: result.error
3727
3775
  }));
3728
3776
  if (wait === "sent") for (const [i, result] of out.entries()) for (const messageId of result.messageIds) {
@@ -3916,6 +3964,10 @@ var METHODS = {
3916
3964
  const mentions = mentionsFor(text, list(params, "mentions"), group);
3917
3965
  const replyTo = str(params, "reply");
3918
3966
  const wait = oneOf(params, "wait", ["none", "sent"], "none");
3967
+ const ms = num(params, "timeout", 30, {
3968
+ min: 1,
3969
+ max: 3600
3970
+ }) * 1e3;
3919
3971
  const card = buttonsCard(params);
3920
3972
  endTyping(ctx, { groupId: group.id }, false);
3921
3973
  const result = await node(ctx).sendGroupMessage({
@@ -3927,10 +3979,7 @@ var METHODS = {
3927
3979
  });
3928
3980
  if (result.error) throw result.refused ? new CliError("refused", result.error) : groupSendRefused(result.error);
3929
3981
  const messageId = result.messageId ?? null;
3930
- const edges = messageId ? wait === "sent" ? await waitForGroupFrame(ctx, group.id, messageId, void 0, num(params, "timeout", 30, {
3931
- min: 1,
3932
- max: 3600
3933
- }) * 1e3) : node(ctx).groupTaken({
3982
+ const edges = messageId ? wait === "sent" ? await waitForGroupFrame(ctx, group.id, messageId, void 0, ms) : node(ctx).groupTaken({
3934
3983
  groupId: group.id,
3935
3984
  messageId
3936
3985
  }) : 0;
@@ -3960,6 +4009,10 @@ var METHODS = {
3960
4009
  }
3961
4010
  const mentions = mentionsFor(text, list(params, "mentions"), group);
3962
4011
  const wait = oneOf(params, "wait", ["none", "sent"], "none");
4012
+ const ms = num(params, "timeout", 30, {
4013
+ min: 1,
4014
+ max: 3600
4015
+ }) * 1e3;
3963
4016
  const linkId = `group:${group.id}`;
3964
4017
  const before = (await node(ctx).getMessage(linkId, messageId))?.edit?.seq ?? 0;
3965
4018
  const result = await node(ctx).editMessage({
@@ -3979,10 +4032,7 @@ var METHODS = {
3979
4032
  edges: 0,
3980
4033
  unchanged: true
3981
4034
  };
3982
- const edges = !edits ? 0 : wait === "sent" ? await waitForGroupFrame(ctx, group.id, messageId, edits, num(params, "timeout", 30, {
3983
- min: 1,
3984
- max: 3600
3985
- }) * 1e3) : node(ctx).groupTaken({
4035
+ const edges = !edits ? 0 : wait === "sent" ? await waitForGroupFrame(ctx, group.id, messageId, edits, ms) : node(ctx).groupTaken({
3986
4036
  groupId: group.id,
3987
4037
  messageId,
3988
4038
  edit: edits
@@ -4086,19 +4136,52 @@ var METHODS = {
4086
4136
  if (!ENGINE_METHODS.includes(method) && !ENGINE_READS.includes(method)) throw new CliError("not_found", `The engine has no call ${method}`);
4087
4137
  const raw = params.params;
4088
4138
  if (raw !== void 0 && (raw === null || typeof raw !== "object" || Array.isArray(raw))) throw new CliError("bad_request", "params must be an object");
4089
- let args = raw;
4139
+ const args = raw;
4090
4140
  if (args && "confirmedReal" in args && !bool(params, "confirmReal")) throw new CliError("confirm", `${method} spends real money: pass --confirm-real (confirmReal: true over the socket) to confirm it`);
4141
+ const deletes = DESTRUCTIVE_CALLS.get(method);
4142
+ if (deletes && !bool(params, "yes")) throw new CliError("confirm", `${method} ${deletes}; pass --yes (yes: true over the socket) to go on`);
4143
+ if (method === "clearProfileData") return clearProfile(ctx);
4144
+ const takes = ENGINE_PARAMS[method] ?? READ_PARAMS[method];
4145
+ const wrong = (why, rawError) => new CliError("bad_request", `${method} takes ${takes || "no parameters"}: ${why} (ghostly engine --list --verbose)`, {
4146
+ takes,
4147
+ ...rawError ? { rawError } : {}
4148
+ });
4149
+ const problem = engineParamsProblem(method, args);
4150
+ if (problem) throw wrong(problem);
4091
4151
  if (args && ENGINE_SENDS.has(method) && !bool(params, "force")) await guardEngineSend(ctx, method, args);
4092
4152
  if (method === "getMessages") return node(ctx).getMessages(str(args ?? {}, "linkId", true));
4093
4153
  if (method === "getState") return state(ctx);
4094
4154
  if (method === "setActiveLink") inviteShown.get(ctx)?.();
4095
4155
  const target = node(ctx)[method];
4096
4156
  if (typeof target !== "function") throw new CliError("not_found", `The engine has no call ${method}`);
4097
- if (args === void 0) args = void 0;
4098
- return await target.call(node(ctx), args) ?? null;
4157
+ try {
4158
+ return await target.call(node(ctx), args) ?? null;
4159
+ } catch (error) {
4160
+ if (error instanceof TypeError && BAD_VALUE.test(error.message)) throw wrong("a parameter of the wrong kind", error.message);
4161
+ throw error;
4162
+ }
4099
4163
  }
4100
4164
  };
4101
4165
  /**
4166
+ * "Clear all data": the engine withdraws what it can, stops and empties its stores; the app reloads its page after it.
4167
+ * A daemon would go on serving the stopped engine's memory (chats and groups that are gone, new ones that are not
4168
+ * kept): it stops once it has answered, and the next command starts on the emptied profile.
4169
+ */
4170
+ async function clearProfile(ctx) {
4171
+ const { links, groups } = state(ctx);
4172
+ await node(ctx).clearProfileData();
4173
+ const cleared = {
4174
+ chats: links.length,
4175
+ groups: groups.length
4176
+ };
4177
+ if (ctx.mode !== "daemon" || !ctx.stop) return { cleared };
4178
+ setTimeout(() => ctx.stop(), 10);
4179
+ return {
4180
+ cleared,
4181
+ stopped: true
4182
+ };
4183
+ }
4184
+ /**
4102
4185
  * The engine's calls that send a text this side writes (a message, a file's caption, a payment's memo): `engine.call`
4103
4186
  * runs the secret guard of `send`, `edit` and `forward` on them too (docs/CLI.md § Safety), so a raw call is no way around it. `force: true` (`--force`) sends it.
4104
4187
  */
@@ -4244,20 +4327,17 @@ function history(messages, params, json = messageJson) {
4244
4327
  });
4245
4328
  const before = params.before;
4246
4329
  const after = params.after;
4247
- let list = messages.slice().sort((a, b) => a.timestamp - b.timestamp);
4248
- const cut = (value, side) => {
4330
+ const sorted = messages.slice().sort((a, b) => a.timestamp - b.timestamp);
4331
+ const bound = (value, side) => {
4249
4332
  if (value === void 0 || value === null) return;
4250
- if (typeof value === "number") {
4251
- list = list.filter((m) => side === "before" ? m.timestamp < value : m.timestamp > value);
4252
- return;
4253
- }
4333
+ if (typeof value === "number") return (m) => side === "before" ? m.timestamp < value : m.timestamp > value;
4254
4334
  if (typeof value !== "string") throw new CliError("bad_request", `${side} must be a timestamp or a message id`);
4255
- const index = list.findIndex((m) => m.id === value);
4335
+ const index = sorted.findIndex((m) => m.id === value);
4256
4336
  if (index === -1) throw new CliError("not_found", `No message ${value}`);
4257
- list = side === "before" ? list.slice(0, index) : list.slice(index + 1);
4337
+ return (_, at) => side === "before" ? at < index : at > index;
4258
4338
  };
4259
- cut(before, "before");
4260
- cut(after, "after");
4339
+ const bounds = [bound(before, "before"), bound(after, "after")].filter((b) => b !== void 0);
4340
+ const list = sorted.filter((m, at) => bounds.every((within) => within(m, at)));
4261
4341
  const more = after !== void 0 && after !== null ? list.length > limit : list.length > limit;
4262
4342
  return {
4263
4343
  messages: (after !== void 0 && after !== null ? list.slice(0, limit) : list.slice(-limit)).map((m) => json(m)),
@@ -4275,4 +4355,4 @@ async function callApi(ctx, method, params = {}) {
4275
4355
  //#endregion
4276
4356
  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 };
4277
4357
 
4278
- //# sourceMappingURL=api-BCrwJ57L.js.map
4358
+ //# sourceMappingURL=api-JMkEO0RR.js.map