securegate-cli-tool 2.0.1 → 2.1.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.
@@ -1,60 +1,191 @@
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;
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 http = require('http');
9
+ const open = require('open');
10
+ const api = require('../api');
11
+ const { getAuth, SUPABASE_URL, SUPABASE_ANON_KEY } = require('../config');
12
+
13
+ // Use production SecureGate domain by default, fallback to localhost for dev
14
+ const SECUREGATE_WEB_URL = process.env.SECUREGATE_WEB_URL || 'https://usesecuregate.com';
15
+
16
+ async function loginCommand() {
17
+ console.log();
18
+ console.log(chalk.cyan.bold('🔐 SecureGate Login'));
19
+ console.log(chalk.dim('━'.repeat(50)));
20
+ console.log();
21
+
22
+ // Check if already logged in
23
+ const existing = getAuth();
24
+ if (existing?.user?.email) {
25
+ const { confirm } = await inquirer.prompt([{
26
+ type: 'confirm',
27
+ name: 'confirm',
28
+ message: `Already logged in as ${chalk.cyan(existing.user.email)}. Re-authenticate?`,
29
+ default: false,
30
+ }]);
31
+ if (!confirm) return;
32
+ }
33
+
34
+ const spinner = ora('Starting local authentication server...').start();
35
+
36
+ return new Promise((resolve, reject) => {
37
+ // Spin up a local HTTP server
38
+ const server = http.createServer();
39
+
40
+ server.on('request', async (req, res) => {
41
+ // Add CORS headers so the SecureGate web app can POST to localhost
42
+ res.setHeader('Access-Control-Allow-Origin', '*');
43
+ res.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET, POST');
44
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
45
+
46
+ if (req.method === 'OPTIONS') {
47
+ res.writeHead(200);
48
+ res.end();
49
+ return;
50
+ }
51
+
52
+ if (req.url === '/auth' && req.method === 'GET') {
53
+ // Return a simple HTML page that grabs the URL hash fragment (from Supabase OAuth/Magic Link)
54
+ // and POSTs it back to our local proxy /callback
55
+ res.writeHead(200, { 'Content-Type': 'text/html' });
56
+ res.end(`
57
+ <html>
58
+ <head><title>SecureGate CLI Authentication</title></head>
59
+ <body>
60
+ <script>
61
+ const hash = window.location.hash.substring(1);
62
+ if (!hash) {
63
+ // No hash present, might be a direct visit instead of redirect
64
+ document.body.innerHTML = '<h2>No session found. Please try logging in again.</h2>';
65
+ } else {
66
+ const params = new URLSearchParams(hash);
67
+ const payload = {
68
+ access_token: params.get('access_token'),
69
+ refresh_token: params.get('refresh_token')
70
+ };
71
+
72
+ fetch('http://127.0.0.1:' + window.location.port + '/callback', {
73
+ method: 'POST',
74
+ headers: { 'Content-Type': 'application/json' },
75
+ body: JSON.stringify(payload)
76
+ }).then(() => {
77
+ document.body.innerHTML = '<h2 style="font-family: sans-serif; color: #10b981; text-align: center; margin-top: 50px;">Authentication successful! You can close this tab and return to your terminal.</h2>';
78
+ setTimeout(() => window.close(), 2000); // Attempt to close, some browsers block this
79
+ }).catch(err => {
80
+ console.error(err);
81
+ document.body.innerHTML = '<h2 style="font-family: sans-serif; color: #ef4444; text-align: center; margin-top: 50px;">Failed to send session to CLI. Check your terminal.</h2>';
82
+ });
83
+ }
84
+ </script>
85
+ </body>
86
+ </html>
87
+ `);
88
+ } else if (req.url === '/callback' && req.method === 'POST') {
89
+ // Receive the JSON payload sent from the /auth HTML page above
90
+ let body = '';
91
+ req.on('data', chunk => {
92
+ body += chunk.toString();
93
+ });
94
+ req.on('end', async () => {
95
+ try {
96
+ const payload = JSON.parse(body);
97
+
98
+ // We must fetch the user profile securely given the access token
99
+ spinner.text = 'Verifying session...';
100
+
101
+ // Set the initial auth context in the API client to make requests
102
+ // We manually overwrite the globally configured client to bypass normal auth requirement
103
+ const { createClient } = require('@supabase/supabase-js');
104
+ api.supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
105
+ auth: { persistSession: false },
106
+ global: { headers: { Authorization: `Bearer ${payload.access_token}` } }
107
+ });
108
+
109
+ // Request user data using the new token to confirm it's valid and get email
110
+ const { data: { user }, error } = await api.supabase.auth.getUser();
111
+
112
+ if (error || !user) {
113
+ console.error("\n[DEBUG] getUser failed:", { error, user, access_token: payload.access_token.substring(0, 15) + '...' });
114
+ throw new Error('Failed to verify user token');
115
+ }
116
+
117
+ // Save the full session permanently
118
+ api.setAuth({
119
+ user: {
120
+ id: user.id,
121
+ email: user.email,
122
+ },
123
+ access_token: payload.access_token,
124
+ refresh_token: payload.refresh_token
125
+ });
126
+
127
+ spinner.succeed(chalk.green('Logged in successfully!'));
128
+ console.log();
129
+ console.log(chalk.dim(` User: ${user.email}`));
130
+ console.log(chalk.dim(` ID: ${user.id.substring(0, 8)}...`));
131
+ console.log();
132
+
133
+ res.writeHead(200, { 'Content-Type': 'application/json' });
134
+ res.end(JSON.stringify({ success: true }));
135
+
136
+ // Close server and resolve the command promise
137
+ server.close();
138
+ resolve();
139
+ } catch (err) {
140
+ res.writeHead(400, { 'Content-Type': 'application/json' });
141
+ res.end(JSON.stringify({ success: false, error: err.message }));
142
+
143
+ spinner.fail(chalk.red(`Login parsing failed: ${err.message}`));
144
+ server.close();
145
+ process.exitCode = 1;
146
+ reject(err);
147
+ }
148
+ });
149
+ } else {
150
+ res.writeHead(404);
151
+ res.end('Not Found');
152
+ }
153
+ });
154
+
155
+ // Listen on a random available port on localhost
156
+ server.listen(0, '127.0.0.1', async () => {
157
+ const port = server.address().port;
158
+ const authUrl = `${SECUREGATE_WEB_URL}/cli/auth?port=${port}`;
159
+
160
+ spinner.text = 'Opening browser to authenticate...';
161
+ console.log(chalk.dim(`\nIf your browser does not open automatically, visit:\n${chalk.cyan(authUrl)}\n`));
162
+
163
+ try {
164
+ // Automatically open the user's default web browser to the SecureGate app
165
+ await open(authUrl);
166
+ spinner.text = 'Waiting for browser authentication...';
167
+ } catch (err) {
168
+ // Fallback if 'open' fails (e.g., inside an SSH session or container)
169
+ spinner.text = 'Waiting for authentication...';
170
+ }
171
+ });
172
+
173
+ server.on('error', (err) => {
174
+ spinner.fail(chalk.red(`Failed to start local server: ${err.message}`));
175
+ process.exitCode = 1;
176
+ reject(err);
177
+ });
178
+
179
+ // Timeout after 5 minutes
180
+ setTimeout(() => {
181
+ if (server.listening) {
182
+ server.close();
183
+ spinner.fail(chalk.red('Authentication timed out.'));
184
+ process.exitCode = 1;
185
+ reject(new Error('Timeout'));
186
+ }
187
+ }, 5 * 60 * 1000);
188
+ });
189
+ }
190
+
191
+ module.exports = loginCommand;
@@ -1,51 +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;
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;
@@ -1,78 +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;
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;