mixin_bot 2.1.0 → 2.2.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 +1 -1
- data/CHANGELOG.md +8 -0
- data/README.md +1 -1
- data/docs/agent/cli.md +5 -1
- data/lib/mixin_bot/api/app.rb +37 -0
- data/lib/mixin_bot/api/user.rb +12 -3
- data/lib/mixin_bot/cli/call.rb +10 -0
- data/lib/mixin_bot/cli/errors.rb +3 -0
- data/lib/mixin_bot/errors.rb +20 -0
- data/lib/mixin_bot/version.rb +1 -1
- data/llms.txt +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2bbc251caedc50e5324a90713c291cf019b2bf0fa003a7ad4df3401af5b175fa
|
|
4
|
+
data.tar.gz: cb7e858d5073bb2fc8bd5e487027fc10a684b8ff05dbc2949aa48270808e1f0a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 758781312e8be1e922eee338ed4ddfd3154c44ea25fb1d7e2b67d9e3ea8298381e4cc4d6df33904c35b831545d136da14c4af1630fdabe7789d3761a1617d6d9
|
|
7
|
+
data.tar.gz: a5350e9812115ef75e39a906e6fb48aa55189692887ee037c0dc3f29cf67b00e70b912575fbe5b9d1f44d93be29c520c2a3142804671c1bcfd3bc96dcfee6b9a
|
data/AGENTS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AGENTS.md — MixinBot
|
|
2
2
|
|
|
3
|
-
Ruby gem (v2.
|
|
3
|
+
Ruby gem (v2.2.0): Mixin Network REST SDK + `mixinbot` CLI. Parity targets: [bot-api-go-client](https://github.com/MixinNetwork/bot-api-go-client), [bot-api-nodejs-client](https://github.com/MixinNetwork/bot-api-nodejs-client).
|
|
4
4
|
|
|
5
5
|
## Commands
|
|
6
6
|
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.2.0] - 2026-05-24
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`MixinBot::API#create_user` billing preflight** — verifies app billing headroom (`credit > cost + next user fee`) via `app_billing` and `app_properties` before `POST /users`. Raises `InsufficientAppBillingError` by default; pass `force: true` to skip. `create_safe_user` forwards `force:` to `create_user`.
|
|
15
|
+
- **`MixinBot::InsufficientAppBillingError`** — structured fields: `app_id`, `credit`, `cost`, `increment`.
|
|
16
|
+
- **CLI** — `mixinbot call create_user ... --force` skips billing preflight; billing failures map to structured error kind `billing`.
|
|
17
|
+
|
|
10
18
|
## [2.1.0] - 2026-05-24
|
|
11
19
|
|
|
12
20
|
### Added
|
data/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Ruby SDK and CLI for [Mixin Network](https://developers.mixin.one/docs): authent
|
|
|
6
6
|
|
|
7
7
|
The gem aims for **parity with the official [bot-api-go-client](https://github.com/MixinNetwork/bot-api-go-client)** Go SDK and **[bot-api-nodejs-client](https://github.com/MixinNetwork/bot-api-nodejs-client)** Node 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
8
|
|
|
9
|
-
Current gem version: **2.
|
|
9
|
+
Current gem version: **2.2.0** (see [CHANGELOG.md](CHANGELOG.md) for breaking changes and deprecations).
|
|
10
10
|
|
|
11
11
|
## Requirements
|
|
12
12
|
|
data/docs/agent/cli.md
CHANGED
|
@@ -72,7 +72,7 @@ Error (stderr, exit 1):
|
|
|
72
72
|
}
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
Error kinds: `invalid_args`, `auth`, `not_found`, `api_error`, `unsupported`, `conflict`, `internal`.
|
|
75
|
+
Error kinds: `invalid_args`, `auth`, `not_found`, `api_error`, `billing`, `unsupported`, `conflict`, `internal`.
|
|
76
76
|
|
|
77
77
|
## Commands
|
|
78
78
|
|
|
@@ -106,8 +106,12 @@ List JSON shape:
|
|
|
106
106
|
mixinbot call me -k keystore.json -o json
|
|
107
107
|
mixinbot call safe_outputs -k keystore.json -d '{"asset":"965e5c6e-434c-3fa9-b780-c50f43cd955c","state":"unspent","limit":10}' -o json
|
|
108
108
|
mixinbot call user USER_UUID -k keystore.json --data-only -o json
|
|
109
|
+
mixinbot call create_user "Bot User" -k keystore.json -o json
|
|
110
|
+
mixinbot call create_user "Bot User" -k keystore.json --force -o json
|
|
109
111
|
```
|
|
110
112
|
|
|
113
|
+
`create_user` performs a client-side app billing preflight by default. When credit lacks headroom for the next billed user, the CLI returns `"kind": "billing"`. Use `--force` to skip the preflight (or pass `"force": true` in `-d`; `-d` wins when both are set).
|
|
114
|
+
|
|
111
115
|
### Raw HTTP
|
|
112
116
|
|
|
113
117
|
```bash
|
data/lib/mixin_bot/api/app.rb
CHANGED
|
@@ -24,6 +24,37 @@ module MixinBot
|
|
|
24
24
|
client.get path, access_token:
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
##
|
|
28
|
+
# Verifies the app has billing headroom before a billed operation (e.g.
|
|
29
|
+
# creating a network user). Skipped when +force+ is true.
|
|
30
|
+
#
|
|
31
|
+
# @param force [Boolean] skip the preflight and call the API anyway
|
|
32
|
+
# @raise [InsufficientAppBillingError] when +credit+ is not greater than
|
|
33
|
+
# total cost plus the next user fee from {app_properties}
|
|
34
|
+
#
|
|
35
|
+
def ensure_app_billing_credit!(force: false, access_token: nil)
|
|
36
|
+
return if force
|
|
37
|
+
|
|
38
|
+
app_id = config.app_id
|
|
39
|
+
billing = app_billing(app_id, access_token:)['data']
|
|
40
|
+
properties = app_properties(access_token:)['data']
|
|
41
|
+
|
|
42
|
+
credit = billing_decimal billing['credit']
|
|
43
|
+
cost_users = billing_decimal billing.dig('cost', 'users')
|
|
44
|
+
cost_resources = billing_decimal billing.dig('cost', 'resources')
|
|
45
|
+
cost = cost_users + cost_resources
|
|
46
|
+
increment = billing_decimal properties['price']
|
|
47
|
+
|
|
48
|
+
return if credit > cost + increment
|
|
49
|
+
|
|
50
|
+
raise InsufficientAppBillingError.new(
|
|
51
|
+
app_id:,
|
|
52
|
+
credit: credit.to_s('F'),
|
|
53
|
+
cost: cost.to_s('F'),
|
|
54
|
+
increment: increment.to_s('F')
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
|
|
27
58
|
def create_app(**kwargs)
|
|
28
59
|
payload = {
|
|
29
60
|
redirect_uri: kwargs[:redirect_uri],
|
|
@@ -95,6 +126,12 @@ module MixinBot
|
|
|
95
126
|
client.post path, user_id: receiver_user_id, pin_base64: tip[:pin_base64] || tip[:pin], access_token:
|
|
96
127
|
end
|
|
97
128
|
alias migrate transfer_app_ownership
|
|
129
|
+
|
|
130
|
+
private
|
|
131
|
+
|
|
132
|
+
def billing_decimal(value)
|
|
133
|
+
BigDecimal(value.to_s)
|
|
134
|
+
end
|
|
98
135
|
end
|
|
99
136
|
end
|
|
100
137
|
end
|
data/lib/mixin_bot/api/user.rb
CHANGED
|
@@ -37,9 +37,14 @@ module MixinBot
|
|
|
37
37
|
#
|
|
38
38
|
# @param full_name [String] display name for the new user
|
|
39
39
|
# @param key [String, nil] optional 32-byte Ed25519 seed
|
|
40
|
+
# @param force [Boolean] when false (default), verify app billing credit
|
|
41
|
+
# headroom before calling the API; when true, skip the preflight
|
|
40
42
|
# @return [Hash] Mixin response merged with the hex-encoded private key
|
|
43
|
+
# @raise [InsufficientAppBillingError] when billing credit lacks headroom
|
|
41
44
|
#
|
|
42
|
-
def create_user(full_name, key: nil)
|
|
45
|
+
def create_user(full_name, key: nil, force: false)
|
|
46
|
+
ensure_app_billing_credit!(force:)
|
|
47
|
+
|
|
43
48
|
keypair = JOSE::JWA::Ed25519.keypair key
|
|
44
49
|
session_secret = Base64.urlsafe_encode64 keypair[0], padding: false
|
|
45
50
|
private_key = keypair[1].unpack1('H*')
|
|
@@ -85,19 +90,23 @@ module MixinBot
|
|
|
85
90
|
# @param name [String] display name for the new user
|
|
86
91
|
# @param private_key [String, nil] optional 32-byte session Ed25519 seed
|
|
87
92
|
# @param spend_key [String, nil] optional 32-byte spend Ed25519 seed
|
|
93
|
+
# @param force [Boolean] forwarded to {#create_user}; see billing preflight
|
|
94
|
+
# there
|
|
88
95
|
# @return [Hash] keystore with +:app_id+, +:session_id+,
|
|
89
96
|
# +:session_private_key+, +:server_public_key+ and +:spend_key+
|
|
90
97
|
# @raise [MixinBot::Error] when registration ultimately fails. Transient
|
|
91
98
|
# PIN/response errors are retried up to {SAFE_REGISTER_MAX_RETRIES}
|
|
92
99
|
# times; other errors bubble up immediately.
|
|
100
|
+
# @raise [InsufficientAppBillingError] when {#create_user} billing
|
|
101
|
+
# preflight fails
|
|
93
102
|
#
|
|
94
|
-
def create_safe_user(name, private_key: nil, spend_key: nil)
|
|
103
|
+
def create_safe_user(name, private_key: nil, spend_key: nil, force: false)
|
|
95
104
|
session_keypair = JOSE::JWA::Ed25519.keypair private_key
|
|
96
105
|
spend_keypair = JOSE::JWA::Ed25519.keypair spend_key
|
|
97
106
|
|
|
98
107
|
spend_key_hex = spend_keypair[1].unpack1('H*')
|
|
99
108
|
|
|
100
|
-
user = create_user name, key: session_keypair[1][...32]
|
|
109
|
+
user = create_user name, key: session_keypair[1][...32], force: force
|
|
101
110
|
data = user.fetch('data')
|
|
102
111
|
|
|
103
112
|
keystore = {
|
data/lib/mixin_bot/cli/call.rb
CHANGED
|
@@ -15,11 +15,13 @@ module MixinBot
|
|
|
15
15
|
LONGDESC
|
|
16
16
|
option :keystore, type: :string, aliases: '-k', desc: 'keystore JSON file path or inline JSON'
|
|
17
17
|
option :data, type: :string, aliases: '-d', default: '{}', desc: 'JSON object of keyword arguments'
|
|
18
|
+
option :force, type: :boolean, default: false, desc: 'Skip billing preflight for create_user (see -d force to override)'
|
|
18
19
|
option :data_only, type: :boolean, default: false, desc: 'Print only the data field of API responses'
|
|
19
20
|
def call(method_name, *positional)
|
|
20
21
|
with_command_name('call') do
|
|
21
22
|
setup_api_instance!
|
|
22
23
|
kwargs = parse_json_data(options[:data])
|
|
24
|
+
kwargs = merge_call_force_kwargs(method_name, kwargs)
|
|
23
25
|
result = invoke_api(method_name, kwargs:, positional:)
|
|
24
26
|
print_result(result, data_only: options[:data_only], command: 'call')
|
|
25
27
|
end
|
|
@@ -55,6 +57,14 @@ module MixinBot
|
|
|
55
57
|
|
|
56
58
|
private
|
|
57
59
|
|
|
60
|
+
def merge_call_force_kwargs(method_name, kwargs)
|
|
61
|
+
return kwargs unless method_name.to_sym == :create_user
|
|
62
|
+
return kwargs if kwargs.key?(:force)
|
|
63
|
+
return kwargs unless options[:force]
|
|
64
|
+
|
|
65
|
+
kwargs.merge(force: true)
|
|
66
|
+
end
|
|
67
|
+
|
|
58
68
|
def print_pretty_list(items, total, limit, offset)
|
|
59
69
|
grouped = items.group_by { |item| item['owner'] }
|
|
60
70
|
grouped.sort_by { |owner, _| owner }.each do |owner, names|
|
data/lib/mixin_bot/cli/errors.rb
CHANGED
|
@@ -12,6 +12,7 @@ module MixinBot
|
|
|
12
12
|
api_error: { retryable: false, description: 'Mixin API returned an error' },
|
|
13
13
|
unsupported: { retryable: false, description: 'Operation is not supported in this context' },
|
|
14
14
|
conflict: { retryable: false, description: 'Resource exists with incompatible configuration' },
|
|
15
|
+
billing: { retryable: false, description: 'App billing credit insufficient for the operation' },
|
|
15
16
|
internal: { retryable: false, description: 'Unexpected internal error' }
|
|
16
17
|
}.freeze
|
|
17
18
|
|
|
@@ -35,6 +36,8 @@ module MixinBot
|
|
|
35
36
|
:auth
|
|
36
37
|
when NotFoundError, UserNotFoundError
|
|
37
38
|
:not_found
|
|
39
|
+
when InsufficientAppBillingError
|
|
40
|
+
:billing
|
|
38
41
|
when ResponseError, RequestError, HttpError,
|
|
39
42
|
InsufficientBalanceError, UtxoInsufficientError, InsufficientPoolError
|
|
40
43
|
:api_error
|
data/lib/mixin_bot/errors.rb
CHANGED
|
@@ -51,6 +51,26 @@ module MixinBot
|
|
|
51
51
|
#
|
|
52
52
|
class InsufficientBalanceError < Error; end
|
|
53
53
|
|
|
54
|
+
##
|
|
55
|
+
# Raised when app prepaid billing credit lacks headroom for a billed operation.
|
|
56
|
+
#
|
|
57
|
+
class InsufficientAppBillingError < Error
|
|
58
|
+
attr_reader :app_id, :credit, :cost, :increment
|
|
59
|
+
|
|
60
|
+
def initialize(app_id:, credit:, cost:, increment:)
|
|
61
|
+
@app_id = app_id
|
|
62
|
+
@credit = credit
|
|
63
|
+
@cost = cost
|
|
64
|
+
@increment = increment
|
|
65
|
+
super(
|
|
66
|
+
format(
|
|
67
|
+
'app billing insufficient: credit %<credit>s <= cost %<cost>s + increment %<increment>s (app_id=%<app_id>s)',
|
|
68
|
+
credit:, cost:, increment:, app_id:
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
54
74
|
##
|
|
55
75
|
# Raised when selected UTXOs cannot cover the requested amount (mirrors Go +UtxoInsufficientError+).
|
|
56
76
|
#
|
data/lib/mixin_bot/version.rb
CHANGED
data/llms.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MixinBot
|
|
2
2
|
|
|
3
|
-
> Ruby SDK and CLI for Mixin Network: Safe UTXO transfers, REST API, Blaze messaging, transaction crypto, optional MVM helpers. Ruby >= 3.2. Gem version 2.
|
|
3
|
+
> Ruby SDK and CLI for Mixin Network: Safe UTXO transfers, REST API, Blaze messaging, transaction crypto, optional MVM helpers. Ruby >= 3.2. Gem version 2.2.0.
|
|
4
4
|
|
|
5
5
|
Important notes:
|
|
6
6
|
|