securegate-cli-tool 2.1.3 → 2.1.4

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.
@@ -1,280 +1,280 @@
1
- /**
2
- * securegate keys — Manage security keys
3
- */
4
-
5
- const chalk = require('chalk');
6
- const inquirer = require('inquirer');
7
- const ora = require('ora');
8
- const api = require('../api');
9
- const { setConnection, PROXY_BASE_URL } = require('../config');
10
-
11
- async function keysListCommand() {
12
- console.log();
13
- console.log(chalk.cyan.bold('🔑 Security Keys'));
14
- console.log(chalk.dim('━'.repeat(50)));
15
-
16
- const spinner = ora('Fetching connections...').start();
17
-
18
- try {
19
- const res = await api.listConnections();
20
-
21
- if (res.status !== 200) {
22
- throw new Error(res.data?.error || `Server returned ${res.status}`);
23
- }
24
-
25
- const connections = res.data?.connections || res.data || [];
26
- spinner.stop();
27
-
28
- if (connections.length === 0) {
29
- console.log();
30
- console.log(chalk.dim(' No connections found.'));
31
- console.log(chalk.dim(` Run: ${chalk.cyan('securegate connect')} to create one.`));
32
- console.log();
33
- return;
34
- }
35
-
36
- console.log();
37
- for (const conn of connections) {
38
- const keyCount = conn.security_keys?.length || 0;
39
- console.log(chalk.white.bold(` ${conn.connection_id}`));
40
- console.log(chalk.dim(` Provider: ${conn.provider}`));
41
- console.log(chalk.dim(` Keys: ${keyCount} active`));
42
- console.log(chalk.dim(` Created: ${new Date(conn.created_at).toLocaleDateString()}`));
43
-
44
- if (conn.security_keys?.length > 0) {
45
- for (const key of conn.security_keys) {
46
- const preview = key.key_prefix || 'SG_***';
47
- const status = key.is_active ? chalk.green('● active') : chalk.red('● revoked');
48
- console.log(chalk.dim(` ${preview} ${status} ${key.label || 'no label'}`));
49
- }
50
- }
51
- console.log();
52
- }
53
- } catch (err) {
54
- spinner.fail(chalk.red(`Failed: ${err.message}`));
55
- process.exitCode = 1;
56
- }
57
- }
58
-
59
- async function keysCreateCommand() {
60
- console.log();
61
- console.log(chalk.cyan.bold('🔑 Generate Security Key'));
62
- console.log(chalk.dim('━'.repeat(50)));
63
-
64
- const spinner = ora('Fetching connections...').start();
65
-
66
- try {
67
- const res = await api.listConnections();
68
- spinner.stop();
69
-
70
- if (res.status !== 200) {
71
- throw new Error(res.data?.error || `Server returned ${res.status}`);
72
- }
73
-
74
- const connections = res.data?.connections || res.data || [];
75
-
76
- if (connections.length === 0) {
77
- console.log();
78
- console.log(chalk.dim(' No connections found. Create one first:'));
79
- console.log(chalk.cyan(' securegate connect'));
80
- console.log();
81
- return;
82
- }
83
-
84
- const choices = connections.map(c => ({
85
- name: `${c.connection_id} (${c.provider})`,
86
- value: c.connection_id,
87
- }));
88
-
89
- console.log();
90
- const { connectionId, label } = await inquirer.prompt([
91
- {
92
- type: 'list',
93
- name: 'connectionId',
94
- message: 'Which connection?',
95
- choices,
96
- },
97
- {
98
- type: 'input',
99
- name: 'label',
100
- message: 'Key label (optional):',
101
- default: `${require('os').hostname()} CLI`,
102
- },
103
- ]);
104
-
105
- const genSpinner = ora('Generating security key...').start();
106
- const keyRes = await api.generateKey(connectionId, label);
107
-
108
- if (keyRes.status !== 200 && keyRes.status !== 201) {
109
- throw new Error(keyRes.data?.error || `Server returned ${keyRes.status}`);
110
- }
111
-
112
- genSpinner.succeed(chalk.green('Security key generated!'));
113
-
114
- const { security_key, bound_to, usage } = keyRes.data;
115
-
116
- // Save locally
117
- setConnection(connectionId, {
118
- security_key,
119
- provider: connections.find(c => c.connection_id === connectionId)?.provider,
120
- bound_to,
121
- created_at: new Date().toISOString(),
122
- });
123
-
124
- console.log();
125
- console.log(chalk.yellow.bold(' ⚠ SAVE THIS KEY — IT WILL NOT BE SHOWN AGAIN!'));
126
- console.log();
127
- console.log(chalk.dim(' Security Key:'));
128
- console.log(chalk.white.bold(` ${security_key}`));
129
- console.log();
130
- if (bound_to) {
131
- console.log(chalk.dim(` Bound to IP: ${bound_to.ip || 'auto-bind on first use'}`));
132
- console.log(chalk.dim(` Bound to Device: ${bound_to.device || 'this machine'}`));
133
- }
134
- console.log();
135
- console.log(chalk.dim(' Quick usage:'));
136
- console.log(chalk.dim(` ${chalk.cyan(`baseURL: "${usage?.baseURL || PROXY_BASE_URL}"`)}`));
137
- console.log(chalk.dim(` ${chalk.cyan(`apiKey: "${security_key.substring(0, 12)}..."`)}`));
138
- console.log();
139
- } catch (err) {
140
- spinner?.fail?.(chalk.red(`Failed: ${err.message}`));
141
- process.exitCode = 1;
142
- }
143
- }
144
-
145
- async function keysRevokeCommand(keyId) {
146
- if (!keyId) {
147
- console.log(chalk.red(' Error: Key ID required.'));
148
- console.log(chalk.dim(' Usage: securegate keys revoke <key-id>'));
149
- return;
150
- }
151
-
152
- const { confirm } = await inquirer.prompt([{
153
- type: 'confirm',
154
- name: 'confirm',
155
- message: `Revoke key ${chalk.red(keyId)}? This cannot be undone.`,
156
- default: false,
157
- }]);
158
-
159
- if (!confirm) {
160
- console.log(chalk.dim(' Cancelled.'));
161
- return;
162
- }
163
-
164
- const spinner = ora('Revoking key...').start();
165
-
166
- try {
167
- const res = await api.deleteKey(keyId);
168
-
169
- if (res.status !== 200) {
170
- throw new Error(res.data?.error || `Server returned ${res.status}`);
171
- }
172
-
173
- spinner.succeed(chalk.green('Key revoked.'));
174
- } catch (err) {
175
- spinner.fail(chalk.red(`Failed: ${err.message}`));
176
- process.exitCode = 1;
177
- }
178
- }
179
-
180
- async function keysLockCommand(keyId, options) {
181
- if (!keyId) {
182
- console.log(chalk.red(' Error: Key ID required.'));
183
- console.log(chalk.dim(' Usage: securegate keys lock <key-id> [--ip <ip-address>]'));
184
- return;
185
- }
186
-
187
- const spinner = ora('Checking IP...').start();
188
- let ipToLock = options.ip;
189
-
190
- if (!ipToLock) {
191
- try {
192
- const res = await fetch('https://api.ipify.org?format=json').then(r => r.json());
193
- ipToLock = res.ip;
194
- } catch (err) {
195
- spinner.fail(chalk.red('Failed to detect public IP. Please specify with --ip'));
196
- return;
197
- }
198
- }
199
- spinner.stop();
200
-
201
- console.log();
202
- const { confirm } = await inquirer.prompt([{
203
- type: 'confirm',
204
- name: 'confirm',
205
- message: `Lock key ${chalk.cyan(keyId)} to IP ${chalk.yellow(ipToLock)}?`,
206
- default: true,
207
- }]);
208
-
209
- if (!confirm) {
210
- console.log(chalk.dim(' Cancelled.'));
211
- return;
212
- }
213
-
214
- const lockSpinner = ora('Locking key...').start();
215
-
216
- try {
217
- const res = await api.lockKey(keyId, ipToLock);
218
-
219
- if (res.status !== 200) {
220
- throw new Error(res.data?.error || `Server returned ${res.status}`);
221
- }
222
-
223
- lockSpinner.succeed(chalk.green(`Key locked to ${ipToLock}`));
224
- console.log(chalk.dim(' Run `securegate keys list` to verify.'));
225
- console.log();
226
- } catch (err) {
227
- lockSpinner.fail(chalk.red(`Failed: ${err.message}`));
228
- process.exitCode = 1;
229
- }
230
- }
231
-
232
- async function keysUpdateCommand(keyId, options) {
233
- if (!keyId) {
234
- console.log(chalk.red(' Error: Key ID required.'));
235
- console.log(chalk.dim(' Usage: securegate keys update <key-id> [--city <city>] [--models <model1,model2>]'));
236
- return;
237
- }
238
-
239
- const { city, models } = options;
240
-
241
- if (city === undefined && models === undefined) {
242
- console.log(chalk.red(' Error: Must specify at least one option to update.'));
243
- console.log(chalk.dim(' Usage: securegate keys update <key-id> [--city <city>] [--models <model1,model2>]'));
244
- return;
245
- }
246
-
247
- const updateData = {};
248
-
249
- if (city !== undefined) {
250
- updateData.bound_city = city === '' ? null : city;
251
- }
252
-
253
- if (models !== undefined) {
254
- updateData.allowed_models = models === '' ? [] : models.split(',').map(m => m.trim()).filter(m => m);
255
- }
256
-
257
- const spinner = ora('Updating key...').start();
258
-
259
- try {
260
- const res = await api.updateKey(keyId, updateData);
261
-
262
- if (res.status !== 200) {
263
- throw new Error(res.data?.error || `Server returned ${res.status}`);
264
- }
265
-
266
- spinner.succeed(chalk.green(`Key ${keyId} updated successfully.`));
267
- if (updateData.bound_city !== undefined) {
268
- console.log(chalk.dim(` City Lock: ${updateData.bound_city || 'None'}`));
269
- }
270
- if (updateData.allowed_models !== undefined) {
271
- console.log(chalk.dim(` Model Lock: ${updateData.allowed_models.length > 0 ? updateData.allowed_models.join(', ') : 'None'}`));
272
- }
273
- console.log();
274
- } catch (err) {
275
- spinner.fail(chalk.red(`Failed: ${err.message}`));
276
- process.exitCode = 1;
277
- }
278
- }
279
-
280
- module.exports = { keysListCommand, keysCreateCommand, keysRevokeCommand, keysLockCommand, keysUpdateCommand };
1
+ /**
2
+ * securegate keys — Manage security keys
3
+ */
4
+
5
+ const chalk = require('chalk');
6
+ const inquirer = require('inquirer');
7
+ const ora = require('ora');
8
+ const api = require('../api');
9
+ const { setConnection, PROXY_BASE_URL } = require('../config');
10
+
11
+ async function keysListCommand() {
12
+ console.log();
13
+ console.log(chalk.cyan.bold('🔑 Security Keys'));
14
+ console.log(chalk.dim('━'.repeat(50)));
15
+
16
+ const spinner = ora('Fetching connections...').start();
17
+
18
+ try {
19
+ const res = await api.listConnections();
20
+
21
+ if (res.status !== 200) {
22
+ throw new Error(res.data?.error || `Server returned ${res.status}`);
23
+ }
24
+
25
+ const connections = res.data?.connections || res.data || [];
26
+ spinner.stop();
27
+
28
+ if (connections.length === 0) {
29
+ console.log();
30
+ console.log(chalk.dim(' No connections found.'));
31
+ console.log(chalk.dim(` Run: ${chalk.cyan('securegate connect')} to create one.`));
32
+ console.log();
33
+ return;
34
+ }
35
+
36
+ console.log();
37
+ for (const conn of connections) {
38
+ const keyCount = conn.security_keys?.length || 0;
39
+ console.log(chalk.white.bold(` ${conn.connection_id}`));
40
+ console.log(chalk.dim(` Provider: ${conn.provider}`));
41
+ console.log(chalk.dim(` Keys: ${keyCount} active`));
42
+ console.log(chalk.dim(` Created: ${new Date(conn.created_at).toLocaleDateString()}`));
43
+
44
+ if (conn.security_keys?.length > 0) {
45
+ for (const key of conn.security_keys) {
46
+ const preview = key.key_prefix || 'SG_***';
47
+ const status = key.is_active ? chalk.green('● active') : chalk.red('● revoked');
48
+ console.log(chalk.dim(` ${preview} ${status} ${key.label || 'no label'}`));
49
+ }
50
+ }
51
+ console.log();
52
+ }
53
+ } catch (err) {
54
+ spinner.fail(chalk.red(`Failed: ${err.message}`));
55
+ process.exitCode = 1;
56
+ }
57
+ }
58
+
59
+ async function keysCreateCommand() {
60
+ console.log();
61
+ console.log(chalk.cyan.bold('🔑 Generate Security Key'));
62
+ console.log(chalk.dim('━'.repeat(50)));
63
+
64
+ const spinner = ora('Fetching connections...').start();
65
+
66
+ try {
67
+ const res = await api.listConnections();
68
+ spinner.stop();
69
+
70
+ if (res.status !== 200) {
71
+ throw new Error(res.data?.error || `Server returned ${res.status}`);
72
+ }
73
+
74
+ const connections = res.data?.connections || res.data || [];
75
+
76
+ if (connections.length === 0) {
77
+ console.log();
78
+ console.log(chalk.dim(' No connections found. Create one first:'));
79
+ console.log(chalk.cyan(' securegate connect'));
80
+ console.log();
81
+ return;
82
+ }
83
+
84
+ const choices = connections.map(c => ({
85
+ name: `${c.connection_id} (${c.provider})`,
86
+ value: c.connection_id,
87
+ }));
88
+
89
+ console.log();
90
+ const { connectionId, label } = await inquirer.prompt([
91
+ {
92
+ type: 'list',
93
+ name: 'connectionId',
94
+ message: 'Which connection?',
95
+ choices,
96
+ },
97
+ {
98
+ type: 'input',
99
+ name: 'label',
100
+ message: 'Key label (optional):',
101
+ default: `${require('os').hostname()} CLI`,
102
+ },
103
+ ]);
104
+
105
+ const genSpinner = ora('Generating security key...').start();
106
+ const keyRes = await api.generateKey(connectionId, label);
107
+
108
+ if (keyRes.status !== 200 && keyRes.status !== 201) {
109
+ throw new Error(keyRes.data?.error || `Server returned ${keyRes.status}`);
110
+ }
111
+
112
+ genSpinner.succeed(chalk.green('Security key generated!'));
113
+
114
+ const { security_key, bound_to, usage } = keyRes.data;
115
+
116
+ // Save locally
117
+ setConnection(connectionId, {
118
+ security_key,
119
+ provider: connections.find(c => c.connection_id === connectionId)?.provider,
120
+ bound_to,
121
+ created_at: new Date().toISOString(),
122
+ });
123
+
124
+ console.log();
125
+ console.log(chalk.yellow.bold(' ⚠ SAVE THIS KEY — IT WILL NOT BE SHOWN AGAIN!'));
126
+ console.log();
127
+ console.log(chalk.dim(' Security Key:'));
128
+ console.log(chalk.white.bold(` ${security_key}`));
129
+ console.log();
130
+ if (bound_to) {
131
+ console.log(chalk.dim(` Bound to IP: ${bound_to.ip || 'auto-bind on first use'}`));
132
+ console.log(chalk.dim(` Bound to Device: ${bound_to.device || 'this machine'}`));
133
+ }
134
+ console.log();
135
+ console.log(chalk.dim(' Quick usage:'));
136
+ console.log(chalk.dim(` ${chalk.cyan(`baseURL: "${usage?.baseURL || PROXY_BASE_URL}"`)}`));
137
+ console.log(chalk.dim(` ${chalk.cyan(`apiKey: "${security_key.substring(0, 12)}..."`)}`));
138
+ console.log();
139
+ } catch (err) {
140
+ spinner?.fail?.(chalk.red(`Failed: ${err.message}`));
141
+ process.exitCode = 1;
142
+ }
143
+ }
144
+
145
+ async function keysRevokeCommand(keyId) {
146
+ if (!keyId) {
147
+ console.log(chalk.red(' Error: Key ID required.'));
148
+ console.log(chalk.dim(' Usage: securegate keys revoke <key-id>'));
149
+ return;
150
+ }
151
+
152
+ const { confirm } = await inquirer.prompt([{
153
+ type: 'confirm',
154
+ name: 'confirm',
155
+ message: `Revoke key ${chalk.red(keyId)}? This cannot be undone.`,
156
+ default: false,
157
+ }]);
158
+
159
+ if (!confirm) {
160
+ console.log(chalk.dim(' Cancelled.'));
161
+ return;
162
+ }
163
+
164
+ const spinner = ora('Revoking key...').start();
165
+
166
+ try {
167
+ const res = await api.deleteKey(keyId);
168
+
169
+ if (res.status !== 200) {
170
+ throw new Error(res.data?.error || `Server returned ${res.status}`);
171
+ }
172
+
173
+ spinner.succeed(chalk.green('Key revoked.'));
174
+ } catch (err) {
175
+ spinner.fail(chalk.red(`Failed: ${err.message}`));
176
+ process.exitCode = 1;
177
+ }
178
+ }
179
+
180
+ async function keysLockCommand(keyId, options) {
181
+ if (!keyId) {
182
+ console.log(chalk.red(' Error: Key ID required.'));
183
+ console.log(chalk.dim(' Usage: securegate keys lock <key-id> [--ip <ip-address>]'));
184
+ return;
185
+ }
186
+
187
+ const spinner = ora('Checking IP...').start();
188
+ let ipToLock = options.ip;
189
+
190
+ if (!ipToLock) {
191
+ try {
192
+ const res = await fetch('https://api.ipify.org?format=json').then(r => r.json());
193
+ ipToLock = res.ip;
194
+ } catch (err) {
195
+ spinner.fail(chalk.red('Failed to detect public IP. Please specify with --ip'));
196
+ return;
197
+ }
198
+ }
199
+ spinner.stop();
200
+
201
+ console.log();
202
+ const { confirm } = await inquirer.prompt([{
203
+ type: 'confirm',
204
+ name: 'confirm',
205
+ message: `Lock key ${chalk.cyan(keyId)} to IP ${chalk.yellow(ipToLock)}?`,
206
+ default: true,
207
+ }]);
208
+
209
+ if (!confirm) {
210
+ console.log(chalk.dim(' Cancelled.'));
211
+ return;
212
+ }
213
+
214
+ const lockSpinner = ora('Locking key...').start();
215
+
216
+ try {
217
+ const res = await api.lockKey(keyId, ipToLock);
218
+
219
+ if (res.status !== 200) {
220
+ throw new Error(res.data?.error || `Server returned ${res.status}`);
221
+ }
222
+
223
+ lockSpinner.succeed(chalk.green(`Key locked to ${ipToLock}`));
224
+ console.log(chalk.dim(' Run `securegate keys list` to verify.'));
225
+ console.log();
226
+ } catch (err) {
227
+ lockSpinner.fail(chalk.red(`Failed: ${err.message}`));
228
+ process.exitCode = 1;
229
+ }
230
+ }
231
+
232
+ async function keysUpdateCommand(keyId, options) {
233
+ if (!keyId) {
234
+ console.log(chalk.red(' Error: Key ID required.'));
235
+ console.log(chalk.dim(' Usage: securegate keys update <key-id> [--city <city>] [--models <model1,model2>]'));
236
+ return;
237
+ }
238
+
239
+ const { city, models } = options;
240
+
241
+ if (city === undefined && models === undefined) {
242
+ console.log(chalk.red(' Error: Must specify at least one option to update.'));
243
+ console.log(chalk.dim(' Usage: securegate keys update <key-id> [--city <city>] [--models <model1,model2>]'));
244
+ return;
245
+ }
246
+
247
+ const updateData = {};
248
+
249
+ if (city !== undefined) {
250
+ updateData.bound_city = city === '' ? null : city;
251
+ }
252
+
253
+ if (models !== undefined) {
254
+ updateData.allowed_models = models === '' ? [] : models.split(',').map(m => m.trim()).filter(m => m);
255
+ }
256
+
257
+ const spinner = ora('Updating key...').start();
258
+
259
+ try {
260
+ const res = await api.updateKey(keyId, updateData);
261
+
262
+ if (res.status !== 200) {
263
+ throw new Error(res.data?.error || `Server returned ${res.status}`);
264
+ }
265
+
266
+ spinner.succeed(chalk.green(`Key ${keyId} updated successfully.`));
267
+ if (updateData.bound_city !== undefined) {
268
+ console.log(chalk.dim(` City Lock: ${updateData.bound_city || 'None'}`));
269
+ }
270
+ if (updateData.allowed_models !== undefined) {
271
+ console.log(chalk.dim(` Model Lock: ${updateData.allowed_models.length > 0 ? updateData.allowed_models.join(', ') : 'None'}`));
272
+ }
273
+ console.log();
274
+ } catch (err) {
275
+ spinner.fail(chalk.red(`Failed: ${err.message}`));
276
+ process.exitCode = 1;
277
+ }
278
+ }
279
+
280
+ module.exports = { keysListCommand, keysCreateCommand, keysRevokeCommand, keysLockCommand, keysUpdateCommand };