securegate-cli-tool 2.0.4 → 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.
- package/package.json +4 -4
- package/scripts/postinstall.js +37 -37
- package/src/api.js +213 -205
- package/src/commands/connect.js +102 -102
- package/src/commands/keys.js +280 -232
- package/src/commands/login.js +191 -60
- package/src/commands/providers.js +51 -51
- package/src/commands/status.js +78 -78
- package/src/config.js +82 -82
- package/src/index.js +165 -187
- package/templates/SKILL.md +59 -62
- package/templates/AGENTCONNECT.md +0 -66
package/src/commands/login.js
CHANGED
|
@@ -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
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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 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;
|
package/src/commands/status.js
CHANGED
|
@@ -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;
|