securegate-cli-tool 2.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/index.js +351 -0
- package/package.json +35 -0
- package/scripts/postinstall.js +37 -0
- package/src/api.js +205 -0
- package/src/commands/connect.js +102 -0
- package/src/commands/keys.js +232 -0
- package/src/commands/login.js +60 -0
- package/src/commands/providers.js +51 -0
- package/src/commands/status.js +78 -0
- package/src/config.js +82 -0
- package/src/index.js +155 -0
- package/templates/SKILL.md +59 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* securegate connect — Create a new provider connection
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
const inquirer = require('inquirer');
|
|
7
|
+
const ora = require('ora');
|
|
8
|
+
const api = require('../api');
|
|
9
|
+
|
|
10
|
+
// Provider registry (mirrors lib/providers.ts)
|
|
11
|
+
const PROVIDERS = [
|
|
12
|
+
{ value: 'openai', name: 'OpenAI — GPT-4o, GPT-4.1, o3, DALL·E', placeholder: 'sk-...' },
|
|
13
|
+
{ value: 'anthropic', name: 'Anthropic — Claude 4, Claude 3.5 Sonnet', placeholder: 'sk-ant-...' },
|
|
14
|
+
{ value: 'google', name: 'Google AI — Gemini 2.5 Pro, Flash', placeholder: 'AIza...' },
|
|
15
|
+
{ value: 'azure', name: 'Azure OpenAI — GPT-4o via Azure', placeholder: 'Your Azure API key' },
|
|
16
|
+
{ value: 'groq', name: 'Groq — Ultra-fast LPU inference', placeholder: 'gsk_...' },
|
|
17
|
+
{ value: 'together', name: 'Together AI — Llama, Qwen, DeepSeek', placeholder: 'Your key' },
|
|
18
|
+
{ value: 'fireworks', name: 'Fireworks AI — Fast serverless inference', placeholder: 'fw_...' },
|
|
19
|
+
{ value: 'perplexity', name: 'Perplexity — Sonar models + web search', placeholder: 'pplx-...' },
|
|
20
|
+
{ value: 'mistral', name: 'Mistral AI — Mistral Large, Codestral', placeholder: 'Your key' },
|
|
21
|
+
{ value: 'deepseek', name: 'DeepSeek — V3, R1 reasoning', placeholder: 'sk-...' },
|
|
22
|
+
{ value: 'cohere', name: 'Cohere — Command R+, Embed', placeholder: 'Your key' },
|
|
23
|
+
{ value: 'replicate', name: 'Replicate — Open-source models', placeholder: 'r8_...' },
|
|
24
|
+
{ value: 'bedrock', name: 'AWS Bedrock — Managed AI on AWS', placeholder: 'Your key' },
|
|
25
|
+
{ value: 'huggingface', name: 'Hugging Face — 200k+ models', placeholder: 'hf_...' },
|
|
26
|
+
{ value: 'custom', name: 'Custom / OpenAI-Compatible — vLLM, LM Studio, Ollama', placeholder: 'Your key' },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
async function connectCommand() {
|
|
30
|
+
console.log();
|
|
31
|
+
console.log(chalk.cyan.bold('🔗 Connect a Provider'));
|
|
32
|
+
console.log(chalk.dim('━'.repeat(50)));
|
|
33
|
+
console.log();
|
|
34
|
+
|
|
35
|
+
const answers = await inquirer.prompt([
|
|
36
|
+
{
|
|
37
|
+
type: 'list',
|
|
38
|
+
name: 'provider',
|
|
39
|
+
message: 'Select your AI provider:',
|
|
40
|
+
choices: PROVIDERS,
|
|
41
|
+
pageSize: 15,
|
|
42
|
+
},
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
const selected = PROVIDERS.find(p => p.value === answers.provider);
|
|
46
|
+
const followUp = [
|
|
47
|
+
{
|
|
48
|
+
type: 'password',
|
|
49
|
+
name: 'apiKey',
|
|
50
|
+
message: `Enter your ${selected.name.split(' — ')[0]} API key:`,
|
|
51
|
+
mask: '•',
|
|
52
|
+
validate: (v) => v.length > 5 ? true : 'API key seems too short',
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
// Custom provider needs base URL
|
|
57
|
+
if (answers.provider === 'custom') {
|
|
58
|
+
followUp.push({
|
|
59
|
+
type: 'input',
|
|
60
|
+
name: 'baseUrl',
|
|
61
|
+
message: 'Base URL (e.g., http://localhost:11434/v1):',
|
|
62
|
+
validate: (v) => v.startsWith('http') ? true : 'Must start with http:// or https://',
|
|
63
|
+
});
|
|
64
|
+
followUp.push({
|
|
65
|
+
type: 'input',
|
|
66
|
+
name: 'customName',
|
|
67
|
+
message: 'Connection name (optional):',
|
|
68
|
+
default: 'custom-endpoint',
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const details = await inquirer.prompt(followUp);
|
|
73
|
+
|
|
74
|
+
const spinner = ora('Encrypting & storing your key...').start();
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const res = await api.createConnection({
|
|
78
|
+
provider: answers.provider,
|
|
79
|
+
apiKey: details.apiKey,
|
|
80
|
+
customName: details.customName,
|
|
81
|
+
baseUrl: details.baseUrl,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (res.status !== 200 && res.status !== 201) {
|
|
85
|
+
throw new Error(res.data?.error || `Server returned ${res.status}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
spinner.succeed(chalk.green('Connection created!'));
|
|
89
|
+
console.log();
|
|
90
|
+
console.log(chalk.dim(' Connection ID: ') + chalk.white.bold(res.data.connection_id));
|
|
91
|
+
console.log(chalk.dim(' Provider: ') + chalk.white(answers.provider));
|
|
92
|
+
console.log();
|
|
93
|
+
console.log(chalk.yellow(' Next step: Generate a security key'));
|
|
94
|
+
console.log(chalk.dim(` Run: ${chalk.cyan('securegate keys create')}`));
|
|
95
|
+
console.log();
|
|
96
|
+
} catch (err) {
|
|
97
|
+
spinner.fail(chalk.red(`Failed: ${err.message}`));
|
|
98
|
+
process.exitCode = 1;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = connectCommand;
|
|
@@ -0,0 +1,232 @@
|
|
|
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
|
+
module.exports = { keysListCommand, keysCreateCommand, keysRevokeCommand, keysLockCommand };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* securegate login — Authenticate with SecureGate
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
const inquirer = require('inquirer');
|
|
7
|
+
const ora = require('ora');
|
|
8
|
+
const api = require('../api');
|
|
9
|
+
const { getAuth } = require('../config');
|
|
10
|
+
|
|
11
|
+
async function loginCommand() {
|
|
12
|
+
console.log();
|
|
13
|
+
console.log(chalk.cyan.bold('🔐 SecureGate Login'));
|
|
14
|
+
console.log(chalk.dim('━'.repeat(50)));
|
|
15
|
+
console.log();
|
|
16
|
+
|
|
17
|
+
// Check if already logged in
|
|
18
|
+
const existing = getAuth();
|
|
19
|
+
if (existing?.user?.email) {
|
|
20
|
+
const { confirm } = await inquirer.prompt([{
|
|
21
|
+
type: 'confirm',
|
|
22
|
+
name: 'confirm',
|
|
23
|
+
message: `Already logged in as ${chalk.cyan(existing.user.email)}. Re-authenticate?`,
|
|
24
|
+
default: false,
|
|
25
|
+
}]);
|
|
26
|
+
if (!confirm) return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const answers = await inquirer.prompt([
|
|
30
|
+
{
|
|
31
|
+
type: 'input',
|
|
32
|
+
name: 'email',
|
|
33
|
+
message: 'Email:',
|
|
34
|
+
validate: (v) => v.includes('@') ? true : 'Enter a valid email',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: 'password',
|
|
38
|
+
name: 'password',
|
|
39
|
+
message: 'Password:',
|
|
40
|
+
mask: '•',
|
|
41
|
+
validate: (v) => v.length >= 6 ? true : 'Password must be at least 6 characters',
|
|
42
|
+
},
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
const spinner = ora('Authenticating...').start();
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const data = await api.login(answers.email, answers.password);
|
|
49
|
+
spinner.succeed(chalk.green('Logged in successfully!'));
|
|
50
|
+
console.log();
|
|
51
|
+
console.log(chalk.dim(` User: ${data.user?.email}`));
|
|
52
|
+
console.log(chalk.dim(` ID: ${data.user?.id?.substring(0, 8)}...`));
|
|
53
|
+
console.log();
|
|
54
|
+
} catch (err) {
|
|
55
|
+
spinner.fail(chalk.red(`Login failed: ${err.message}`));
|
|
56
|
+
process.exitCode = 1;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = loginCommand;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* securegate providers — List available providers
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
|
|
7
|
+
const PROVIDERS = {
|
|
8
|
+
'Major Providers': [
|
|
9
|
+
{ id: 'openai', name: 'OpenAI', desc: 'GPT-4o, GPT-4.1, o3, DALL·E, Whisper' },
|
|
10
|
+
{ id: 'anthropic', name: 'Anthropic', desc: 'Claude 4, Claude 3.5 Sonnet, Haiku' },
|
|
11
|
+
{ id: 'google', name: 'Google AI', desc: 'Gemini 2.5 Pro, Gemini Flash, PaLM' },
|
|
12
|
+
{ id: 'azure', name: 'Azure OpenAI', desc: 'GPT-4o & OpenAI models via Azure' },
|
|
13
|
+
],
|
|
14
|
+
'Cloud & Inference': [
|
|
15
|
+
{ id: 'groq', name: 'Groq', desc: 'Ultra-fast LPU inference for Llama, Mixtral' },
|
|
16
|
+
{ id: 'together', name: 'Together AI', desc: 'Open-source models: Llama, Qwen, DeepSeek' },
|
|
17
|
+
{ id: 'fireworks', name: 'Fireworks AI', desc: 'Fast serverless inference for open models' },
|
|
18
|
+
{ id: 'perplexity', name: 'Perplexity', desc: 'Sonar models with built-in web search' },
|
|
19
|
+
{ id: 'mistral', name: 'Mistral AI', desc: 'Mistral Large, Medium, Codestral' },
|
|
20
|
+
{ id: 'deepseek', name: 'DeepSeek', desc: 'DeepSeek-V3, DeepSeek-R1 reasoning' },
|
|
21
|
+
],
|
|
22
|
+
'Specialized & Enterprise': [
|
|
23
|
+
{ id: 'cohere', name: 'Cohere', desc: 'Command R+, Embed, Rerank for enterprise' },
|
|
24
|
+
{ id: 'replicate', name: 'Replicate', desc: 'Run open-source models via API' },
|
|
25
|
+
{ id: 'bedrock', name: 'AWS Bedrock', desc: 'Managed AI models on AWS infrastructure' },
|
|
26
|
+
{ id: 'huggingface', name: 'Hugging Face', desc: 'Inference API for 200k+ models' },
|
|
27
|
+
],
|
|
28
|
+
'Custom': [
|
|
29
|
+
{ id: 'custom', name: 'Custom / OpenAI-Compatible', desc: 'vLLM, LM Studio, Ollama, LocalAI, etc.' },
|
|
30
|
+
],
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function providersCommand() {
|
|
34
|
+
console.log();
|
|
35
|
+
console.log(chalk.cyan.bold('📦 Available Providers'));
|
|
36
|
+
console.log(chalk.dim('━'.repeat(50)));
|
|
37
|
+
console.log();
|
|
38
|
+
|
|
39
|
+
for (const [category, providers] of Object.entries(PROVIDERS)) {
|
|
40
|
+
console.log(chalk.white.bold(` ${category}`));
|
|
41
|
+
for (const p of providers) {
|
|
42
|
+
console.log(` ${chalk.cyan(p.id.padEnd(16))} ${chalk.dim(p.desc)}`);
|
|
43
|
+
}
|
|
44
|
+
console.log();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.log(chalk.dim(` Run ${chalk.cyan('securegate connect')} to add a provider.`));
|
|
48
|
+
console.log();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = providersCommand;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* securegate status — Show current authentication & connection state
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
const ora = require('ora');
|
|
7
|
+
const { getAuth, getConnections, CONFIG_FILE } = require('../config');
|
|
8
|
+
const api = require('../api');
|
|
9
|
+
|
|
10
|
+
async function statusCommand() {
|
|
11
|
+
console.log();
|
|
12
|
+
console.log(chalk.cyan.bold('📊 SecureGate Status'));
|
|
13
|
+
console.log(chalk.dim('━'.repeat(50)));
|
|
14
|
+
console.log();
|
|
15
|
+
|
|
16
|
+
// Auth status
|
|
17
|
+
const auth = getAuth();
|
|
18
|
+
if (!auth) {
|
|
19
|
+
console.log(chalk.dim(' Auth: ') + chalk.red('Not logged in'));
|
|
20
|
+
console.log(chalk.dim(` Run: ${chalk.cyan('securegate login')}`));
|
|
21
|
+
console.log();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log(chalk.dim(' Auth: ') + chalk.green('● Logged in'));
|
|
26
|
+
console.log(chalk.dim(' Email: ') + chalk.white(auth.user?.email || 'unknown'));
|
|
27
|
+
console.log(chalk.dim(' ID: ') + chalk.dim(auth.user?.id?.substring(0, 8) + '...'));
|
|
28
|
+
|
|
29
|
+
if (auth.expires_at) {
|
|
30
|
+
const remaining = auth.expires_at - Date.now();
|
|
31
|
+
if (remaining > 0) {
|
|
32
|
+
const mins = Math.round(remaining / 60000);
|
|
33
|
+
console.log(chalk.dim(' Token: ') + chalk.green(`Valid (${mins}m remaining)`));
|
|
34
|
+
} else {
|
|
35
|
+
console.log(chalk.dim(' Token: ') + chalk.yellow('Expired (will auto-refresh)'));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
console.log();
|
|
39
|
+
|
|
40
|
+
// Local connections
|
|
41
|
+
const localConns = getConnections();
|
|
42
|
+
const localCount = Object.keys(localConns).length;
|
|
43
|
+
console.log(chalk.dim(` Local keys: ${localCount} saved in ${CONFIG_FILE}`));
|
|
44
|
+
|
|
45
|
+
// Remote connections
|
|
46
|
+
const spinner = ora({ text: 'Fetching remote status...', indent: 2 }).start();
|
|
47
|
+
try {
|
|
48
|
+
const res = await api.listConnections();
|
|
49
|
+
if (res.status === 200) {
|
|
50
|
+
const conns = res.data?.connections || res.data || [];
|
|
51
|
+
spinner.succeed(`${conns.length} connection(s) on server`);
|
|
52
|
+
|
|
53
|
+
for (const c of conns) {
|
|
54
|
+
const keyCount = c.security_keys?.length || 0;
|
|
55
|
+
console.log(chalk.dim(` ${chalk.cyan(c.connection_id)} — ${c.provider} — ${keyCount} key(s)`));
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
spinner.warn('Could not fetch remote status');
|
|
59
|
+
}
|
|
60
|
+
} catch (err) {
|
|
61
|
+
spinner.warn(`Could not reach server: ${err.message}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
console.log();
|
|
65
|
+
// Plan info
|
|
66
|
+
try {
|
|
67
|
+
const profile = await api.getProfile();
|
|
68
|
+
if (profile.status === 200) {
|
|
69
|
+
const plan = profile.data?.plan || 'free';
|
|
70
|
+
const planColor = plan === 'free' ? chalk.dim : chalk.green;
|
|
71
|
+
console.log(chalk.dim(' Plan: ') + planColor(plan.charAt(0).toUpperCase() + plan.slice(1)));
|
|
72
|
+
}
|
|
73
|
+
} catch { }
|
|
74
|
+
|
|
75
|
+
console.log();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = statusCommand;
|
package/src/config.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SecureGate CLI — Configuration & Storage
|
|
3
|
+
* Manages ~/.securegate/config.json
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
const CONFIG_DIR = path.join(os.homedir(), '.securegate');
|
|
11
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
12
|
+
|
|
13
|
+
// Supabase project
|
|
14
|
+
const SUPABASE_URL = 'https://pbrmsfoowrjqsikgkijb.supabase.co';
|
|
15
|
+
const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBicm1zZm9vd3JqcXNpa2draWpiIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Mzg5MTg0NjcsImV4cCI6MjA1NDQ5NDQ2N30.4lGMcORfVTiRxSRcVMeuiECra3SvDpkWRMkJEiRQAS8';
|
|
16
|
+
|
|
17
|
+
// Public-facing proxy URL
|
|
18
|
+
const PROXY_BASE_URL = 'https://securegate.xyz/v1';
|
|
19
|
+
|
|
20
|
+
function ensureConfigDir() {
|
|
21
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
22
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function loadConfig() {
|
|
27
|
+
try {
|
|
28
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
29
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
30
|
+
}
|
|
31
|
+
} catch { }
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function saveConfig(config) {
|
|
36
|
+
ensureConfigDir();
|
|
37
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getAuth() {
|
|
41
|
+
const config = loadConfig();
|
|
42
|
+
return config.auth || null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function setAuth(auth) {
|
|
46
|
+
const config = loadConfig();
|
|
47
|
+
config.auth = auth;
|
|
48
|
+
saveConfig(config);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function clearAuth() {
|
|
52
|
+
const config = loadConfig();
|
|
53
|
+
delete config.auth;
|
|
54
|
+
saveConfig(config);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getConnections() {
|
|
58
|
+
const config = loadConfig();
|
|
59
|
+
return config.connections || {};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function setConnection(connectionId, data) {
|
|
63
|
+
const config = loadConfig();
|
|
64
|
+
if (!config.connections) config.connections = {};
|
|
65
|
+
config.connections[connectionId] = data;
|
|
66
|
+
saveConfig(config);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = {
|
|
70
|
+
CONFIG_DIR,
|
|
71
|
+
CONFIG_FILE,
|
|
72
|
+
SUPABASE_URL,
|
|
73
|
+
SUPABASE_ANON_KEY,
|
|
74
|
+
PROXY_BASE_URL,
|
|
75
|
+
loadConfig,
|
|
76
|
+
saveConfig,
|
|
77
|
+
getAuth,
|
|
78
|
+
setAuth,
|
|
79
|
+
clearAuth,
|
|
80
|
+
getConnections,
|
|
81
|
+
setConnection,
|
|
82
|
+
};
|