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.
- checksums.yaml +4 -4
- data/AGENTS.md +75 -0
- data/API_COVERAGE.md +143 -0
- data/CHANGELOG.md +112 -0
- data/README.md +375 -0
- data/docs/agent/cli.md +149 -0
- data/docs/agent/cookbook.md +152 -0
- data/examples/blaze.rb +43 -0
- data/examples/config.yml.example +21 -0
- data/lib/mixin_bot/address.rb +43 -3
- data/lib/mixin_bot/api/app.rb +7 -0
- data/lib/mixin_bot/api/asset.rb +114 -3
- data/lib/mixin_bot/api/auth.rb +19 -10
- data/lib/mixin_bot/api/blaze.rb +81 -0
- data/lib/mixin_bot/api/chain.rb +94 -0
- data/lib/mixin_bot/api/code.rb +16 -0
- data/lib/mixin_bot/api/computer_api.rb +60 -0
- data/lib/mixin_bot/api/conversation.rb +7 -1
- data/lib/mixin_bot/api/deposit.rb +12 -0
- data/lib/mixin_bot/api/encrypted_message.rb +1 -1
- data/lib/mixin_bot/api/fiat.rb +12 -0
- data/lib/mixin_bot/api/inscription.rb +2 -2
- data/lib/mixin_bot/api/legacy_collectible.rb +26 -27
- data/lib/mixin_bot/api/legacy_multisig.rb +20 -21
- data/lib/mixin_bot/api/legacy_output.rb +10 -3
- data/lib/mixin_bot/api/legacy_payment.rb +2 -0
- data/lib/mixin_bot/api/legacy_snapshot.rb +16 -0
- data/lib/mixin_bot/api/legacy_transaction.rb +28 -13
- data/lib/mixin_bot/api/legacy_transfer.rb +11 -8
- data/lib/mixin_bot/api/legacy_user.rb +51 -0
- data/lib/mixin_bot/api/me.rb +99 -3
- data/lib/mixin_bot/api/message.rb +18 -27
- data/lib/mixin_bot/api/multisig.rb +19 -0
- data/lib/mixin_bot/api/network.rb +17 -0
- data/lib/mixin_bot/api/network_asset.rb +27 -0
- data/lib/mixin_bot/api/output.rb +1 -1
- data/lib/mixin_bot/api/pin.rb +16 -3
- data/lib/mixin_bot/api/pin_payload.rb +26 -0
- data/lib/mixin_bot/api/session.rb +14 -0
- data/lib/mixin_bot/api/snapshot.rb +6 -0
- data/lib/mixin_bot/api/tip.rb +74 -1
- data/lib/mixin_bot/api/transaction.rb +106 -17
- data/lib/mixin_bot/api/transfer.rb +141 -14
- data/lib/mixin_bot/api/turn.rb +12 -0
- data/lib/mixin_bot/api/user.rb +148 -45
- data/lib/mixin_bot/api/withdraw.rb +24 -23
- data/lib/mixin_bot/api.rb +248 -3
- data/lib/mixin_bot/bot_auth.rb +71 -0
- data/lib/mixin_bot/cli/api.rb +224 -143
- data/lib/mixin_bot/cli/base.rb +77 -0
- data/lib/mixin_bot/cli/call.rb +71 -0
- data/lib/mixin_bot/cli/errors.rb +56 -0
- data/lib/mixin_bot/cli/node.rb +9 -2
- data/lib/mixin_bot/cli/output.rb +196 -0
- data/lib/mixin_bot/cli/schema.rb +274 -0
- data/lib/mixin_bot/cli/schema_command.rb +21 -0
- data/lib/mixin_bot/cli/utils.rb +114 -18
- data/lib/mixin_bot/cli.rb +124 -48
- data/lib/mixin_bot/client/error_mapper.rb +40 -0
- data/lib/mixin_bot/client.rb +94 -64
- data/lib/mixin_bot/computer.rb +132 -0
- data/lib/mixin_bot/configuration.rb +108 -1
- data/lib/mixin_bot/errors.rb +102 -0
- data/lib/mixin_bot/models/address.rb +11 -0
- data/lib/mixin_bot/models/api_envelope.rb +67 -0
- data/lib/mixin_bot/models/asset.rb +11 -0
- data/lib/mixin_bot/models/ghost_keys.rb +14 -0
- data/lib/mixin_bot/models/output.rb +11 -0
- data/lib/mixin_bot/models/safe_multisig_request.rb +11 -0
- data/lib/mixin_bot/models/sequencer_transaction_request.rb +11 -0
- data/lib/mixin_bot/models/user.rb +11 -0
- data/lib/mixin_bot/models.rb +10 -0
- data/lib/mixin_bot/monitor.rb +77 -0
- data/lib/mixin_bot/transaction/buffer.rb +34 -0
- data/lib/mixin_bot/transaction/decoder.rb +227 -0
- data/lib/mixin_bot/transaction/encoder.rb +255 -0
- data/lib/mixin_bot/transaction.rb +6 -475
- data/lib/mixin_bot/url_scheme.rb +63 -0
- data/lib/mixin_bot/utils/address.rb +17 -80
- data/lib/mixin_bot/utils/crypto.rb +173 -1
- data/lib/mixin_bot/utils/decoder.rb +1 -1
- data/lib/mixin_bot/utils/encoder.rb +13 -0
- data/lib/mixin_bot/utils.rb +45 -0
- data/lib/mixin_bot/uuid.rb +78 -1
- data/lib/mixin_bot/version.rb +11 -1
- data/lib/mixin_bot.rb +172 -18
- data/lib/mvm/bridge.rb +46 -0
- data/lib/mvm/client.rb +60 -0
- data/lib/mvm/nft.rb +4 -2
- data/lib/mvm/registry.rb +2 -1
- data/lib/mvm.rb +93 -0
- data/lib/tasks/api_coverage.rake +20 -0
- data/llms.txt +29 -0
- metadata +77 -9
data/README.md
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
# MixinBot
|
|
2
|
+
|
|
3
|
+
[](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.
|