muxi 0.20260211.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 +7 -0
- data/.rubocop.yml +28 -0
- data/CHANGELOG.md +14 -0
- data/README.md +160 -0
- data/Rakefile +10 -0
- data/SECURITY.md +28 -0
- data/USER_GUIDE.md +151 -0
- data/lib/muxi/auth.rb +27 -0
- data/lib/muxi/errors.rb +55 -0
- data/lib/muxi/formation_client.rb +546 -0
- data/lib/muxi/server_client.rb +165 -0
- data/lib/muxi/transport.rb +190 -0
- data/lib/muxi/version.rb +5 -0
- data/lib/muxi/version_check.rb +85 -0
- data/lib/muxi/webhook.rb +135 -0
- data/lib/muxi.rb +20 -0
- data/muxi.gemspec +41 -0
- metadata +148 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 2a71c3de149de10344234f91e906aa9282499252f85f9d732e7e786619675ac3
|
|
4
|
+
data.tar.gz: 46903422d8696f82902658d44817332a6ba6e2f7cbe5b1591b298215b57de7b6
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 5edbf6f9ef3eccec33b8d4bba841f0eab3af71714a06a7516ba4ab4c3bdbc6319b1ad2f61ebfcb2e1adc6dd841a021aab7f2fb2f87f897dd9f9437c7292d78ca
|
|
7
|
+
data.tar.gz: 512e17859b7b6349ee81d3e023ee53bcdb8982298eecbafe47c6e8c3e0bfd85fe64d8979ff2bae3568f00e714d26ef183eb85ef4da4f8ad3ecd302537ff69b7a
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 3.0
|
|
3
|
+
NewCops: enable
|
|
4
|
+
SuggestExtensions: false
|
|
5
|
+
Exclude:
|
|
6
|
+
- 'vendor/**/*'
|
|
7
|
+
- 'tmp/**/*'
|
|
8
|
+
|
|
9
|
+
Style/Documentation:
|
|
10
|
+
Enabled: false
|
|
11
|
+
|
|
12
|
+
Style/FrozenStringLiteralComment:
|
|
13
|
+
Enabled: true
|
|
14
|
+
EnforcedStyle: always
|
|
15
|
+
|
|
16
|
+
Metrics/MethodLength:
|
|
17
|
+
Max: 30
|
|
18
|
+
|
|
19
|
+
Metrics/ClassLength:
|
|
20
|
+
Max: 500
|
|
21
|
+
|
|
22
|
+
Metrics/BlockLength:
|
|
23
|
+
Exclude:
|
|
24
|
+
- 'spec/**/*'
|
|
25
|
+
- '*.gemspec'
|
|
26
|
+
|
|
27
|
+
Layout/LineLength:
|
|
28
|
+
Max: 150
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- `mode` parameter in `FormationConfig` for draft/live URL routing
|
|
9
|
+
- `mode="live"` (default): Uses `/api/{id}/v1` prefix
|
|
10
|
+
- `mode="draft"`: Uses `/draft/{id}/v1` prefix for local development with `muxi up`
|
|
11
|
+
- SDK version update notifications (via `X-Muxi-SDK-Latest` response header)
|
|
12
|
+
- Notifies when newer SDK version available (max once per 12 hours)
|
|
13
|
+
- Disable with `MUXI_SDK_VERSION_NOTIFICATION=0`
|
|
14
|
+
- Console telemetry support via internal `_app` parameter
|
data/README.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# MUXI Ruby SDK
|
|
2
|
+
|
|
3
|
+
Official Ruby SDK for [MUXI](https://muxi.org) — infrastructure for AI agents.
|
|
4
|
+
|
|
5
|
+
**Highlights**
|
|
6
|
+
- Pure Ruby with stdlib only (no external dependencies)
|
|
7
|
+
- Built-in retries, idempotency, and typed errors
|
|
8
|
+
- Streaming helpers for chat/audio and deploy/log tails
|
|
9
|
+
|
|
10
|
+
> Need deeper usage notes? See the [User Guide](https://github.com/muxi-ai/muxi-ruby/blob/main/USER_GUIDE.md) for streaming, retries, and auth details.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
Add to your Gemfile:
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
gem 'muxi'
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Or install directly:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
gem install muxi
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### Server Management (Control Plane)
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
require 'muxi'
|
|
32
|
+
|
|
33
|
+
# Create a server client for managing formations
|
|
34
|
+
server = Muxi::ServerClient.new(
|
|
35
|
+
url: ENV['MUXI_SERVER_URL'],
|
|
36
|
+
key_id: ENV['MUXI_KEY_ID'],
|
|
37
|
+
secret_key: ENV['MUXI_SECRET_KEY']
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# List formations
|
|
41
|
+
formations = server.list_formations
|
|
42
|
+
formations['formations'].each do |f|
|
|
43
|
+
puts "#{f['id']}: #{f['status']}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Get server status
|
|
47
|
+
status = server.status
|
|
48
|
+
puts "Uptime: #{status['uptime']}s"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Formation Usage (Runtime API)
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
require 'muxi'
|
|
55
|
+
|
|
56
|
+
# Create a formation client
|
|
57
|
+
client = Muxi::FormationClient.new(
|
|
58
|
+
formation_id: 'my-bot',
|
|
59
|
+
server_url: ENV['MUXI_SERVER_URL'],
|
|
60
|
+
admin_key: ENV['MUXI_ADMIN_KEY'],
|
|
61
|
+
client_key: ENV['MUXI_CLIENT_KEY']
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Or connect directly to a formation
|
|
65
|
+
client = Muxi::FormationClient.new(
|
|
66
|
+
url: 'http://localhost:8001',
|
|
67
|
+
admin_key: ENV['MUXI_ADMIN_KEY'],
|
|
68
|
+
client_key: ENV['MUXI_CLIENT_KEY']
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Chat (non-streaming)
|
|
72
|
+
response = client.chat({ message: 'Hello!' }, user_id: 'user123')
|
|
73
|
+
puts response['message']
|
|
74
|
+
|
|
75
|
+
# Chat (streaming)
|
|
76
|
+
client.chat_stream({ message: 'Tell me a story' }, user_id: 'user123') do |event|
|
|
77
|
+
data = JSON.parse(event['data']) rescue event['data']
|
|
78
|
+
print data['text'] if data.is_a?(Hash) && data['text']
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Health check
|
|
82
|
+
health = client.health
|
|
83
|
+
puts "Status: #{health['status']}"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Webhook Verification
|
|
87
|
+
|
|
88
|
+
```ruby
|
|
89
|
+
require 'muxi'
|
|
90
|
+
|
|
91
|
+
# In your webhook handler (e.g., Rails controller)
|
|
92
|
+
def webhook
|
|
93
|
+
payload = request.raw_post
|
|
94
|
+
signature = request.headers['X-Muxi-Signature']
|
|
95
|
+
|
|
96
|
+
unless Muxi::Webhook.verify_signature(payload, signature, ENV['WEBHOOK_SECRET'])
|
|
97
|
+
render json: { error: 'Invalid signature' }, status: 401
|
|
98
|
+
return
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
event = Muxi::Webhook.parse(payload)
|
|
102
|
+
|
|
103
|
+
case event.status
|
|
104
|
+
when 'completed'
|
|
105
|
+
event.content.each do |item|
|
|
106
|
+
puts item.text if item.type == 'text'
|
|
107
|
+
end
|
|
108
|
+
when 'failed'
|
|
109
|
+
puts "Error: #{event.error.message}"
|
|
110
|
+
when 'awaiting_clarification'
|
|
111
|
+
puts "Question: #{event.clarification.question}"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
render json: { received: true }
|
|
115
|
+
end
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Configuration
|
|
119
|
+
|
|
120
|
+
### Environment Variables
|
|
121
|
+
|
|
122
|
+
- `MUXI_DEBUG=1` - Enable debug logging
|
|
123
|
+
|
|
124
|
+
### Client Options
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
Muxi::ServerClient.new(
|
|
128
|
+
url: 'https://muxi.example.com:7890',
|
|
129
|
+
key_id: 'your-key-id',
|
|
130
|
+
secret_key: 'your-secret-key',
|
|
131
|
+
timeout: 30, # Request timeout in seconds
|
|
132
|
+
max_retries: 3, # Retry on 429/5xx errors
|
|
133
|
+
debug: true # Enable debug logging
|
|
134
|
+
)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Error Handling
|
|
138
|
+
|
|
139
|
+
```ruby
|
|
140
|
+
begin
|
|
141
|
+
server.get_formation('nonexistent')
|
|
142
|
+
rescue Muxi::NotFoundError => e
|
|
143
|
+
puts "Not found: #{e.message}"
|
|
144
|
+
rescue Muxi::AuthenticationError => e
|
|
145
|
+
puts "Auth failed: #{e.message}"
|
|
146
|
+
rescue Muxi::RateLimitError => e
|
|
147
|
+
puts "Rate limited. Retry after: #{e.retry_after}s"
|
|
148
|
+
rescue Muxi::MuxiError => e
|
|
149
|
+
puts "Error: #{e.message} (#{e.status_code})"
|
|
150
|
+
end
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Requirements
|
|
154
|
+
|
|
155
|
+
- Ruby 3.0+
|
|
156
|
+
- No external dependencies (uses stdlib only)
|
|
157
|
+
|
|
158
|
+
## License
|
|
159
|
+
|
|
160
|
+
MIT
|
data/Rakefile
ADDED
data/SECURITY.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
| Version | Supported |
|
|
6
|
+
| ------- | ------------------ |
|
|
7
|
+
| 0.x.x | :white_check_mark: |
|
|
8
|
+
|
|
9
|
+
## Reporting a Vulnerability
|
|
10
|
+
|
|
11
|
+
If you discover a security vulnerability in this SDK, please report it by emailing security@muxi.org.
|
|
12
|
+
|
|
13
|
+
Please include:
|
|
14
|
+
- Description of the vulnerability
|
|
15
|
+
- Steps to reproduce
|
|
16
|
+
- Potential impact
|
|
17
|
+
- Any suggested fixes (optional)
|
|
18
|
+
|
|
19
|
+
We will acknowledge receipt within 48 hours and provide a detailed response within 7 days.
|
|
20
|
+
|
|
21
|
+
## Security Best Practices
|
|
22
|
+
|
|
23
|
+
When using this SDK:
|
|
24
|
+
|
|
25
|
+
1. **Never commit credentials** - Use environment variables or secure secret management
|
|
26
|
+
2. **Validate webhook signatures** - Always verify `X-Muxi-Signature` headers
|
|
27
|
+
3. **Use HTTPS** - Always connect to MUXI servers over HTTPS in production
|
|
28
|
+
4. **Keep dependencies updated** - Regularly update the SDK to get security patches
|
data/USER_GUIDE.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# MUXI Ruby SDK User Guide
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
gem install muxi
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Or add to your Gemfile:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
gem 'muxi'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quickstart
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
require 'muxi'
|
|
19
|
+
|
|
20
|
+
# Server client (management, HMAC auth)
|
|
21
|
+
server = Muxi::ServerClient.new(
|
|
22
|
+
url: 'https://server.example.com',
|
|
23
|
+
key_id: '<key_id>',
|
|
24
|
+
secret_key: '<secret_key>'
|
|
25
|
+
)
|
|
26
|
+
puts server.status
|
|
27
|
+
|
|
28
|
+
# Formation client (runtime, key auth)
|
|
29
|
+
formation = Muxi::FormationClient.new(
|
|
30
|
+
server_url: 'https://server.example.com',
|
|
31
|
+
formation_id: '<formation_id>',
|
|
32
|
+
client_key: '<client_key>',
|
|
33
|
+
admin_key: '<admin_key>'
|
|
34
|
+
)
|
|
35
|
+
puts formation.health
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Clients
|
|
39
|
+
|
|
40
|
+
- **ServerClient** (management, HMAC): deploy/list/update formations, server health/status, server logs.
|
|
41
|
+
- **FormationClient** (runtime, client/admin keys): chat/audio (streaming), agents, secrets, MCP, memory, scheduler, sessions/requests, identifiers, credentials, triggers/SOPs/audit, async/A2A/logging config, overlord/LLM settings, events/logs streaming.
|
|
42
|
+
|
|
43
|
+
## Streaming
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
# Chat streaming (block)
|
|
47
|
+
formation.chat_stream({ message: 'Tell me a story' }, user_id: 'user-123') do |event|
|
|
48
|
+
puts event['data'] if event['event'] == 'message'
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Chat streaming (enumerator)
|
|
52
|
+
formation.chat_stream({ message: 'Tell me a story' }, user_id: 'user-123').each do |event|
|
|
53
|
+
puts event['data']
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Event streaming
|
|
57
|
+
formation.stream_events('user-123') do |event|
|
|
58
|
+
puts event
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Log streaming (admin)
|
|
62
|
+
formation.stream_logs(level: 'info') do |log|
|
|
63
|
+
puts log
|
|
64
|
+
end
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Auth & Headers
|
|
68
|
+
|
|
69
|
+
- **ServerClient**: HMAC with `key_id`/`secret_key` on `/rpc` endpoints.
|
|
70
|
+
- **FormationClient**: `X-MUXI-CLIENT-KEY` or `X-MUXI-ADMIN-KEY` on `/api/{formation}/v1`. Override `base_url` for direct access (e.g., `http://localhost:9012/v1`).
|
|
71
|
+
- **Idempotency**: `X-Muxi-Idempotency-Key` auto-generated on every request.
|
|
72
|
+
- **SDK headers**: `X-Muxi-SDK`, `X-Muxi-Client` set automatically.
|
|
73
|
+
|
|
74
|
+
## Timeouts & Retries
|
|
75
|
+
|
|
76
|
+
- Default timeout: 30s (no timeout for streaming).
|
|
77
|
+
- Retries: `max_retries` with exponential backoff on 429/5xx/connection errors; respects `Retry-After`.
|
|
78
|
+
- Debug logging: enabled when `debug: true` or `MUXI_DEBUG=1`.
|
|
79
|
+
|
|
80
|
+
## Error Handling
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
begin
|
|
84
|
+
formation.chat(message: 'hello')
|
|
85
|
+
rescue Muxi::AuthenticationError => e
|
|
86
|
+
puts "Auth failed: #{e.message}"
|
|
87
|
+
rescue Muxi::RateLimitError => e
|
|
88
|
+
puts "Rate limited. Retry after: #{e.retry_after}s"
|
|
89
|
+
rescue Muxi::NotFoundError => e
|
|
90
|
+
puts "Not found: #{e.message}"
|
|
91
|
+
rescue Muxi::MuxiError => e
|
|
92
|
+
puts "#{e.code}: #{e.message} (#{e.status_code})"
|
|
93
|
+
end
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Error types: `AuthenticationError`, `AuthorizationError`, `NotFoundError`, `ValidationError`, `RateLimitError`, `ServerError`, `ConnectionError`.
|
|
97
|
+
|
|
98
|
+
## Notable Endpoints (FormationClient)
|
|
99
|
+
|
|
100
|
+
| Category | Methods |
|
|
101
|
+
|----------|---------|
|
|
102
|
+
| Chat/Audio | `chat`, `chat_stream`, `audio_chat`, `audio_chat_stream` |
|
|
103
|
+
| Memory | `get_memory_config`, `get_memories`, `add_memory`, `delete_memory`, `get_user_buffer`, `clear_user_buffer`, `clear_session_buffer`, `clear_all_buffers`, `get_buffer_stats` |
|
|
104
|
+
| Scheduler | `get_scheduler_config`, `get_scheduler_jobs`, `get_scheduler_job`, `create_scheduler_job`, `delete_scheduler_job` |
|
|
105
|
+
| Sessions | `get_sessions`, `get_session`, `get_session_messages`, `restore_session` |
|
|
106
|
+
| Requests | `get_requests`, `get_request_status`, `cancel_request` |
|
|
107
|
+
| Agents/MCP | `get_agents`, `get_agent`, `get_mcp_servers`, `get_mcp_server`, `get_mcp_tools` |
|
|
108
|
+
| Secrets | `get_secrets`, `get_secret`, `set_secret`, `delete_secret` |
|
|
109
|
+
| Credentials | `list_credential_services`, `list_credentials`, `get_credential`, `create_credential`, `delete_credential` |
|
|
110
|
+
| Identifiers | `get_user_identifiers_for_user`, `link_user_identifier`, `unlink_user_identifier` |
|
|
111
|
+
| Triggers/SOP | `get_triggers`, `get_trigger`, `fire_trigger`, `get_sops`, `get_sop` |
|
|
112
|
+
| Audit | `get_audit_log`, `clear_audit_log` |
|
|
113
|
+
| Config | `get_status`, `get_config`, `get_formation_info`, `get_async_config`, `get_a2a_config`, `get_logging_config`, `get_logging_destinations`, `get_overlord_config`, `get_overlord_persona`, `get_llm_settings` |
|
|
114
|
+
| Streaming | `stream_events`, `stream_logs`, `stream_request` |
|
|
115
|
+
| User | `resolve_user` |
|
|
116
|
+
|
|
117
|
+
## Webhook Verification
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
require 'muxi'
|
|
121
|
+
|
|
122
|
+
post '/webhooks/muxi' do
|
|
123
|
+
payload = request.body.read
|
|
124
|
+
signature = request.env['HTTP_X_MUXI_SIGNATURE']
|
|
125
|
+
|
|
126
|
+
unless Muxi::Webhook.verify_signature(payload, signature, ENV['WEBHOOK_SECRET'])
|
|
127
|
+
halt 401, 'Invalid signature'
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
event = Muxi::Webhook.parse(payload)
|
|
131
|
+
|
|
132
|
+
case event.status
|
|
133
|
+
when 'completed'
|
|
134
|
+
event.content.each { |item| puts item.text if item.type == 'text' }
|
|
135
|
+
when 'failed'
|
|
136
|
+
puts "Error: #{event.error&.message}"
|
|
137
|
+
when 'awaiting_clarification'
|
|
138
|
+
puts "Question: #{event.clarification&.question}"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
{ status: 'received' }.to_json
|
|
142
|
+
end
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Testing Locally
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
cd ruby
|
|
149
|
+
bundle install
|
|
150
|
+
rspec
|
|
151
|
+
```
|
data/lib/muxi/auth.rb
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "openssl"
|
|
4
|
+
require "base64"
|
|
5
|
+
|
|
6
|
+
module Muxi
|
|
7
|
+
module Auth
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
def generate_hmac_signature(secret_key, method, path)
|
|
11
|
+
timestamp = Time.now.to_i
|
|
12
|
+
sign_path = path.split("?").first
|
|
13
|
+
message = "#{timestamp};#{method};#{sign_path}"
|
|
14
|
+
|
|
15
|
+
digest = OpenSSL::Digest.new("sha256")
|
|
16
|
+
hmac = OpenSSL::HMAC.digest(digest, secret_key, message)
|
|
17
|
+
signature = Base64.strict_encode64(hmac)
|
|
18
|
+
|
|
19
|
+
[signature, timestamp]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def build_auth_header(key_id, secret_key, method, path)
|
|
23
|
+
signature, timestamp = generate_hmac_signature(secret_key, method, path)
|
|
24
|
+
"MUXI-HMAC key=#{key_id}, timestamp=#{timestamp}, signature=#{signature}"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/muxi/errors.rb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Muxi
|
|
4
|
+
class MuxiError < StandardError
|
|
5
|
+
attr_reader :code, :status_code, :details
|
|
6
|
+
|
|
7
|
+
def initialize(code, message, status_code, details = nil)
|
|
8
|
+
@code = code
|
|
9
|
+
@status_code = status_code
|
|
10
|
+
@details = details || {}
|
|
11
|
+
super(code ? "#{code}: #{message}" : message)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class AuthenticationError < MuxiError; end
|
|
16
|
+
class AuthorizationError < MuxiError; end
|
|
17
|
+
class NotFoundError < MuxiError; end
|
|
18
|
+
class ConflictError < MuxiError; end
|
|
19
|
+
class ValidationError < MuxiError; end
|
|
20
|
+
|
|
21
|
+
class RateLimitError < MuxiError
|
|
22
|
+
attr_reader :retry_after
|
|
23
|
+
|
|
24
|
+
def initialize(message, status_code, retry_after: nil, details: nil)
|
|
25
|
+
super("RATE_LIMITED", message, status_code, details)
|
|
26
|
+
@retry_after = retry_after
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class ServerError < MuxiError; end
|
|
31
|
+
class ConnectionError < MuxiError; end
|
|
32
|
+
|
|
33
|
+
class << self
|
|
34
|
+
def map_error(status, code, message, details = nil, retry_after = nil)
|
|
35
|
+
case status
|
|
36
|
+
when 401
|
|
37
|
+
AuthenticationError.new(code || "UNAUTHORIZED", message, status, details)
|
|
38
|
+
when 403
|
|
39
|
+
AuthorizationError.new(code || "FORBIDDEN", message, status, details)
|
|
40
|
+
when 404
|
|
41
|
+
NotFoundError.new(code || "NOT_FOUND", message, status, details)
|
|
42
|
+
when 409
|
|
43
|
+
ConflictError.new(code || "CONFLICT", message, status, details)
|
|
44
|
+
when 422
|
|
45
|
+
ValidationError.new(code || "VALIDATION_ERROR", message, status, details)
|
|
46
|
+
when 429
|
|
47
|
+
RateLimitError.new(message || "Too Many Requests", status, retry_after: retry_after, details: details)
|
|
48
|
+
when 500..599
|
|
49
|
+
ServerError.new(code || "SERVER_ERROR", message, status, details)
|
|
50
|
+
else
|
|
51
|
+
MuxiError.new(code || "ERROR", message, status, details)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|