mixin_bot 1.4.0 → 2.0.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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +75 -0
  3. data/API_COVERAGE.md +143 -0
  4. data/CHANGELOG.md +112 -0
  5. data/README.md +375 -0
  6. data/docs/agent/cli.md +149 -0
  7. data/docs/agent/cookbook.md +152 -0
  8. data/examples/blaze.rb +43 -0
  9. data/examples/config.yml.example +21 -0
  10. data/lib/mixin_bot/address.rb +43 -3
  11. data/lib/mixin_bot/api/app.rb +7 -0
  12. data/lib/mixin_bot/api/asset.rb +114 -3
  13. data/lib/mixin_bot/api/auth.rb +19 -10
  14. data/lib/mixin_bot/api/blaze.rb +81 -0
  15. data/lib/mixin_bot/api/chain.rb +94 -0
  16. data/lib/mixin_bot/api/code.rb +16 -0
  17. data/lib/mixin_bot/api/computer_api.rb +60 -0
  18. data/lib/mixin_bot/api/conversation.rb +7 -1
  19. data/lib/mixin_bot/api/deposit.rb +12 -0
  20. data/lib/mixin_bot/api/encrypted_message.rb +1 -1
  21. data/lib/mixin_bot/api/fiat.rb +12 -0
  22. data/lib/mixin_bot/api/inscription.rb +2 -2
  23. data/lib/mixin_bot/api/legacy_collectible.rb +26 -27
  24. data/lib/mixin_bot/api/legacy_multisig.rb +20 -21
  25. data/lib/mixin_bot/api/legacy_output.rb +10 -3
  26. data/lib/mixin_bot/api/legacy_payment.rb +2 -0
  27. data/lib/mixin_bot/api/legacy_snapshot.rb +16 -0
  28. data/lib/mixin_bot/api/legacy_transaction.rb +28 -13
  29. data/lib/mixin_bot/api/legacy_transfer.rb +11 -8
  30. data/lib/mixin_bot/api/legacy_user.rb +51 -0
  31. data/lib/mixin_bot/api/me.rb +99 -3
  32. data/lib/mixin_bot/api/message.rb +18 -27
  33. data/lib/mixin_bot/api/multisig.rb +19 -0
  34. data/lib/mixin_bot/api/network.rb +17 -0
  35. data/lib/mixin_bot/api/network_asset.rb +27 -0
  36. data/lib/mixin_bot/api/output.rb +1 -1
  37. data/lib/mixin_bot/api/pin.rb +16 -3
  38. data/lib/mixin_bot/api/pin_payload.rb +26 -0
  39. data/lib/mixin_bot/api/session.rb +14 -0
  40. data/lib/mixin_bot/api/snapshot.rb +6 -0
  41. data/lib/mixin_bot/api/tip.rb +74 -1
  42. data/lib/mixin_bot/api/transaction.rb +106 -17
  43. data/lib/mixin_bot/api/transfer.rb +141 -14
  44. data/lib/mixin_bot/api/turn.rb +12 -0
  45. data/lib/mixin_bot/api/user.rb +148 -45
  46. data/lib/mixin_bot/api/withdraw.rb +24 -23
  47. data/lib/mixin_bot/api.rb +248 -3
  48. data/lib/mixin_bot/bot_auth.rb +71 -0
  49. data/lib/mixin_bot/cli/api.rb +224 -143
  50. data/lib/mixin_bot/cli/base.rb +77 -0
  51. data/lib/mixin_bot/cli/call.rb +71 -0
  52. data/lib/mixin_bot/cli/errors.rb +56 -0
  53. data/lib/mixin_bot/cli/node.rb +9 -2
  54. data/lib/mixin_bot/cli/output.rb +196 -0
  55. data/lib/mixin_bot/cli/schema.rb +274 -0
  56. data/lib/mixin_bot/cli/schema_command.rb +21 -0
  57. data/lib/mixin_bot/cli/utils.rb +114 -18
  58. data/lib/mixin_bot/cli.rb +124 -48
  59. data/lib/mixin_bot/client/error_mapper.rb +40 -0
  60. data/lib/mixin_bot/client.rb +94 -64
  61. data/lib/mixin_bot/computer.rb +132 -0
  62. data/lib/mixin_bot/configuration.rb +108 -1
  63. data/lib/mixin_bot/errors.rb +102 -0
  64. data/lib/mixin_bot/models/address.rb +11 -0
  65. data/lib/mixin_bot/models/api_envelope.rb +67 -0
  66. data/lib/mixin_bot/models/asset.rb +11 -0
  67. data/lib/mixin_bot/models/ghost_keys.rb +14 -0
  68. data/lib/mixin_bot/models/output.rb +11 -0
  69. data/lib/mixin_bot/models/safe_multisig_request.rb +11 -0
  70. data/lib/mixin_bot/models/sequencer_transaction_request.rb +11 -0
  71. data/lib/mixin_bot/models/user.rb +11 -0
  72. data/lib/mixin_bot/models.rb +10 -0
  73. data/lib/mixin_bot/monitor.rb +77 -0
  74. data/lib/mixin_bot/transaction/buffer.rb +34 -0
  75. data/lib/mixin_bot/transaction/decoder.rb +227 -0
  76. data/lib/mixin_bot/transaction/encoder.rb +255 -0
  77. data/lib/mixin_bot/transaction.rb +6 -475
  78. data/lib/mixin_bot/url_scheme.rb +63 -0
  79. data/lib/mixin_bot/utils/address.rb +17 -80
  80. data/lib/mixin_bot/utils/crypto.rb +173 -1
  81. data/lib/mixin_bot/utils/decoder.rb +1 -1
  82. data/lib/mixin_bot/utils/encoder.rb +13 -0
  83. data/lib/mixin_bot/utils.rb +45 -0
  84. data/lib/mixin_bot/uuid.rb +78 -1
  85. data/lib/mixin_bot/version.rb +11 -1
  86. data/lib/mixin_bot.rb +172 -18
  87. data/lib/mvm/bridge.rb +46 -0
  88. data/lib/mvm/client.rb +60 -0
  89. data/lib/mvm/nft.rb +4 -2
  90. data/lib/mvm/registry.rb +2 -1
  91. data/lib/mvm.rb +93 -0
  92. data/lib/tasks/api_coverage.rake +20 -0
  93. data/llms.txt +29 -0
  94. metadata +77 -9
data/README.md ADDED
@@ -0,0 +1,375 @@
1
+ # MixinBot
2
+
3
+ [![CI](https://github.com/an-lee/mixin_bot/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/an-lee/mixin_bot/actions/workflows/ci.yml)
4
+
5
+ Ruby SDK and CLI for [Mixin Network](https://developers.mixin.one/docs): authenticated REST calls, **Safe** UTXO transfers, Blaze messaging, network asset catalog, inscriptions, invoices and mix addresses, transaction encoding, and optional **MVM** (Mixin Virtual Machine) helpers.
6
+
7
+ The gem aims for **parity with the official [bot-api-go-client](https://github.com/MixinNetwork/bot-api-go-client)** Go SDK. See [API_COVERAGE.md](API_COVERAGE.md) for the full mapping; run `rake mixin_bot:api_coverage` to confirm no gaps are marked missing.
8
+
9
+ Current gem version: **2.0.0** (see [CHANGELOG.md](CHANGELOG.md) for breaking changes and deprecations).
10
+
11
+ ## Requirements
12
+
13
+ - **Ruby** ≥ 3.2 (CI runs 3.2, 3.3, and 4.0).
14
+ - **Bundler** 2.5+ recommended, especially on Ruby 4.
15
+ - Optional: the **`mixin`** CLI in `PATH` if you use `MixinBot::API#encode_raw_transaction_native` / `#decode_raw_transaction_native` or the experimental `MixinBot::NodeCLI` helpers.
16
+
17
+ ## Installation
18
+
19
+ Add to your Gemfile:
20
+
21
+ ```ruby
22
+ gem 'mixin_bot'
23
+ ```
24
+
25
+ Then:
26
+
27
+ ```bash
28
+ bundle install
29
+ ```
30
+
31
+ Or install the gem directly:
32
+
33
+ ```bash
34
+ gem install mixin_bot
35
+ ```
36
+
37
+ The gem ships the `mixinbot` executable (see [CLI](#cli)).
38
+
39
+ ## Quick start
40
+
41
+ ### 1. Configure credentials
42
+
43
+ Set the fields your flows need. **Safe** transfers and signing require a **spend key** (`spend_key`) in addition to session material.
44
+
45
+ ```ruby
46
+ require 'mixin_bot'
47
+
48
+ MixinBot.configure do
49
+ self.app_id = 'your-app-uuid'
50
+ self.client_secret = 'your-client-secret' # OAuth flows
51
+ self.session_id = 'your-session-uuid'
52
+ self.session_private_key = '...' # seed or full Ed25519 private key; Base64 or hex
53
+ self.server_public_key = '...' # pin token / server public key
54
+ self.spend_key = '...' # Ed25519 spend private key for Safe UTXO signing
55
+ # self.pin = self.spend_key # optional; used where PIN material is required
56
+ # self.api_host = 'api.mixin.one'
57
+ # self.blaze_host = 'blaze.mixin.one'
58
+ # self.debug = true # Faraday response logging
59
+ end
60
+ ```
61
+
62
+ `MixinBot::Configuration` accepts common aliases: `client_id` → `app_id`, `private_key` → `session_private_key`, `pin_token` → `server_public_key`. Keys are normalized (e.g. 32-byte Ed25519 seeds expanded to 64-byte signing keys) where appropriate.
63
+
64
+ ### 2. Call the API
65
+
66
+ ```ruby
67
+ api = MixinBot.api # singleton using global config, or MixinBot::API.new(...)
68
+
69
+ api.me['full_name']
70
+ api.assets
71
+ api.network_asset('c6d0c728-2624-429b-8e0d-d9d19b6592fa') # public network catalog
72
+ api.fetch_user_sessions(['user-uuid'])
73
+ ```
74
+
75
+ ### 3. Send assets (Safe API, recommended)
76
+
77
+ `create_transfer` with keyword arguments runs the **Safe** pipeline (UTXO select → build → verify → sign → submit). You can also call `create_safe_transfer` explicitly. Configure **`spend_key`** first.
78
+
79
+ ```ruby
80
+ result = MixinBot.api.create_transfer(
81
+ members: '6ae1c7ae-1df1-498e-8f21-d48cb6d129b5',
82
+ asset_id: '965e5c6e-434c-3fa9-b780-c50f43cd955c',
83
+ amount: '0.01',
84
+ memo: 'payment',
85
+ trace_id: SecureRandom.uuid
86
+ )
87
+
88
+ # Multisig: 2-of-3
89
+ MixinBot.api.create_safe_transfer(
90
+ members: %w[uuid-1 uuid-2 uuid-3],
91
+ threshold: 2,
92
+ asset_id: '965e5c6e-434c-3fa9-b780-c50f43cd955c',
93
+ amount: '0.01'
94
+ )
95
+ ```
96
+
97
+ Lower-level steps: `build_utxos`, `build_safe_transaction`, `verify_raw_transaction` / `create_safe_transaction_request`, `sign_safe_transaction`, `send_safe_transaction` (batch via `requests:`).
98
+
99
+ Aliases aligned with the Go SDK include `send_transaction`, `send_transfer_transaction`, and `get_transaction_by_id` (→ `safe_transaction`).
100
+
101
+ ### 4. Legacy `POST /transfers`
102
+
103
+ If the first argument is a **PIN string** and you pass `opponent_id:` (old Messenger transfer shape), `create_transfer` delegates to **`create_legacy_transfer`** (deprecated, warns once). Prefer Safe transfers for new code.
104
+
105
+ ```ruby
106
+ # Legacy only — deprecated
107
+ MixinBot.api.create_legacy_transfer(
108
+ pin,
109
+ asset_id: '...',
110
+ opponent_id: '...',
111
+ amount: 0.00000001,
112
+ trace_id: SecureRandom.uuid
113
+ )
114
+ ```
115
+
116
+ ## HTTP responses (`ApiEnvelope`)
117
+
118
+ `MixinBot::Client` returns **`MixinBot::Models::ApiEnvelope`** for REST calls. It wraps the raw JSON so you can use either envelope or flattened shapes:
119
+
120
+ ```ruby
121
+ res = MixinBot.api.client.get('/me')
122
+ res['data']['user_id'] # envelope
123
+ res['user_id'] # delegated lookup into `data` when present
124
+ res.to_h # raw Hash
125
+ ```
126
+
127
+ Many convenience methods on `MixinBot::API` still return the **inner `data` hash** where that was the historical contract (e.g. `#me`).
128
+
129
+ ## Library layout
130
+
131
+ ### `MixinBot::API` modules
132
+
133
+ `MixinBot::API` composes one module per API area (all methods are available on `MixinBot.api`):
134
+
135
+ | Module | Examples |
136
+ |--------|----------|
137
+ | **Me** | `me`, `safe_me`, `update_me`, `friends`, `update_preferences`, `relationship` |
138
+ | **User** | `user`, `fetch_users`, `search_user`, `create_user`, `safe_register`, `migrate_to_safe` |
139
+ | **Session** | `fetch_user_sessions` |
140
+ | **LegacyUser** | `upgrade_legacy_user` |
141
+ | **Asset** | `assets`, `asset`, `ticker`, `fetch_assets`, `asset_fee`, `asset_balance` |
142
+ | **NetworkAsset** | `network_asset`, `network_ticker`, `network_asset_search` |
143
+ | **Network** | `network_assets`, `network_assets_top` |
144
+ | **Fiat** | `fiats` |
145
+ | **Chain** | `network_chain`, `network_chains`, `chain_name`, `chain_id?` |
146
+ | **Transfer** | `create_transfer`, `create_safe_transfer`, `build_utxos`, `send_transaction`, … |
147
+ | **Transaction** | `create_safe_keys`, `build_safe_transaction`, `verify_raw_transaction`, `sign_safe_transaction`, `build_object_transaction`, `create_object_storage_transaction`, … |
148
+ | **Output** | `safe_outputs`, `safe_output`, `build_threshold_script` |
149
+ | **Deposit** | `pending_safe_deposits` |
150
+ | **Address** (deposit entries) | `safe_deposit_entries` |
151
+ | **Snapshot** | `safe_snapshots`, `safe_snapshot`, `create_safe_snapshot_notification` |
152
+ | **LegacySnapshot** | `snapshots`, `snapshot`, `snapshot_by_trace_id`, `network_snapshots`, … |
153
+ | **Payment** | `safe_pay_url` |
154
+ | **LegacyPayment** | `pay_url`, `verify_payment` (deprecated) |
155
+ | **Multisig** | `create_safe_multisig_request`, `safe_multisig_request`, `create_multisig_raw_tx` |
156
+ | **LegacyMultisig** | `create_multisig_request`, `cancel_multisig_request`, … |
157
+ | **LegacyOutput** | `legacy_outputs`, `read_multisigs`, `create_output`, … |
158
+ | **LegacyTransfer** | `create_legacy_transfer`, `legacy_transfer` |
159
+ | **LegacyTransaction** | `build_raw_transaction`, `create_multisig_transaction`, … |
160
+ | **Inscription** | `collection`, `collectible`, `build_inscribe_transaction`, … |
161
+ | **LegacyCollectible** | legacy collectible requests (deprecated) |
162
+ | **Withdraw** | `withdrawals`, `create_withdraw_address`, `check_address`, `withdraw_addresses` |
163
+ | **Conversation** | `conversation`, `create_group_conversation`, `join_conversation`, … |
164
+ | **Message** | `send_message`, `send_plain_messages`, Blaze helpers |
165
+ | **EncryptedMessage** | `send_encrypted_*`, `encrypt_message`, `decrypt_message` |
166
+ | **Blaze** | `blaze`, `start_blaze_connect`, `blaze_send_plain_text`, … |
167
+ | **Attachment** | `create_attachment`, `upload_attachment` |
168
+ | **Auth** | `oauth_token`, `authorize_code`, `access_token`, `sign_oauth_access_token` |
169
+ | **Pin** / **Tip** | `verify_pin`, `update_pin`, `update_tip_pin`, `encrypt_tip_pin`, `get_tip_node`, `tip_body_for_*` |
170
+ | **App** | `favorite_apps`, `transfer_app_ownership` (`migrate`) |
171
+ | **Code** | `read_code`, `read_multisig_by_code` |
172
+ | **Turn** | `turn_servers` |
173
+ | **Rpc** | `rpc_proxy`, `send_raw_transaction`, `get_transaction`, … |
174
+ | **ComputerApi** | delegates to `MixinBot::Computer` (`get_computer_info`, `register_computer`, …) |
175
+
176
+ Top-level helpers on **`MixinBot::API`**: `access_token`, `encode_raw_transaction`, `decode_raw_transaction`, native variants via `mixin` CLI.
177
+
178
+ ### Other libraries
179
+
180
+ | Area | Description |
181
+ |------|-------------|
182
+ | **`MixinBot::Client`** | Faraday HTTP client (JSON, retries, optional debug). |
183
+ | **`MixinBot::Configuration`** | Credentials and hosts. |
184
+ | **`MixinBot::Utils`** | Crypto, JWT, encoding, `unique_object_id`, `generate_user_checksum`, … |
185
+ | **`MixinBot::Transaction`** | Encode/decode raw Safe transactions. |
186
+ | **`MixinBot::MixAddress`** | Parse/build `MIX…` addresses; `request_or_generate_ghost_keys`. |
187
+ | **`MixinBot::Invoice`** | `MIN…` payment invoices. |
188
+ | **`MixinBot::UrlScheme`** | `mixin://` deep links (`scheme_users`, `scheme_pay`, …). |
189
+ | **`MixinBot::Computer`** | [Mixin Computer](https://computer.mixin.one) HTTP API (separate host). |
190
+ | **`MixinBot::BotAuth`** | Sign bot-platform requests (`BotAuth::Client#sign_request`). |
191
+ | **`MixinBot::Monitor`** | YAML monitor messages, `report_to_monitor`, `check_retryable_error`. |
192
+ | **`MixinBot::UUID`**, **`MixinBot::Nfo`** | UUID and NFT memo helpers. |
193
+ | **`MVM`** | Optional MVM namespace: `MVM::Bridge`, `MVM::Nft`, `MVM::Scan`, `MVM::Registry`. |
194
+
195
+ ### Errors
196
+
197
+ Custom errors under `MixinBot::` include `ResponseError`, `UnauthorizedError`, `InsufficientBalanceError`, `UtxoInsufficientError`, `PinError`, and `InvalidInvoiceFormatError`. See `lib/mixin_bot/errors.rb`.
198
+
199
+ ### Multiple bots
200
+
201
+ ```ruby
202
+ bot_a = MixinBot::API.new(
203
+ app_id: '...',
204
+ session_id: '...',
205
+ session_private_key: '...',
206
+ server_public_key: '...',
207
+ spend_key: '...'
208
+ )
209
+
210
+ bot_b = MixinBot::API.new(...) # separate configuration
211
+
212
+ bot_a.me
213
+ bot_b.me
214
+ ```
215
+
216
+ ## Blaze (WebSocket)
217
+
218
+ Blaze uses a WebSocket after JWT auth. `start_blaze_connect` yields a `Faye::WebSocket::Client`; define `on_open` / `on_message` / `on_error` / `on_close` on the receiver (see `examples/blaze.rb`). Run an event loop (e.g. **EventMachine**) in your app.
219
+
220
+ ```ruby
221
+ require 'eventmachine'
222
+ require 'mixin_bot'
223
+
224
+ EM.run do
225
+ MixinBot.api.start_blaze_connect do
226
+ def on_open(blaze, _event)
227
+ blaze.send list_pending_message
228
+ end
229
+
230
+ def on_message(blaze, event)
231
+ raw = JSON.parse ws_message(event.data)
232
+ blaze.send acknowledge_message_receipt(raw['data']['message_id']) if raw.dig('data', 'message_id')
233
+ end
234
+ end
235
+ end
236
+ ```
237
+
238
+ For outbound messages over an open socket, use `blaze_send_plain_text`, `blaze_send_contact`, `blaze_send_app_card`, and related helpers (parity with Go `BlazeClient`).
239
+
240
+ ## Deep links and bot auth
241
+
242
+ ```ruby
243
+ MixinBot::UrlScheme.scheme_pay(
244
+ asset_id: '...',
245
+ trace_id: SecureRandom.uuid,
246
+ recipient_id: '...',
247
+ memo: 'hello',
248
+ amount: '0.01'
249
+ )
250
+
251
+ client = MixinBot::BotAuth.new_client(MixinBot.api)
252
+ token = client.sign_request(Time.now.to_i, bot_user_id, 'GET', '/some/path')
253
+ ```
254
+
255
+ ## CLI
256
+
257
+ Invoke **`mixinbot`** (global options: `-a` / `--apihost`, `-o` / `--output pretty|json|yaml`, `-r` / `--pretty`).
258
+
259
+ When stdout is piped, output defaults to JSON. Use `mixinbot schema -o json` for machine-readable command discovery. See [docs/agent/cli.md](docs/agent/cli.md).
260
+
261
+ Subcommands that talk to the API accept **`-k`** / **`--keystore`**: path to a JSON file **or** inline JSON (`app_id`, `session_id`, `session_private_key`, `server_public_key`, `spend_key`, `client_secret`, `pin`, etc.). Without `-k`, the CLI uses global `MixinBot.configure` credentials.
262
+
263
+ | Command | Purpose |
264
+ |---------|---------|
265
+ | `mixinbot call METHOD` | Invoke any `MixinBot::API` method (`-d` JSON kwargs, optional positional args). |
266
+ | `mixinbot list [FILTER]` | List callable API methods (grouped by module). |
267
+ | `mixinbot api PATH` | Signed `GET`/`POST` via `MixinBot::Client` (`-m`, `-d`, `-p`, `-t`). |
268
+ | `mixinbot transfer USER_ID` | Safe transfer (`create_safe_transfer`; `--asset`, `--amount`, …). |
269
+ | `mixinbot legacy-transfer USER_ID` | Deprecated `POST /transfers`. |
270
+ | `mixinbot safetransfer USER_ID` | Alias for `transfer` (deprecated name). |
271
+ | `mixinbot authcode` | OAuth authorize code (`-c`, `-s`). |
272
+ | `mixinbot encrypt PIN` / `verifypin` / `updatetip` | PIN/TIP helpers. |
273
+ | `mixinbot saferegister` | Safe registration (`--spend_key`). |
274
+ | `mixinbot pay` | Safe payment URL. |
275
+ | `mixinbot utils call METHOD` | Invoke any `MixinBot.utils` method (`-d` JSON kwargs). |
276
+ | `mixinbot utils list [FILTER]` | List utils methods. |
277
+ | `mixinbot unique UUID …` | Deterministic UUID. |
278
+ | `mixinbot generatetrace HASH` | Trace UUID from tx hash. |
279
+ | `mixinbot decodetx HEX` | Decode raw transaction. |
280
+ | `mixinbot nftmemo` | NFT mint memo. |
281
+ | `mixinbot rsa` / `ed25519` | Key generation. |
282
+ | `mixinbot version` | Gem version. |
283
+
284
+ Examples:
285
+
286
+ ```bash
287
+ mixinbot call me -k ~/.mixinbot/keystore.json
288
+ mixinbot call safe_outputs -k keystore.json -d '{"asset":"965e5c6e-434c-3fa9-b780-c50f43cd955c","state":"unspent"}'
289
+ mixinbot transfer USER_ID -k keystore.json --asset ASSET_ID --amount 0.01
290
+ ```
291
+
292
+ Run `mixinbot help` and `mixinbot help COMMAND` for details.
293
+
294
+ ## For AI agents / LLMs
295
+
296
+ - **[llms.txt](llms.txt)** — curated documentation index ([llmstxt.org](https://llmstxt.org/) format)
297
+ - **[AGENTS.md](AGENTS.md)** — repository layout, conventions, and workflows for coding agents
298
+ - **[docs/agent/cli.md](docs/agent/cli.md)** — structured `mixinbot` output, schema introspection, JSON examples
299
+ - **[docs/agent/cookbook.md](docs/agent/cookbook.md)** — task recipes (transfers, auth, messaging)
300
+
301
+ Run `mixinbot schema -o json` to discover CLI commands programmatically.
302
+
303
+ ## Documentation
304
+
305
+ - **API coverage** — [API_COVERAGE.md](API_COVERAGE.md) vs [bot-api-go-client](https://github.com/MixinNetwork/bot-api-go-client).
306
+ - **RDoc** — `rake rdoc` → `doc/index.html`.
307
+ - **Online** — [RubyDoc.info](https://www.rubydoc.info/gems/mixin_bot).
308
+ - **Changelog** — [CHANGELOG.md](CHANGELOG.md).
309
+
310
+ ## Development & tests
311
+
312
+ ```bash
313
+ git clone https://github.com/an-lee/mixin_bot.git
314
+ cd mixin_bot
315
+ bundle install
316
+ ```
317
+
318
+ - **Default suite** (offline WebMock stubs):
319
+
320
+ ```bash
321
+ rake test
322
+ ```
323
+
324
+ - **API coverage check**:
325
+
326
+ ```bash
327
+ rake mixin_bot:api_coverage
328
+ ```
329
+
330
+ - **RuboCop** — `rake` runs tests + RuboCop.
331
+
332
+ - **Live API** (optional):
333
+
334
+ ```bash
335
+ cp test/config.yml.example test/config.yml
336
+ LIVE=1 rake test
337
+ # or: rake test_live
338
+ ```
339
+
340
+ Examples under `examples/` expect `examples/config.yml` (copy from `examples/config.yml.example`).
341
+
342
+ ### CI
343
+
344
+ GitHub Actions runs on every pull request and on pushes to `main`:
345
+
346
+ - **Test** — Ruby 3.2, 3.3, and 4.0: `bundle exec rake test`
347
+ - **RuboCop** — Ruby 3.3: `bundle exec rake rubocop`
348
+ - **API coverage** — `bundle exec rake mixin_bot:api_coverage`
349
+
350
+ ### Release
351
+
352
+ Publishing to [RubyGems.org](https://rubygems.org/gems/mixin_bot) is automated when a version tag is pushed:
353
+
354
+ 1. Bump `MixinBot::VERSION` in `lib/mixin_bot/version.rb` and update `CHANGELOG.md`.
355
+ 2. Commit and push to `main`.
356
+ 3. Create and push a tag matching the gem version (e.g. `v2.0.1` for version `2.0.1`):
357
+
358
+ ```bash
359
+ git tag v2.0.1
360
+ git push origin v2.0.1
361
+ ```
362
+
363
+ The [Release workflow](.github/workflows/release.yml) builds the gem and publishes to RubyGems.org via [trusted publishing](https://guides.rubygems.org/trusted-publishing/) (GitHub OIDC; trusted publisher for workflow `release.yml` on `an-lee/mixin_bot`). To build without publishing, run the Release workflow manually with **dry run** enabled.
364
+
365
+ ## References
366
+
367
+ - [Mixin developers documentation](https://developers.mixin.one/docs)
368
+ - [Mixin API overview](https://developers.mixin.one/api)
369
+ - [bot-api-go-client](https://github.com/MixinNetwork/bot-api-go-client) (official Go SDK; parity reference)
370
+ - [mixin_client_demo (Python)](https://github.com/myrual/mixin_client_demo)
371
+ - [mixin-node (Node.js)](https://github.com/virushuo/mixin-node)
372
+
373
+ ## License
374
+
375
+ MIT — see [MIT-LICENSE](MIT-LICENSE).
data/docs/agent/cli.md ADDED
@@ -0,0 +1,149 @@
1
+ # mixinbot CLI — agent guide
2
+
3
+ The `mixinbot` executable wraps `MixinBot::API` and utilities for scripting and agent automation.
4
+
5
+ ## Global options
6
+
7
+ | Flag | Description |
8
+ |------|-------------|
9
+ | `-a`, `--apihost` | Mixin API host (default: `api.mixin.one`) |
10
+ | `-o`, `--output` | Output format: `pretty`, `json`, `yaml` |
11
+ | `-r`, `--pretty` | Alias for `--output pretty` (default in TTY) |
12
+
13
+ **Auto-detect**: when `--output` is omitted, TTY → `pretty`, piped → `json`.
14
+
15
+ Environment:
16
+
17
+ - `MIXINBOT_NO_SPINNER=1` — disable progress spinners
18
+
19
+ ## Keystore
20
+
21
+ API commands accept `-k` / `--keystore`: path to JSON or inline JSON:
22
+
23
+ ```json
24
+ {
25
+ "app_id": "uuid",
26
+ "session_id": "uuid",
27
+ "session_private_key": "hex-or-base64",
28
+ "server_public_key": "hex-or-base64",
29
+ "spend_key": "hex",
30
+ "client_secret": "...",
31
+ "pin": "123456"
32
+ }
33
+ ```
34
+
35
+ Aliases: `client_id` → `app_id`, `private_key` → `session_private_key`, `pin_token` → `server_public_key`.
36
+
37
+ Without `-k`, the CLI uses global `MixinBot.configure` credentials.
38
+
39
+ ## Schema introspection
40
+
41
+ Discover commands without parsing `--help`:
42
+
43
+ ```bash
44
+ mixinbot schema -o json
45
+ mixinbot schema -o json | jq '.commands[].name'
46
+ ```
47
+
48
+ Returns clispec-shaped JSON: `name`, `version`, `commands[]` (with `mutating`, `args`), `errors[]`.
49
+
50
+ ## Structured output envelope
51
+
52
+ Success (stdout):
53
+
54
+ ```json
55
+ {
56
+ "status": "ok",
57
+ "command": "call",
58
+ "data": { ... }
59
+ }
60
+ ```
61
+
62
+ Error (stderr, exit 1):
63
+
64
+ ```json
65
+ {
66
+ "status": "error",
67
+ "error": {
68
+ "kind": "invalid_args",
69
+ "message": "unknown or unsupported API method: foo",
70
+ "hint": "mixinbot list"
71
+ }
72
+ }
73
+ ```
74
+
75
+ Error kinds: `invalid_args`, `auth`, `not_found`, `api_error`, `unsupported`, `conflict`, `internal`.
76
+
77
+ ## Commands
78
+
79
+ ### Discovery
80
+
81
+ ```bash
82
+ mixinbot list # all callable API methods
83
+ mixinbot list transfer -o json --limit 10 --offset 0
84
+ mixinbot list transfer -o json --fields name,owner
85
+ mixinbot utils list -o json --limit 20
86
+ ```
87
+
88
+ List JSON shape:
89
+
90
+ ```json
91
+ {
92
+ "status": "ok",
93
+ "command": "list",
94
+ "data": {
95
+ "items": [{"name": "create_safe_transfer", "owner": "MixinBot::API::Transfer"}],
96
+ "total": 142,
97
+ "limit": 10,
98
+ "offset": 0
99
+ }
100
+ }
101
+ ```
102
+
103
+ ### API invocation
104
+
105
+ ```bash
106
+ mixinbot call me -k keystore.json -o json
107
+ mixinbot call safe_outputs -k keystore.json -d '{"asset":"965e5c6e-434c-3fa9-b780-c50f43cd955c","state":"unspent","limit":10}' -o json
108
+ mixinbot call user USER_UUID -k keystore.json --data-only -o json
109
+ ```
110
+
111
+ ### Raw HTTP
112
+
113
+ ```bash
114
+ mixinbot api /me -k keystore.json -o json
115
+ mixinbot api /search/1051445 -k keystore.json -o json
116
+ mixinbot api /payments -k keystore.json -m POST -d '{"asset_id":"..."}' -o json
117
+ ```
118
+
119
+ ### Transfers (mutating)
120
+
121
+ ```bash
122
+ mixinbot transfer USER_ID -k keystore.json --asset ASSET_ID --amount 0.01 -o json
123
+ mixinbot transfer USER_ID -k keystore.json --asset ASSET_ID --amount 0.01 --dry-run -o json
124
+ ```
125
+
126
+ `--dry-run` validates keystore and prints kwargs without submitting.
127
+
128
+ ### Utilities
129
+
130
+ ```bash
131
+ mixinbot utils call unique_uuid -d '{"uuids":["uuid1","uuid2"]}' -o json
132
+ mixinbot unique UUID1 UUID2 -o json
133
+ mixinbot decodetx HEX -o json
134
+ mixinbot version -o json
135
+ ```
136
+
137
+ ## Piping
138
+
139
+ ```bash
140
+ mixinbot version | jq .status
141
+ mixinbot list --limit 5 | jq '.data.items[].name'
142
+ mixinbot schema | jq '.commands[] | select(.mutating) | .name'
143
+ ```
144
+
145
+ Diagnostics (spinners, warnings) go to **stderr**; data goes to **stdout**.
146
+
147
+ ## Non-interactive
148
+
149
+ All commands run without prompts. Blaze WebSocket (`blaze`, `start_blaze_connect`) is **not** available via CLI — use the Ruby API.
@@ -0,0 +1,152 @@
1
+ # MixinBot agent cookbook
2
+
3
+ Task-oriented recipes. Full API reference: [README](../../README.md), [API_COVERAGE.md](../../API_COVERAGE.md).
4
+
5
+ ## Configure credentials (Ruby)
6
+
7
+ ```ruby
8
+ require 'mixin_bot'
9
+
10
+ MixinBot.configure do
11
+ self.app_id = 'your-app-uuid'
12
+ self.session_id = 'your-session-uuid'
13
+ self.session_private_key = '...'
14
+ self.server_public_key = '...'
15
+ self.spend_key = '...' # required for Safe transfers
16
+ self.client_secret = '...' # OAuth flows
17
+ end
18
+
19
+ api = MixinBot.api
20
+ api.me
21
+ ```
22
+
23
+ ## Call /me (CLI)
24
+
25
+ ```bash
26
+ mixinbot call me -k keystore.json -o json --data-only
27
+ # or
28
+ mixinbot api /me -k keystore.json -o json
29
+ ```
30
+
31
+ ## Safe transfer
32
+
33
+ **Ruby** (recommended):
34
+
35
+ ```ruby
36
+ MixinBot.api.create_transfer(
37
+ members: 'recipient-uuid',
38
+ asset_id: '965e5c6e-434c-3fa9-b780-c50f43cd955c',
39
+ amount: '0.01',
40
+ memo: 'payment',
41
+ trace_id: SecureRandom.uuid
42
+ )
43
+ ```
44
+
45
+ **CLI**:
46
+
47
+ ```bash
48
+ mixinbot transfer RECIPIENT_UUID \
49
+ -k keystore.json \
50
+ --asset 965e5c6e-434c-3fa9-b780-c50f43cd955c \
51
+ --amount 0.01 \
52
+ --memo "payment" \
53
+ -o json
54
+ ```
55
+
56
+ Preview without submitting:
57
+
58
+ ```bash
59
+ mixinbot transfer RECIPIENT_UUID -k keystore.json \
60
+ --asset ASSET_ID --amount 0.01 --dry-run -o json
61
+ ```
62
+
63
+ ## OAuth authorization code
64
+
65
+ ```bash
66
+ mixinbot authcode -k keystore.json -c TARGET_APP_ID -s PROFILE:READ -o json
67
+ ```
68
+
69
+ ## PIN / TIP
70
+
71
+ ```bash
72
+ mixinbot verifypin 123456 -k keystore.json -o json
73
+ mixinbot encrypt 123456 -k keystore.json -o json
74
+ mixinbot updatetip NEW_PIN -k keystore.json -o json
75
+ ```
76
+
77
+ ## Safe registration
78
+
79
+ ```bash
80
+ mixinbot saferegister -k keystore.json --spend_key SPEND_KEY_HEX -o json
81
+ ```
82
+
83
+ ## Payment URL
84
+
85
+ ```bash
86
+ mixinbot pay \
87
+ --members RECIPIENT_UUID \
88
+ --asset ASSET_ID \
89
+ --amount 0.01 \
90
+ -o json
91
+ ```
92
+
93
+ ## Decode transaction
94
+
95
+ ```bash
96
+ mixinbot decodetx RAW_TX_HEX -o json
97
+ mixinbot utils call decode_raw_transaction -d '{"raw":"..."}' -o json
98
+ ```
99
+
100
+ ## List UTXOs
101
+
102
+ ```bash
103
+ mixinbot call safe_outputs -k keystore.json \
104
+ -d '{"asset":"965e5c6e-434c-3fa9-b780-c50f43cd955c","state":"unspent","limit":10}' \
105
+ -o json --data-only
106
+ ```
107
+
108
+ ## Blaze messaging (Ruby only)
109
+
110
+ Blaze requires WebSocket + EventMachine. **Not supported via CLI.**
111
+
112
+ ```ruby
113
+ require 'eventmachine'
114
+ require 'mixin_bot'
115
+
116
+ EM.run do
117
+ MixinBot.api.start_blaze_connect do
118
+ def on_open(blaze, _event)
119
+ blaze.send list_pending_message
120
+ end
121
+
122
+ def on_message(blaze, event)
123
+ raw = JSON.parse ws_message(event.data)
124
+ mid = raw.dig('data', 'message_id')
125
+ blaze.send acknowledge_message_receipt(mid) if mid
126
+ end
127
+ end
128
+ end
129
+ ```
130
+
131
+ See [examples/blaze.rb](../../examples/blaze.rb).
132
+
133
+ ## Discover API methods
134
+
135
+ ```bash
136
+ mixinbot list transfer -o json --limit 20
137
+ mixinbot schema -o json | jq '.commands[] | select(.name == "call")'
138
+ ```
139
+
140
+ ## Error handling
141
+
142
+ ```ruby
143
+ begin
144
+ MixinBot.api.create_safe_transfer(...)
145
+ rescue MixinBot::InsufficientBalanceError => e
146
+ # handle
147
+ rescue MixinBot::UnauthorizedError => e
148
+ # check credentials
149
+ end
150
+ ```
151
+
152
+ CLI errors emit JSON to stderr with `error.kind` when output is structured.