@modelriver/cli 1.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.
package/README.md ADDED
@@ -0,0 +1,406 @@
1
+ # ModelRiver CLI
2
+
3
+ A command-line tool for testing webhooks and WebSockets from live/production ModelRiver, similar to Stripe CLI.
4
+
5
+ ## Installation
6
+
7
+ ### From npm (when published)
8
+
9
+ ```bash
10
+ npm install -g @modelriver/cli
11
+ ```
12
+
13
+ ### From source
14
+
15
+ ```bash
16
+ cd modelriver-cli
17
+ npm install
18
+ npm link # Makes `modelriver` command available globally
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ### Step 1: Login (One-Time Setup)
24
+
25
+ Run the interactive login command to configure your API key and forward URL:
26
+
27
+ ```bash
28
+ modelriver login
29
+ ```
30
+
31
+ You'll be prompted for:
32
+ 1. **API Key**: Your ModelRiver API key (starts with `mr_live_` or `mr_test_`)
33
+ 2. **Forward URL**: Where to forward webhooks (e.g., `http://localhost:4000`)
34
+
35
+ The URL will be automatically normalized to include `/webhook/modelriver`.
36
+
37
+ ### Step 2: Start Forwarding
38
+
39
+ After login, simply run:
40
+
41
+ ```bash
42
+ modelriver forward
43
+ ```
44
+
45
+ That's it! Webhooks will be forwarded to your configured URL.
46
+
47
+ ## Command Aliases
48
+
49
+ For convenience, the CLI supports short aliases:
50
+
51
+ | Command | Alias | Description |
52
+ |---------|-------|-------------|
53
+ | `listen` | `l` | Listen for webhooks |
54
+ | `forward` | `f` | Forward using saved config |
55
+ | `trigger` | `t` | Send async request |
56
+ | `websocket` | `ws` | Test WebSocket connection |
57
+
58
+ Example:
59
+ ```bash
60
+ modelriver l --print # Same as: modelriver listen --print
61
+ modelriver f # Same as: modelriver forward
62
+ modelriver t -w my-workflow -m "Hello" # Same as: modelriver trigger ...
63
+ ```
64
+
65
+ ## Configuration
66
+
67
+ ### Interactive Setup (Recommended)
68
+
69
+ ```bash
70
+ modelriver login
71
+ ```
72
+
73
+ This saves your configuration to `~/.modelriver/config.json`.
74
+
75
+ ### Environment Variables
76
+
77
+ Set these environment variables for convenience:
78
+
79
+ ```bash
80
+ export MODELRIVER_API_KEY=mr_live_YOUR_API_KEY
81
+ export MODELRIVER_API_URL=https://api.modelriver.com # Optional, defaults to production
82
+ ```
83
+
84
+ ### Config File
85
+
86
+ Create a config file at `~/.modelriver/config.json` or `.modelriverrc` in your project:
87
+
88
+ ```json
89
+ {
90
+ "api_key": "mr_live_YOUR_API_KEY",
91
+ "api_url": "https://api.modelriver.com",
92
+ "forward_url": "http://localhost:4000/webhook/modelriver"
93
+ }
94
+ ```
95
+
96
+ **Priority**: CLI arguments > Environment variables > Config file > Defaults
97
+
98
+ ## Commands
99
+
100
+ ### `modelriver login` - Interactive Setup
101
+
102
+ Configure your API key and forward URL interactively:
103
+
104
+ ```bash
105
+ modelriver login
106
+ ```
107
+
108
+ Example session:
109
+ ```
110
+ ModelRiver CLI Login
111
+ ─────────────────────
112
+ Configure your ModelRiver CLI credentials.
113
+
114
+ Enter your ModelRiver API key: mr_live_abc123...
115
+ Enter your webhook forward URL (e.g. http://localhost:4000/webhook/modelriver): http://localhost:4000
116
+
117
+ ✓ Configuration saved!
118
+
119
+ Saved Configuration
120
+ ───────────────────
121
+ ✓ API Key: mr_live_abc123...
122
+ ✓ Forward URL: http://localhost:4000/webhook/modelriver
123
+ ```
124
+
125
+ ### `modelriver forward` - Quick Webhook Forwarding
126
+
127
+ Forward webhooks using your saved configuration:
128
+
129
+ ```bash
130
+ # Simple - uses saved config from login
131
+ modelriver forward
132
+
133
+ # Override port
134
+ modelriver forward --port 4001
135
+
136
+ # Verbose output
137
+ modelriver forward --verbose
138
+ ```
139
+
140
+ ### `modelriver listen` - Receive Webhooks via WebSocket
141
+
142
+ Like Stripe CLI, receive webhook events directly via WebSocket - no public URL needed.
143
+
144
+ ```bash
145
+ # Start listening for webhooks (like `stripe listen`)
146
+ modelriver listen --print
147
+ # Or use alias: modelriver l --print
148
+
149
+ # Forward to local server
150
+ modelriver listen --port 3001 --print
151
+
152
+ # With custom API key
153
+ modelriver listen --api-key mr_live_YOUR_KEY --print
154
+
155
+ # Forward to external server (without starting local server)
156
+ modelriver listen --port 3002 --forward --print
157
+ ```
158
+
159
+ **How it works (like Stripe CLI):**
160
+ 1. Authenticates with your API key
161
+ 2. Gets a secure WebSocket token (valid for 24 hours)
162
+ 3. Connects to ModelRiver via WebSocket
163
+ 4. Receives webhook events in real-time - no public URL or ngrok required!
164
+ 5. Optionally forwards to local server
165
+
166
+ **Example Output:**
167
+ ```
168
+ ✓ Connecting to ModelRiver...
169
+ ✓ WebSocket connected
170
+ ✓ Joined webhook channel
171
+
172
+ > Ready! Listening for webhook events
173
+ > User ID: user-id-123
174
+ > Channel: cli_webhooks:user-id-123
175
+ > Local port: 3001
176
+
177
+ > Press Ctrl+C to stop
178
+
179
+ [2026-01-07 15:20:30] Webhook received via WebSocket:
180
+ Channel ID: abc-123-def
181
+ Status: success
182
+ Data: {"result": "..."}
183
+ ```
184
+
185
+ ### `modelriver websocket` - Test WebSocket Connection
186
+
187
+ Test WebSocket connections to production and receive real-time responses.
188
+
189
+ ```bash
190
+ # Test with workflow
191
+ modelriver websocket --workflow my-workflow --message "Hello from CLI"
192
+
193
+ # With custom payload
194
+ modelriver websocket --workflow my-workflow --payload '{"messages": [{"role": "user", "content": "Test"}]}'
195
+
196
+ # Connect to existing channel
197
+ modelriver websocket --channel-id abc-123 --project-id xyz-789
198
+
199
+ # Verbose output
200
+ modelriver websocket --workflow my-workflow --message "Test" --verbose
201
+ ```
202
+
203
+ **Example Output:**
204
+ ```
205
+ ✓ Making async request...
206
+ ✓ Request queued: abc-123-def
207
+
208
+ > Channel ID: abc-123-def
209
+ > Project ID: xyz-789
210
+
211
+ ✓ Connecting to WebSocket...
212
+ ✓ WebSocket connected
213
+ ✓ Channel joined
214
+
215
+ > Waiting for response...
216
+
217
+ ✅ Response received:
218
+ {
219
+ "status": "success",
220
+ "data": { ... },
221
+ "meta": { ... }
222
+ }
223
+ ```
224
+
225
+ ### `modelriver trigger` - Send Async Request
226
+
227
+ Send a test async request and get channel details.
228
+
229
+ ```bash
230
+ # Basic trigger
231
+ modelriver trigger --workflow my-workflow --message "Test message"
232
+
233
+ # With custom payload
234
+ modelriver trigger --workflow my-workflow --payload '{"messages": [...]}'
235
+
236
+ # Create webhook to receive response
237
+ modelriver trigger --workflow my-workflow --message "Test" --webhook-url https://webhook.site/your-id
238
+
239
+ # Print channel details
240
+ modelriver trigger --workflow my-workflow --message "Test" --print-channel
241
+ ```
242
+
243
+ **Example Output:**
244
+ ```
245
+ ✓ Async request created
246
+
247
+ Channel Details
248
+ {
249
+ "channel_id": "abc-123-def",
250
+ "project_id": "xyz-789",
251
+ "websocket_url": "wss://api.modelriver.com/socket",
252
+ "websocket_channel": "ai_response:xyz-789:abc-123-def",
253
+ "status": "pending"
254
+ }
255
+
256
+ > Use --webhook-url to automatically receive responses via webhook
257
+ > Or use "modelriver websocket" to connect and receive responses
258
+ ```
259
+
260
+ ### `modelriver webhook list` - List Webhooks
261
+
262
+ List all webhooks for your project.
263
+
264
+ ```bash
265
+ # List webhooks
266
+ modelriver webhook list
267
+
268
+ # Verbose output
269
+ modelriver webhook list --verbose
270
+ ```
271
+
272
+ ### `modelriver webhook verify` - Verify Signature
273
+
274
+ Verify a webhook signature locally.
275
+
276
+ ```bash
277
+ # From file
278
+ modelriver webhook verify \
279
+ --payload webhook.json \
280
+ --signature abc123... \
281
+ --timestamp 1234567890 \
282
+ --secret your-webhook-secret
283
+
284
+ # From JSON string
285
+ modelriver webhook verify \
286
+ --payload '{"channel_id": "...", "data": {...}}' \
287
+ --signature abc123... \
288
+ --timestamp 1234567890 \
289
+ --secret your-webhook-secret
290
+ ```
291
+
292
+ ## Usage Examples
293
+
294
+ ### Example 1: Listen for Webhooks (like Stripe CLI)
295
+
296
+ ```bash
297
+ # Terminal 1: Start listening for webhooks via WebSocket
298
+ export MODELRIVER_API_KEY=mr_live_YOUR_KEY
299
+ modelriver listen --print
300
+
301
+ # Terminal 2: Trigger an async AI request
302
+ # Webhook events will appear in Terminal 1 via WebSocket (no public URL needed!)
303
+ ```
304
+
305
+ ### Example 2: Test WebSocket Connection
306
+
307
+ ```bash
308
+ export MODELRIVER_API_KEY=mr_live_YOUR_KEY
309
+ modelriver websocket --workflow my-workflow --message "Hello from CLI" --verbose
310
+ ```
311
+
312
+ ### Example 3: Send Test Request and Get Channel Info
313
+
314
+ ```bash
315
+ export MODELRIVER_API_KEY=mr_live_YOUR_KEY
316
+ modelriver trigger --workflow my-workflow --message "Test" --print-channel
317
+ ```
318
+
319
+ ## Webhook Payload Format
320
+
321
+ Webhooks are sent as HTTP POST requests with the following format:
322
+
323
+ **Body:**
324
+ ```json
325
+ {
326
+ "channel_id": "uuid",
327
+ "timestamp": 1234567890,
328
+ "data": {
329
+ "status": "success|error",
330
+ "data": { ... },
331
+ "meta": { ... }
332
+ }
333
+ }
334
+ ```
335
+
336
+ **Headers:**
337
+ - `X-ModelRiver-Signature`: HMAC-SHA256 hex signature
338
+ - `X-ModelRiver-Timestamp`: Unix timestamp string
339
+ - `X-ModelRiver-Webhook-Id`: Webhook UUID
340
+ - `Content-Type`: `application/json`
341
+
342
+ ## WebSocket Protocol
343
+
344
+ The CLI connects to ModelRiver WebSockets using the Phoenix protocol:
345
+
346
+ 1. Connect to `wss://api.modelriver.com/socket?token={ws_token}`
347
+ 2. Join channel: `ai_response:{project_id}:{channel_id}`
348
+ 3. Listen for `response` event containing the AI response
349
+
350
+ ## Troubleshooting
351
+
352
+ ### "API key is required"
353
+
354
+ Set the API key via:
355
+ - Environment variable: `export MODELRIVER_API_KEY=mr_live_YOUR_KEY`
356
+ - CLI flag: `--api-key mr_live_YOUR_KEY`
357
+ - Config file: `~/.modelriver/config.json`
358
+
359
+ ### "WebSocket connection failed"
360
+
361
+ - Verify the API URL is correct
362
+ - Check that the `ws_token` hasn't expired (24-hour limit for CLI tokens)
363
+ - Check network connectivity
364
+ - If using production, ensure you're using `wss://` (secure WebSocket)
365
+
366
+ ### "Webhook signature verification failed"
367
+
368
+ - Ensure the secret matches exactly (no extra spaces)
369
+ - Verify timestamp is recent (within 5 minutes)
370
+ - Check that payload structure matches expected format
371
+
372
+ **Note**: The CLI does not support creating permanent webhooks. Use the ModelRiver dashboard to create webhooks for production use. The CLI only creates temporary webhooks for testing (via `listen` and `test-webhook` commands), which are automatically cleaned up.
373
+
374
+ ## API Endpoints
375
+
376
+ The CLI uses these ModelRiver API endpoints:
377
+
378
+ - `POST /v1/ai/async` - Create async request (production)
379
+ - `POST /api/v1/ai/async` - Create async request (dev/test)
380
+ - `POST /v1/ai/reconnect` - Get reconnect token (production)
381
+ - `POST /api/v1/ai/reconnect` - Get reconnect token (dev/test)
382
+ - `GET /v1/webhooks` - List webhooks (production)
383
+ - `GET /api/v1/webhooks` - List webhooks (dev/test)
384
+
385
+ ## Development
386
+
387
+ ### Running Tests
388
+
389
+ The CLI includes a Jest test suite. To run the tests:
390
+
391
+ ```bash
392
+ npm test
393
+ ```
394
+
395
+ Tests cover critical components including:
396
+ - Webhook signature verification
397
+ - Configuration loading
398
+ - API client validation
399
+ - Output formatting
400
+
401
+
402
+ ## License
403
+
404
+ MIT
405
+
406
+
package/bin/modelriver ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ require('../src/index.js');
4
+
5
+
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@modelriver/cli",
3
+ "version": "1.0.0",
4
+ "description": "ModelRiver CLI for testing webhooks and WebSockets from production",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "modelriver": "./bin/modelriver"
8
+ },
9
+ "scripts": {
10
+ "start": "node bin/modelriver",
11
+ "test": "jest"
12
+ },
13
+ "keywords": [
14
+ "modelriver",
15
+ "cli",
16
+ "webhook",
17
+ "websocket",
18
+ "testing",
19
+ "ai",
20
+ "llm",
21
+ "api"
22
+ ],
23
+ "author": "ModelRiver",
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/modelriver/modelriver-cli.git"
28
+ },
29
+ "homepage": "https://modelriver.com",
30
+ "bugs": {
31
+ "url": "https://github.com/modelriver/modelriver-cli/issues"
32
+ },
33
+ "dependencies": {
34
+ "commander": "^11.1.0",
35
+ "axios": "^1.6.0",
36
+ "ws": "^8.14.2",
37
+ "chalk": "^4.1.2",
38
+ "ora": "^5.4.1",
39
+ "express": "^4.18.2"
40
+ },
41
+ "devDependencies": {
42
+ "jest": "^29.7.0"
43
+ },
44
+ "engines": {
45
+ "node": ">=16.0.0"
46
+ },
47
+ "files": [
48
+ "bin/",
49
+ "src/",
50
+ "README.md"
51
+ ]
52
+ }
@@ -0,0 +1,61 @@
1
+ const config = require('../lib/config');
2
+ const Logger = require('../utils/logger');
3
+ const { extractPort } = require('../utils/url-helpers');
4
+ const { listenCommand } = require('./listen');
5
+
6
+ /**
7
+ * Forward command - simplified webhook forwarding using saved config
8
+ * This is a user-friendly shortcut for: modelriver listen --forward --print
9
+ */
10
+ async function forwardCommand(options) {
11
+ const { port, verbose } = options;
12
+
13
+ // Get saved configuration
14
+ const apiKey = config.getApiKey();
15
+ const forwardUrl = config.getForwardUrl();
16
+
17
+ if (!apiKey) {
18
+ Logger.error('No API key configured.');
19
+ Logger.info('Run "modelriver login" first to configure your credentials.');
20
+ process.exit(1);
21
+ }
22
+
23
+ if (!forwardUrl) {
24
+ Logger.error('No forward URL configured.');
25
+ Logger.info('Run "modelriver login" first to configure your forward URL.');
26
+ process.exit(1);
27
+ }
28
+
29
+ // Check if env var is overriding the config
30
+ if (process.env.MODELRIVER_API_KEY) {
31
+ Logger.warning('NOTICE: Using API key from MODELRIVER_API_KEY environment variable.');
32
+ Logger.warning('This overrides your logged-in user configuration.');
33
+ Logger.warning('Run `unset MODELRIVER_API_KEY` to use your saved login credentials.\n');
34
+ }
35
+
36
+ // Extract port from forward URL or use provided port
37
+ let targetPort = port;
38
+ if (!targetPort) {
39
+ targetPort = extractPort(forwardUrl);
40
+ }
41
+ if (!targetPort) {
42
+ targetPort = 4000; // Default port
43
+ }
44
+
45
+ Logger.info(`Using saved configuration:`);
46
+ Logger.info(` Forward URL: ${forwardUrl}`);
47
+ Logger.info(` Target Port: ${targetPort}`);
48
+ console.log('');
49
+
50
+ // Call listen command with forward options
51
+ await listenCommand({
52
+ apiKey,
53
+ apiUrl: config.getApiUrl(),
54
+ port: targetPort,
55
+ forward: true,
56
+ print: true,
57
+ verbose
58
+ });
59
+ }
60
+
61
+ module.exports = { forwardCommand };