api-response-manager 1.0.0 → 2.3.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/CLI_UPDATES.md ADDED
@@ -0,0 +1,196 @@
1
+ # CLI Updates for Advanced Tunnel Features
2
+
3
+ ## āœ… Changes Made
4
+
5
+ ### 1. Enhanced `tunnel` Command
6
+ Added new options to the main tunnel command:
7
+ ```bash
8
+ arm tunnel [port] [options]
9
+
10
+ Options:
11
+ -s, --subdomain <subdomain> Custom subdomain
12
+ -n, --name <name> Tunnel name
13
+ -a, --auth Enable basic authentication
14
+ -r, --rate-limit <limit> Rate limit (requests per minute)
15
+ -p, --protocol <protocol> Protocol (http, https, tcp, ws, wss)
16
+ --ssl Enable SSL/HTTPS
17
+ -d, --domain <domain> Custom domain
18
+ ```
19
+
20
+ ### 2. New Commands Added
21
+
22
+ #### Custom Domain
23
+ ```bash
24
+ arm tunnel:domain <tunnelId> <domain>
25
+ ```
26
+ Example:
27
+ ```bash
28
+ arm tunnel:domain abc123 api.yourdomain.com
29
+ ```
30
+
31
+ #### SSL Certificate Upload
32
+ ```bash
33
+ arm tunnel:ssl <tunnelId> --cert <path> --key <path> [--ca <path>]
34
+ ```
35
+ Example:
36
+ ```bash
37
+ arm tunnel:ssl abc123 --cert cert.pem --key key.pem --ca ca.pem
38
+ ```
39
+
40
+ #### OAuth Configuration
41
+ ```bash
42
+ arm tunnel:auth:oauth <tunnelId> [options]
43
+
44
+ Options:
45
+ --provider <provider> OAuth provider (google, github, microsoft, custom)
46
+ --client-id <id> OAuth client ID
47
+ --client-secret <secret> OAuth client secret
48
+ --callback-url <url> OAuth callback URL
49
+ --scope <scope> OAuth scope (comma-separated)
50
+ ```
51
+ Example:
52
+ ```bash
53
+ arm tunnel:auth:oauth abc123 \
54
+ --provider google \
55
+ --client-id YOUR_CLIENT_ID \
56
+ --client-secret YOUR_SECRET \
57
+ --callback-url https://yourtunnel.arm.dev/auth/callback \
58
+ --scope openid,email,profile
59
+ ```
60
+
61
+ #### OIDC Configuration
62
+ ```bash
63
+ arm tunnel:auth:oidc <tunnelId> [options]
64
+
65
+ Options:
66
+ --issuer <url> OIDC issuer URL
67
+ --client-id <id> OIDC client ID
68
+ --client-secret <secret> OIDC client secret
69
+ --callback-url <url> OIDC callback URL
70
+ ```
71
+ Example:
72
+ ```bash
73
+ arm tunnel:auth:oidc abc123 \
74
+ --issuer https://accounts.google.com \
75
+ --client-id YOUR_CLIENT_ID \
76
+ --client-secret YOUR_SECRET \
77
+ --callback-url https://yourtunnel.arm.dev/auth/callback
78
+ ```
79
+
80
+ #### SAML Configuration
81
+ ```bash
82
+ arm tunnel:auth:saml <tunnelId> [options]
83
+
84
+ Options:
85
+ --entry-point <url> SAML entry point URL
86
+ --issuer <issuer> SAML issuer
87
+ --cert <path> Path to IdP certificate file
88
+ --callback-url <url> SAML callback URL
89
+ ```
90
+ Example:
91
+ ```bash
92
+ arm tunnel:auth:saml abc123 \
93
+ --entry-point https://idp.example.com/saml/sso \
94
+ --issuer https://yourtunnel.arm.dev \
95
+ --cert idp-cert.pem \
96
+ --callback-url https://yourtunnel.arm.dev/auth/saml/callback
97
+ ```
98
+
99
+ #### Ingress Configuration
100
+ ```bash
101
+ arm tunnel:ingress <tunnelId> <rules> [--tls]
102
+
103
+ Rules format: "/path=host:port,/path2=host:port"
104
+ ```
105
+ Example:
106
+ ```bash
107
+ arm tunnel:ingress abc123 "/api=localhost:3000,/admin=localhost:4000" --tls
108
+ ```
109
+
110
+ ## šŸ“ Usage Examples
111
+
112
+ ### Example 1: Create HTTPS Tunnel
113
+ ```bash
114
+ arm tunnel 3000 --protocol https --ssl --subdomain myapi
115
+ ```
116
+
117
+ ### Example 2: Create TCP Tunnel
118
+ ```bash
119
+ arm tunnel 5432 --protocol tcp --subdomain postgres
120
+ ```
121
+
122
+ ### Example 3: Create Tunnel with Custom Domain
123
+ ```bash
124
+ # Create tunnel
125
+ arm tunnel 3000 --protocol https --ssl
126
+
127
+ # Set custom domain
128
+ arm tunnel:domain <tunnel-id> api.mycompany.com
129
+ ```
130
+
131
+ ### Example 4: Create Tunnel with OAuth
132
+ ```bash
133
+ # Create tunnel
134
+ arm tunnel 3000 --protocol https --ssl --subdomain myapi
135
+
136
+ # Configure OAuth
137
+ arm tunnel:auth:oauth <tunnel-id> \
138
+ --provider google \
139
+ --client-id YOUR_CLIENT_ID \
140
+ --client-secret YOUR_SECRET \
141
+ --callback-url https://myapi.tunnel.arm.dev/auth/callback
142
+ ```
143
+
144
+ ### Example 5: Create Tunnel with Ingress
145
+ ```bash
146
+ # Create tunnel
147
+ arm tunnel 3000 --protocol https --ssl
148
+
149
+ # Configure ingress rules
150
+ arm tunnel:ingress <tunnel-id> \
151
+ "/v1=localhost:3000,/v2=localhost:4000" \
152
+ --tls
153
+ ```
154
+
155
+ ## šŸ”§ Updated Files
156
+
157
+ 1. **cli/bin/arm.js** - Added new commands
158
+ 2. **cli/commands/tunnel.js** - Added new functions
159
+ 3. **cli/utils/api.js** - Added new API methods
160
+
161
+ ## šŸ“¦ No New Dependencies Required
162
+
163
+ All new features use existing dependencies.
164
+
165
+ ## šŸš€ Testing
166
+
167
+ Test the new commands:
168
+ ```bash
169
+ # Test HTTPS tunnel
170
+ arm tunnel 3000 --protocol https --ssl
171
+
172
+ # Test custom domain
173
+ arm tunnel:domain <tunnel-id> test.example.com
174
+
175
+ # Test SSL upload
176
+ arm tunnel:ssl <tunnel-id> --cert cert.pem --key key.pem
177
+
178
+ # Test OAuth
179
+ arm tunnel:auth:oauth <tunnel-id> --provider google --client-id test --client-secret test --callback-url http://localhost/callback
180
+
181
+ # Test ingress
182
+ arm tunnel:ingress <tunnel-id> "/api=localhost:3000" --tls
183
+ ```
184
+
185
+ ## šŸ“– Help Output
186
+
187
+ ```bash
188
+ arm --help
189
+ arm tunnel --help
190
+ arm tunnel:domain --help
191
+ arm tunnel:ssl --help
192
+ arm tunnel:auth:oauth --help
193
+ arm tunnel:auth:oidc --help
194
+ arm tunnel:auth:saml --help
195
+ arm tunnel:ingress --help
196
+ ```
package/README.md CHANGED
@@ -6,7 +6,7 @@ Command-line interface for API Response Manager. Manage tunnels, webhooks, and p
6
6
 
7
7
  ### Option 1: Install from npm (Recommended)
8
8
  ```bash
9
- npm install -g @arm/cli
9
+ npm install -g @vijaypurohit322-arm/cli
10
10
  ```
11
11
 
12
12
  After installation, verify:
@@ -33,20 +33,44 @@ arm --version
33
33
 
34
34
  ### Option 3: Use with npx (No Installation)
35
35
  ```bash
36
- npx @arm/cli login
37
- npx @arm/cli tunnel 3000
38
- npx @arm/cli webhook
36
+ npx @vijaypurohit322-arm/cli login
37
+ npx @vijaypurohit322-arm/cli tunnel 3000
38
+ npx @vijaypurohit322-arm/cli webhook
39
39
  ```
40
40
 
41
41
  ## Quick Start
42
42
 
43
43
  ### 1. Login
44
+
45
+ **Interactive Login (Recommended)**
44
46
  ```bash
45
47
  arm login
46
- # Or provide credentials directly
48
+ # Choose from: Email/Password, Google, GitHub, or Microsoft
49
+ ```
50
+
51
+ **Email & Password**
52
+ ```bash
47
53
  arm login -e your@email.com -p yourpassword
48
54
  ```
49
55
 
56
+ **Social Login (OAuth)**
57
+ ```bash
58
+ # Login with Google
59
+ arm login --provider google
60
+
61
+ # Login with GitHub
62
+ arm login --provider github
63
+
64
+ # Login with Microsoft
65
+ arm login --provider microsoft
66
+ ```
67
+
68
+ The CLI will:
69
+ 1. Generate a unique device code
70
+ 2. Open your browser automatically
71
+ 3. Wait for you to authenticate
72
+ 4. Store your credentials securely
73
+
50
74
  ### 2. Start a Tunnel
51
75
  ```bash
52
76
  # Expose local port 3000
@@ -77,9 +101,59 @@ arm webhook --tunnel <tunnel-id>
77
101
 
78
102
  #### `arm login`
79
103
  Authenticate with API Response Manager
104
+
105
+ **Options:**
106
+ - `-e, --email <email>` - Email address (for email/password login)
107
+ - `-p, --password <password>` - Password (for email/password login)
108
+ - `--provider <provider>` - OAuth provider: google, github, or microsoft
109
+
110
+ **Examples:**
80
111
  ```bash
112
+ # Interactive login (choose method)
81
113
  arm login
82
- arm login -e email@example.com -p password
114
+
115
+ # Email & Password
116
+ arm login -e user@example.com -p mypassword
117
+
118
+ # Social Login (OAuth Device Flow)
119
+ arm login --provider github
120
+ arm login --provider google
121
+ arm login --provider microsoft
122
+ ```
123
+
124
+ **OAuth Device Flow:**
125
+ When using social login, the CLI will:
126
+ 1. Generate a unique device code (e.g., `ABCD-EFGH`)
127
+ 2. Display a verification URL
128
+ 3. Automatically open your browser
129
+ 4. Wait for you to complete authentication
130
+ 5. Store your token securely
131
+
132
+ Example output:
133
+ ```bash
134
+ $ arm login --provider github
135
+
136
+ 🌐 Logging in with github...
137
+
138
+ šŸ“‹ Please complete authentication:
139
+
140
+ 1. Visit: https://localhost:5173/device
141
+ 2. Enter code: ABCD-EFGH
142
+
143
+ Code expires in 600 seconds
144
+
145
+ ? Open browser automatically? (Y/n)
146
+
147
+ āœ“ Browser opened
148
+
149
+ ā ‹ Waiting for authentication...
150
+ āœ“ Authentication successful!
151
+
152
+ User: John Doe
153
+ Provider: github
154
+ Token saved to: ~/.config/arm-cli/config.json
155
+
156
+ āœ“ You can now use all ARM CLI commands
83
157
  ```
84
158
 
85
159
  #### `arm logout`
@@ -93,9 +167,23 @@ arm logout
93
167
  #### `arm tunnel <port>`
94
168
  Start a tunnel to expose local server
95
169
  ```bash
170
+ # Basic HTTP tunnel
96
171
  arm tunnel 3000
97
- arm tunnel 3000 --subdomain myapi --name "My API"
98
- arm tunnel 3000 --auth --rate-limit 100
172
+
173
+ # HTTPS tunnel with SSL
174
+ arm tunnel 3000 --protocol https --ssl
175
+
176
+ # TCP tunnel (for databases, etc.)
177
+ arm tunnel 5432 --protocol tcp --subdomain postgres
178
+
179
+ # WebSocket tunnel
180
+ arm tunnel 8080 --protocol ws
181
+
182
+ # With custom subdomain and authentication
183
+ arm tunnel 3000 --subdomain myapi --name "My API" --auth --rate-limit 100
184
+
185
+ # With custom domain
186
+ arm tunnel 3000 --protocol https --ssl --domain api.yourdomain.com
99
187
  ```
100
188
 
101
189
  Options:
@@ -103,6 +191,9 @@ Options:
103
191
  - `-n, --name <name>` - Tunnel name
104
192
  - `-a, --auth` - Enable basic authentication
105
193
  - `-r, --rate-limit <limit>` - Rate limit (requests per minute, default: 60)
194
+ - `-p, --protocol <protocol>` - Protocol: http, https, tcp, ws, wss (default: http)
195
+ - `--ssl` - Enable SSL/HTTPS
196
+ - `-d, --domain <domain>` - Custom domain
106
197
 
107
198
  #### `arm tunnel:list`
108
199
  List all active tunnels
@@ -128,6 +219,91 @@ Options:
128
219
  - `-f, --follow` - Follow log output (real-time)
129
220
  - `-n, --lines <number>` - Number of lines to show (default: 50)
130
221
 
222
+ #### `arm tunnel:domain <tunnelId> <domain>`
223
+ Set custom domain for tunnel
224
+ ```bash
225
+ arm tunnel:domain 507f1f77bcf86cd799439011 api.yourdomain.com
226
+ ```
227
+
228
+ #### `arm tunnel:ssl <tunnelId>`
229
+ Upload SSL certificate for tunnel
230
+ ```bash
231
+ arm tunnel:ssl 507f1f77bcf86cd799439011 --cert cert.pem --key key.pem
232
+ arm tunnel:ssl 507f1f77bcf86cd799439011 --cert cert.pem --key key.pem --ca ca.pem
233
+ ```
234
+
235
+ Options:
236
+ - `--cert <path>` - Path to certificate file
237
+ - `--key <path>` - Path to private key file
238
+ - `--ca <path>` - Path to CA certificate file (optional)
239
+
240
+ #### `arm tunnel:auth:oauth <tunnelId>`
241
+ Configure OAuth authentication for tunnel
242
+ ```bash
243
+ arm tunnel:auth:oauth 507f1f77bcf86cd799439011 \
244
+ --provider google \
245
+ --client-id YOUR_CLIENT_ID \
246
+ --client-secret YOUR_SECRET \
247
+ --callback-url https://yourtunnel.arm.dev/auth/callback \
248
+ --scope openid,email,profile
249
+ ```
250
+
251
+ Options:
252
+ - `--provider <provider>` - OAuth provider: google, github, microsoft, custom
253
+ - `--client-id <id>` - OAuth client ID
254
+ - `--client-secret <secret>` - OAuth client secret
255
+ - `--callback-url <url>` - OAuth callback URL
256
+ - `--scope <scope>` - OAuth scope (comma-separated)
257
+
258
+ #### `arm tunnel:auth:oidc <tunnelId>`
259
+ Configure OpenID Connect authentication for tunnel
260
+ ```bash
261
+ arm tunnel:auth:oidc 507f1f77bcf86cd799439011 \
262
+ --issuer https://accounts.google.com \
263
+ --client-id YOUR_CLIENT_ID \
264
+ --client-secret YOUR_SECRET \
265
+ --callback-url https://yourtunnel.arm.dev/auth/callback
266
+ ```
267
+
268
+ Options:
269
+ - `--issuer <url>` - OIDC issuer URL
270
+ - `--client-id <id>` - OIDC client ID
271
+ - `--client-secret <secret>` - OIDC client secret
272
+ - `--callback-url <url>` - OIDC callback URL
273
+
274
+ #### `arm tunnel:auth:saml <tunnelId>`
275
+ Configure SAML authentication for tunnel
276
+ ```bash
277
+ arm tunnel:auth:saml 507f1f77bcf86cd799439011 \
278
+ --entry-point https://idp.example.com/saml/sso \
279
+ --issuer https://yourtunnel.arm.dev \
280
+ --cert idp-cert.pem \
281
+ --callback-url https://yourtunnel.arm.dev/auth/saml/callback
282
+ ```
283
+
284
+ Options:
285
+ - `--entry-point <url>` - SAML entry point URL
286
+ - `--issuer <issuer>` - SAML issuer
287
+ - `--cert <path>` - Path to IdP certificate file
288
+ - `--callback-url <url>` - SAML callback URL
289
+
290
+ #### `arm tunnel:ingress <tunnelId> <rules>`
291
+ Configure ingress rules for tunnel (path-based routing)
292
+ ```bash
293
+ # Route different paths to different backends
294
+ arm tunnel:ingress 507f1f77bcf86cd799439011 \
295
+ "/api=localhost:3000,/admin=localhost:4000" \
296
+ --tls
297
+
298
+ # Single rule
299
+ arm tunnel:ingress 507f1f77bcf86cd799439011 "/api=localhost:3000"
300
+ ```
301
+
302
+ Options:
303
+ - `--tls` - Enable TLS for ingress
304
+
305
+ Rules format: `/path=host:port,/path2=host:port`
306
+
131
307
  ### Webhooks
132
308
 
133
309
  #### `arm webhook`
@@ -268,13 +444,53 @@ Default configuration:
268
444
  # Login
269
445
  arm login
270
446
 
271
- # Start tunnel on port 3000
272
- arm tunnel 3000 --subdomain myapp
447
+ # Start HTTPS tunnel on port 3000
448
+ arm tunnel 3000 --protocol https --ssl --subdomain myapp
273
449
 
274
450
  # Your local server is now accessible at:
275
451
  # https://myapp.tunnel.arm.dev
276
452
  ```
277
453
 
454
+ ### Secure Tunnel with OAuth Authentication
455
+ ```bash
456
+ # Create HTTPS tunnel
457
+ arm tunnel 3000 --protocol https --ssl --subdomain myapi
458
+
459
+ # Configure Google OAuth
460
+ arm tunnel:auth:oauth <tunnel-id> \
461
+ --provider google \
462
+ --client-id YOUR_GOOGLE_CLIENT_ID \
463
+ --client-secret YOUR_GOOGLE_SECRET \
464
+ --callback-url https://myapi.tunnel.arm.dev/auth/callback
465
+
466
+ # Now your tunnel requires Google login to access
467
+ ```
468
+
469
+ ### TCP Tunnel for Database
470
+ ```bash
471
+ # Expose PostgreSQL database
472
+ arm tunnel 5432 --protocol tcp --subdomain mydb
473
+
474
+ # Connect from anywhere:
475
+ # psql -h mydb.tunnel.arm.dev -p 5432 -U username -d database
476
+ ```
477
+
478
+ ### Multi-Service Routing with Ingress
479
+ ```bash
480
+ # Create tunnel
481
+ arm tunnel 3000 --protocol https --ssl
482
+
483
+ # Configure path-based routing
484
+ arm tunnel:ingress <tunnel-id> \
485
+ "/api/v1=localhost:3000,/api/v2=localhost:4000,/admin=localhost:5000" \
486
+ --tls
487
+
488
+ # Now:
489
+ # https://yourtunnel.arm.dev/api/v1 -> localhost:3000
490
+ # https://yourtunnel.arm.dev/api/v2 -> localhost:4000
491
+ # https://yourtunnel.arm.dev/admin -> localhost:5000
492
+ ```
493
+
278
494
  ### Test Webhooks Locally
279
495
  ```bash
280
496
  # Create webhook that forwards to local server
@@ -328,6 +544,28 @@ arm config:set apiUrl https://your-api-url.com/api
328
544
  arm config:get
329
545
  ```
330
546
 
547
+ ## Troubleshooting
548
+
549
+ ### Command Not Found After Installation
550
+
551
+ If you get `arm: command not found` after installing:
552
+
553
+ **Windows:**
554
+ 1. Check npm prefix: `npm config get prefix`
555
+ 2. Add to PATH: `C:\Users\<YourUsername>\AppData\Roaming\npm`
556
+ 3. Restart terminal
557
+
558
+ **macOS/Linux:**
559
+ ```bash
560
+ echo 'export PATH="$(npm config get prefix)/bin:$PATH"' >> ~/.bashrc
561
+ source ~/.bashrc
562
+ ```
563
+
564
+ **Alternative - Use npx:**
565
+ ```bash
566
+ npx @vijaypurohit322-arm/cli login
567
+ ```
568
+
331
569
  ## Publishing to npm
332
570
 
333
571
  See [PUBLISHING.md](./PUBLISHING.md) for detailed instructions on publishing this package to npm.
package/bin/arm.js CHANGED
@@ -30,6 +30,7 @@ program
30
30
  .description('Authenticate with API Response Manager')
31
31
  .option('-e, --email <email>', 'Email address')
32
32
  .option('-p, --password <password>', 'Password')
33
+ .option('--provider <provider>', 'OAuth provider (google, github, microsoft)')
33
34
  .action(loginCommand);
34
35
 
35
36
  // Logout command
@@ -47,6 +48,9 @@ program
47
48
  .option('-n, --name <name>', 'Tunnel name')
48
49
  .option('-a, --auth', 'Enable basic authentication')
49
50
  .option('-r, --rate-limit <limit>', 'Rate limit (requests per minute)', '60')
51
+ .option('-p, --protocol <protocol>', 'Protocol (http, https, tcp, ws, wss)', 'http')
52
+ .option('--ssl', 'Enable SSL/HTTPS')
53
+ .option('-d, --domain <domain>', 'Custom domain')
50
54
  .action(tunnelCommand.start);
51
55
 
52
56
  program
@@ -68,6 +72,61 @@ program
68
72
  .option('-n, --lines <number>', 'Number of lines to show', '50')
69
73
  .action(tunnelCommand.logs);
70
74
 
75
+ program
76
+ .command('tunnel:domain')
77
+ .description('Set custom domain for tunnel')
78
+ .argument('<tunnelId>', 'Tunnel ID')
79
+ .argument('<domain>', 'Custom domain (e.g., api.yourdomain.com)')
80
+ .action(tunnelCommand.setDomain);
81
+
82
+ program
83
+ .command('tunnel:ssl')
84
+ .description('Upload SSL certificate for tunnel')
85
+ .argument('<tunnelId>', 'Tunnel ID')
86
+ .option('--cert <path>', 'Path to certificate file')
87
+ .option('--key <path>', 'Path to private key file')
88
+ .option('--ca <path>', 'Path to CA certificate file (optional)')
89
+ .action(tunnelCommand.uploadSSL);
90
+
91
+ program
92
+ .command('tunnel:auth:oauth')
93
+ .description('Configure OAuth authentication')
94
+ .argument('<tunnelId>', 'Tunnel ID')
95
+ .option('--provider <provider>', 'OAuth provider (google, github, microsoft, custom)')
96
+ .option('--client-id <id>', 'OAuth client ID')
97
+ .option('--client-secret <secret>', 'OAuth client secret')
98
+ .option('--callback-url <url>', 'OAuth callback URL')
99
+ .option('--scope <scope>', 'OAuth scope (comma-separated)')
100
+ .action(tunnelCommand.configureOAuth);
101
+
102
+ program
103
+ .command('tunnel:auth:oidc')
104
+ .description('Configure OIDC authentication')
105
+ .argument('<tunnelId>', 'Tunnel ID')
106
+ .option('--issuer <url>', 'OIDC issuer URL')
107
+ .option('--client-id <id>', 'OIDC client ID')
108
+ .option('--client-secret <secret>', 'OIDC client secret')
109
+ .option('--callback-url <url>', 'OIDC callback URL')
110
+ .action(tunnelCommand.configureOIDC);
111
+
112
+ program
113
+ .command('tunnel:auth:saml')
114
+ .description('Configure SAML authentication')
115
+ .argument('<tunnelId>', 'Tunnel ID')
116
+ .option('--entry-point <url>', 'SAML entry point URL')
117
+ .option('--issuer <issuer>', 'SAML issuer')
118
+ .option('--cert <path>', 'Path to IdP certificate file')
119
+ .option('--callback-url <url>', 'SAML callback URL')
120
+ .action(tunnelCommand.configureSAML);
121
+
122
+ program
123
+ .command('tunnel:ingress')
124
+ .description('Configure ingress rules for tunnel')
125
+ .argument('<tunnelId>', 'Tunnel ID')
126
+ .argument('<rules>', 'Ingress rules (e.g., "/api=localhost:3000,/admin=localhost:4000")')
127
+ .option('--tls', 'Enable TLS for ingress')
128
+ .action(tunnelCommand.configureIngress);
129
+
71
130
  // Webhook commands
72
131
  program
73
132
  .command('webhook')
package/commands/login.js CHANGED
@@ -1,12 +1,38 @@
1
1
  const inquirer = require('inquirer');
2
2
  const chalk = require('chalk');
3
3
  const ora = require('ora');
4
+ const open = require('open');
4
5
  const api = require('../utils/api');
5
6
  const config = require('../utils/config');
6
7
 
7
8
  async function login(options) {
8
9
  console.log(chalk.blue.bold('\nšŸ” API Response Manager - Login\n'));
9
10
 
11
+ // Check if social login is requested
12
+ if (options.provider) {
13
+ return await socialLogin(options.provider);
14
+ }
15
+
16
+ // Ask for login method
17
+ const { method } = await inquirer.prompt([
18
+ {
19
+ type: 'list',
20
+ name: 'method',
21
+ message: 'Choose login method:',
22
+ choices: [
23
+ { name: 'šŸ“§ Email & Password', value: 'email' },
24
+ { name: '🌐 Google', value: 'google' },
25
+ { name: 'šŸ™ GitHub', value: 'github' },
26
+ { name: '🪟 Microsoft', value: 'microsoft' }
27
+ ]
28
+ }
29
+ ]);
30
+
31
+ if (method !== 'email') {
32
+ return await socialLogin(method);
33
+ }
34
+
35
+ // Traditional email/password login
10
36
  let email = options.email;
11
37
  let password = options.password;
12
38
 
@@ -85,4 +111,100 @@ async function login(options) {
85
111
  }
86
112
  }
87
113
 
114
+ async function socialLogin(provider) {
115
+ console.log(chalk.blue(`\n🌐 Logging in with ${provider}...\n`));
116
+
117
+ const spinner = ora('Initiating OAuth flow...').start();
118
+
119
+ try {
120
+ // Request device code from backend
121
+ const deviceResponse = await api.requestDeviceCode(provider);
122
+
123
+ spinner.stop();
124
+
125
+ const { device_code, user_code, verification_uri, expires_in } = deviceResponse;
126
+
127
+ console.log(chalk.yellow('\nšŸ“‹ Please complete authentication:\n'));
128
+ console.log(chalk.white(' 1. Visit:'), chalk.cyan.underline(verification_uri));
129
+ console.log(chalk.white(' 2. Enter code:'), chalk.green.bold(user_code));
130
+ console.log(chalk.gray(`\n Code expires in ${expires_in} seconds\n`));
131
+
132
+ // Open browser automatically
133
+ const { openBrowser } = await inquirer.prompt([
134
+ {
135
+ type: 'confirm',
136
+ name: 'openBrowser',
137
+ message: 'Open browser automatically?',
138
+ default: true
139
+ }
140
+ ]);
141
+
142
+ if (openBrowser) {
143
+ await open(verification_uri);
144
+ console.log(chalk.gray(' āœ“ Browser opened\n'));
145
+ }
146
+
147
+ const pollSpinner = ora('Waiting for authentication...').start();
148
+
149
+ // Poll for token
150
+ const pollInterval = 5000; // 5 seconds
151
+ const maxAttempts = Math.ceil(expires_in / (pollInterval / 1000));
152
+ let attempts = 0;
153
+
154
+ const pollForToken = async () => {
155
+ while (attempts < maxAttempts) {
156
+ attempts++;
157
+
158
+ try {
159
+ const tokenResponse = await api.pollDeviceToken(device_code, provider);
160
+
161
+ if (tokenResponse.token) {
162
+ pollSpinner.succeed(chalk.green('Authentication successful!'));
163
+
164
+ // Store credentials
165
+ api.setToken(tokenResponse.token);
166
+
167
+ const userId = tokenResponse.user?._id || tokenResponse.user?.id;
168
+ const userEmail = tokenResponse.user?.email;
169
+ const userName = tokenResponse.user?.name;
170
+
171
+ if (userId) config.set('userId', userId);
172
+ if (userEmail) config.set('email', userEmail);
173
+
174
+ console.log(chalk.gray('\nUser:'), chalk.white(userName || userEmail));
175
+ console.log(chalk.gray('Provider:'), chalk.white(provider));
176
+ console.log(chalk.gray('Token saved to:'), chalk.white(config.path));
177
+ console.log(chalk.green('\nāœ“ You can now use all ARM CLI commands\n'));
178
+
179
+ return;
180
+ }
181
+ } catch (error) {
182
+ if (error.response?.status === 428) {
183
+ // Still pending, continue polling
184
+ await new Promise(resolve => setTimeout(resolve, pollInterval));
185
+ continue;
186
+ }
187
+ throw error;
188
+ }
189
+ }
190
+
191
+ throw new Error('Authentication timeout - please try again');
192
+ };
193
+
194
+ await pollForToken();
195
+
196
+ } catch (error) {
197
+ spinner.stop();
198
+ console.error(chalk.red('\nāœ— Social login failed'));
199
+
200
+ if (error.response?.data?.msg) {
201
+ console.error(chalk.red(`āœ— ${error.response.data.msg}\n`));
202
+ } else {
203
+ console.error(chalk.red(`āœ— ${error.message}\n`));
204
+ }
205
+
206
+ process.exit(1);
207
+ }
208
+ }
209
+
88
210
  module.exports = login;
@@ -21,6 +21,9 @@ async function start(port, options) {
21
21
  name: options.name || `Tunnel-${port}`,
22
22
  subdomain: options.subdomain,
23
23
  localPort: parseInt(port),
24
+ protocol: options.protocol || 'http',
25
+ sslEnabled: options.ssl || options.protocol === 'https',
26
+ customDomain: options.domain,
24
27
  security: {
25
28
  requireAuth: options.auth || false
26
29
  },
@@ -222,9 +225,147 @@ async function logs(tunnelId, options) {
222
225
  }
223
226
  }
224
227
 
228
+ // Set custom domain
229
+ async function setDomain(tunnelId, domain) {
230
+ const spinner = ora('Setting custom domain...').start();
231
+
232
+ try {
233
+ await api.setTunnelDomain(tunnelId, domain);
234
+ spinner.succeed(chalk.green('Custom domain set successfully'));
235
+ console.log(chalk.cyan(`\nāœ“ Tunnel now accessible at: https://${domain}\n`));
236
+ } catch (error) {
237
+ spinner.fail(chalk.red('Failed to set custom domain'));
238
+ console.error(chalk.red(`\nāœ— ${error.response?.data?.msg || error.message}\n`));
239
+ process.exit(1);
240
+ }
241
+ }
242
+
243
+ // Upload SSL certificate
244
+ async function uploadSSL(tunnelId, options) {
245
+ const spinner = ora('Uploading SSL certificate...').start();
246
+
247
+ try {
248
+ const fs = require('fs');
249
+ const cert = fs.readFileSync(options.cert, 'utf8');
250
+ const key = fs.readFileSync(options.key, 'utf8');
251
+ const ca = options.ca ? fs.readFileSync(options.ca, 'utf8') : null;
252
+
253
+ await api.uploadTunnelSSL(tunnelId, { cert, key, ca });
254
+ spinner.succeed(chalk.green('SSL certificate uploaded successfully'));
255
+ console.log();
256
+ } catch (error) {
257
+ spinner.fail(chalk.red('Failed to upload SSL certificate'));
258
+ console.error(chalk.red(`\nāœ— ${error.response?.data?.msg || error.message}\n`));
259
+ process.exit(1);
260
+ }
261
+ }
262
+
263
+ // Configure OAuth
264
+ async function configureOAuth(tunnelId, options) {
265
+ const spinner = ora('Configuring OAuth...').start();
266
+
267
+ try {
268
+ await api.configureTunnelOAuth(tunnelId, {
269
+ provider: options.provider,
270
+ clientId: options.clientId,
271
+ clientSecret: options.clientSecret,
272
+ callbackUrl: options.callbackUrl,
273
+ scope: options.scope ? options.scope.split(',') : ['openid', 'email', 'profile']
274
+ });
275
+ spinner.succeed(chalk.green('OAuth configured successfully'));
276
+ console.log();
277
+ } catch (error) {
278
+ spinner.fail(chalk.red('Failed to configure OAuth'));
279
+ console.error(chalk.red(`\nāœ— ${error.response?.data?.msg || error.message}\n`));
280
+ process.exit(1);
281
+ }
282
+ }
283
+
284
+ // Configure OIDC
285
+ async function configureOIDC(tunnelId, options) {
286
+ const spinner = ora('Configuring OIDC...').start();
287
+
288
+ try {
289
+ await api.configureTunnelOIDC(tunnelId, {
290
+ issuer: options.issuer,
291
+ clientId: options.clientId,
292
+ clientSecret: options.clientSecret,
293
+ callbackUrl: options.callbackUrl
294
+ });
295
+ spinner.succeed(chalk.green('OIDC configured successfully'));
296
+ console.log();
297
+ } catch (error) {
298
+ spinner.fail(chalk.red('Failed to configure OIDC'));
299
+ console.error(chalk.red(`\nāœ— ${error.response?.data?.msg || error.message}\n`));
300
+ process.exit(1);
301
+ }
302
+ }
303
+
304
+ // Configure SAML
305
+ async function configureSAML(tunnelId, options) {
306
+ const spinner = ora('Configuring SAML...').start();
307
+
308
+ try {
309
+ const fs = require('fs');
310
+ const cert = fs.readFileSync(options.cert, 'utf8');
311
+
312
+ await api.configureTunnelSAML(tunnelId, {
313
+ entryPoint: options.entryPoint,
314
+ issuer: options.issuer,
315
+ cert,
316
+ callbackUrl: options.callbackUrl
317
+ });
318
+ spinner.succeed(chalk.green('SAML configured successfully'));
319
+ console.log();
320
+ } catch (error) {
321
+ spinner.fail(chalk.red('Failed to configure SAML'));
322
+ console.error(chalk.red(`\nāœ— ${error.response?.data?.msg || error.message}\n`));
323
+ process.exit(1);
324
+ }
325
+ }
326
+
327
+ // Configure ingress
328
+ async function configureIngress(tunnelId, rules, options) {
329
+ const spinner = ora('Configuring ingress...').start();
330
+
331
+ try {
332
+ // Parse rules: "/api=localhost:3000,/admin=localhost:4000"
333
+ const parsedRules = rules.split(',').map(rule => {
334
+ const [path, backend] = rule.split('=');
335
+ const [host, port] = backend.split(':');
336
+ return {
337
+ path: path.trim(),
338
+ pathType: 'Prefix',
339
+ backend: {
340
+ host: host.trim(),
341
+ port: parseInt(port)
342
+ }
343
+ };
344
+ });
345
+
346
+ await api.configureTunnelIngress(tunnelId, {
347
+ enabled: true,
348
+ rules: parsedRules,
349
+ tls: { enabled: options.tls || false }
350
+ });
351
+ spinner.succeed(chalk.green('Ingress configured successfully'));
352
+ console.log();
353
+ } catch (error) {
354
+ spinner.fail(chalk.red('Failed to configure ingress'));
355
+ console.error(chalk.red(`\nāœ— ${error.response?.data?.msg || error.message}\n`));
356
+ process.exit(1);
357
+ }
358
+ }
359
+
225
360
  module.exports = {
226
361
  start,
227
362
  list,
228
363
  stop,
229
- logs
364
+ logs,
365
+ setDomain,
366
+ uploadSSL,
367
+ configureOAuth,
368
+ configureOIDC,
369
+ configureSAML,
370
+ configureIngress
230
371
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api-response-manager",
3
- "version": "1.0.0",
3
+ "version": "2.3.0",
4
4
  "description": "Command-line interface for API Response Manager",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -21,17 +21,18 @@
21
21
  "author": "Vijay Singh Purohit",
22
22
  "license": "MIT",
23
23
  "dependencies": {
24
- "commander": "^11.1.0",
25
24
  "axios": "^1.6.2",
25
+ "boxen": "^5.1.2",
26
26
  "chalk": "^4.1.2",
27
+ "cli-table3": "^0.6.3",
28
+ "commander": "^11.1.0",
29
+ "conf": "^10.2.0",
30
+ "dotenv": "^16.3.1",
27
31
  "inquirer": "^8.2.5",
32
+ "open": "^8.4.0",
28
33
  "ora": "^5.4.1",
29
- "ws": "^8.14.2",
30
- "dotenv": "^16.3.1",
31
- "conf": "^10.2.0",
32
- "boxen": "^5.1.2",
33
- "cli-table3": "^0.6.3",
34
- "update-notifier": "^6.0.2"
34
+ "update-notifier": "^6.0.2",
35
+ "ws": "^8.14.2"
35
36
  },
36
37
  "devDependencies": {
37
38
  "jest": "^29.7.0"
package/utils/api.js CHANGED
@@ -58,6 +58,20 @@ class APIClient {
58
58
  return response.data;
59
59
  }
60
60
 
61
+ // OAuth Device Flow for CLI
62
+ async requestDeviceCode(provider) {
63
+ const response = await this.client.post('/auth/device/code', { provider });
64
+ return response.data;
65
+ }
66
+
67
+ async pollDeviceToken(deviceCode, provider) {
68
+ const response = await this.client.post('/auth/device/token', {
69
+ device_code: deviceCode,
70
+ provider
71
+ });
72
+ return response.data;
73
+ }
74
+
61
75
  // Tunnels
62
76
  async createTunnel(data) {
63
77
  const response = await this.client.post('/tunnels', data);
@@ -140,6 +154,37 @@ class APIClient {
140
154
  const response = await this.client.get(`/projects/${id}/responses`, { params });
141
155
  return response.data;
142
156
  }
157
+
158
+ // Advanced Tunnel Features
159
+ async setTunnelDomain(id, domain) {
160
+ const response = await this.client.post(`/tunnels/${id}/custom-domain`, { domain });
161
+ return response.data;
162
+ }
163
+
164
+ async uploadTunnelSSL(id, data) {
165
+ const response = await this.client.post(`/tunnels/${id}/ssl`, data);
166
+ return response.data;
167
+ }
168
+
169
+ async configureTunnelOAuth(id, data) {
170
+ const response = await this.client.post(`/tunnels/${id}/auth/oauth`, data);
171
+ return response.data;
172
+ }
173
+
174
+ async configureTunnelOIDC(id, data) {
175
+ const response = await this.client.post(`/tunnels/${id}/auth/oidc`, data);
176
+ return response.data;
177
+ }
178
+
179
+ async configureTunnelSAML(id, data) {
180
+ const response = await this.client.post(`/tunnels/${id}/auth/saml`, data);
181
+ return response.data;
182
+ }
183
+
184
+ async configureTunnelIngress(id, data) {
185
+ const response = await this.client.post(`/tunnels/${id}/ingress`, data);
186
+ return response.data;
187
+ }
143
188
  }
144
189
 
145
190
  module.exports = new APIClient();