enton-cli 0.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/README.md +188 -0
- package/bin/enton.js +34 -0
- package/dist/api/client.d.ts +61 -0
- package/dist/api/client.js +160 -0
- package/dist/commands/auth.d.ts +5 -0
- package/dist/commands/auth.js +156 -0
- package/dist/commands/config.d.ts +5 -0
- package/dist/commands/config.js +96 -0
- package/dist/commands/interactive.d.ts +7 -0
- package/dist/commands/interactive.js +143 -0
- package/dist/commands/query.d.ts +8 -0
- package/dist/commands/query.js +73 -0
- package/dist/commands/watch.d.ts +5 -0
- package/dist/commands/watch.js +98 -0
- package/dist/config/manager.d.ts +54 -0
- package/dist/config/manager.js +168 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +104 -0
- package/package.json +65 -0
package/README.md
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# ENTON CLI
|
|
2
|
+
|
|
3
|
+
š¦ **AI-powered financial assistant** - like Claude Code, but for finance.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @enton/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or run directly with npx:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx @enton/cli "What is the price of AAPL?"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### Interactive Mode
|
|
20
|
+
|
|
21
|
+
Just run `enton` to start an interactive session:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
enton
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
29
|
+
ā š¦ ENTON - AI Finance Agent ā
|
|
30
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
31
|
+
|
|
32
|
+
Type your questions or commands. Type "exit" to quit.
|
|
33
|
+
|
|
34
|
+
> What is the price of AAPL?
|
|
35
|
+
š§ get_stock_quote
|
|
36
|
+
|
|
37
|
+
The current price for Apple (AAPL) is **$262.50**, down 1.78% from the previous close.
|
|
38
|
+
|
|
39
|
+
š Tools: get_stock_quote | 1523ms
|
|
40
|
+
|
|
41
|
+
>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Direct Query Mode
|
|
45
|
+
|
|
46
|
+
Run a single query:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
enton "Get me news about Tesla"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Watch Mode
|
|
53
|
+
|
|
54
|
+
Monitor real-time prices:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
enton watch AAPL,TSLA,NVDA
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
š ENTON Watch Mode
|
|
62
|
+
Last update: 3:45:21 PM
|
|
63
|
+
|
|
64
|
+
āāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāāāā¬āāāāāāāāāāā
|
|
65
|
+
ā Symbol ā Price ā Change ā % Change ā
|
|
66
|
+
āāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāāāā¼āāāāāāāāāāā¤
|
|
67
|
+
ā AAPL ā $262.50 ā ā¼ $4.76 ā -1.78% ā
|
|
68
|
+
ā TSLA ā $245.80 ā ā² $12.30 ā +5.27% ā
|
|
69
|
+
ā NVDA ā $138.20 ā ā² $2.15 ā +1.58% ā
|
|
70
|
+
āāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāāāā“āāāāāāāāāāā
|
|
71
|
+
|
|
72
|
+
Press Ctrl+C to exit
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Authentication
|
|
76
|
+
|
|
77
|
+
Login with your ENTON account:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
enton auth login
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Or use an environment variable:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
export ENTON_API_KEY=your_api_key
|
|
87
|
+
enton
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Commands
|
|
91
|
+
|
|
92
|
+
| Command | Description |
|
|
93
|
+
|---------|-------------|
|
|
94
|
+
| `enton` | Start interactive mode |
|
|
95
|
+
| `enton "query"` | Run a single query |
|
|
96
|
+
| `enton auth login` | Authenticate with ENTON |
|
|
97
|
+
| `enton auth logout` | Log out |
|
|
98
|
+
| `enton auth status` | Check authentication status |
|
|
99
|
+
| `enton watch SYMBOLS` | Watch real-time prices |
|
|
100
|
+
| `enton config list` | List configuration |
|
|
101
|
+
| `enton config set KEY VALUE` | Set a config value |
|
|
102
|
+
|
|
103
|
+
## Interactive Commands
|
|
104
|
+
|
|
105
|
+
While in interactive mode:
|
|
106
|
+
|
|
107
|
+
- `/help` - Show available commands
|
|
108
|
+
- `/clear` - Clear the screen
|
|
109
|
+
- `/status` - Show connection status
|
|
110
|
+
- `exit`, `quit`, `q` - Exit
|
|
111
|
+
|
|
112
|
+
## Configuration
|
|
113
|
+
|
|
114
|
+
Configure with `enton config`:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# Set API URL
|
|
118
|
+
enton config set apiUrl https://api.enton.ai
|
|
119
|
+
|
|
120
|
+
# Set output format
|
|
121
|
+
enton config set outputFormat json
|
|
122
|
+
|
|
123
|
+
# List all config
|
|
124
|
+
enton config list
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## JSON Output
|
|
128
|
+
|
|
129
|
+
Use `--json` flag for machine-readable output:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
enton "price of AAPL" --json
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"answer": "The current price for Apple (AAPL) is $262.50...",
|
|
138
|
+
"toolsUsed": ["get_stock_quote"],
|
|
139
|
+
"iterations": 2,
|
|
140
|
+
"totalTime": 1523
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Examples
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
# Stock prices
|
|
148
|
+
enton "What is the price of AAPL?"
|
|
149
|
+
enton "Compare NVDA vs AMD"
|
|
150
|
+
|
|
151
|
+
# News
|
|
152
|
+
enton "Get me news about Tesla"
|
|
153
|
+
enton "Latest market news"
|
|
154
|
+
|
|
155
|
+
# Analysis
|
|
156
|
+
enton "Analyze MSFT stock"
|
|
157
|
+
enton "Technical analysis for SPY"
|
|
158
|
+
|
|
159
|
+
# Portfolio (requires auth)
|
|
160
|
+
enton "Show my portfolio"
|
|
161
|
+
enton "What are my positions?"
|
|
162
|
+
|
|
163
|
+
# Trading (requires auth)
|
|
164
|
+
enton "Buy 10 shares of AAPL"
|
|
165
|
+
enton "Show my open orders"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Development
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# Clone and install
|
|
172
|
+
git clone https://github.com/enton-ai/enton-cli
|
|
173
|
+
cd enton-cli
|
|
174
|
+
npm install
|
|
175
|
+
|
|
176
|
+
# Run in development mode
|
|
177
|
+
npm run dev
|
|
178
|
+
|
|
179
|
+
# Build
|
|
180
|
+
npm run build
|
|
181
|
+
|
|
182
|
+
# Test
|
|
183
|
+
npm test
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## License
|
|
187
|
+
|
|
188
|
+
MIT Ā© ENTON AI
|
package/bin/enton.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ENTON CLI Entry Point
|
|
5
|
+
*
|
|
6
|
+
* This file bootstraps the CLI application.
|
|
7
|
+
* The actual logic is in dist/index.js (compiled TypeScript)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
// Check if we're in development mode
|
|
13
|
+
const isDev = process.env.NODE_ENV === 'development' || process.argv.includes('--dev');
|
|
14
|
+
|
|
15
|
+
if (isDev) {
|
|
16
|
+
// In development, use tsx to run TypeScript directly
|
|
17
|
+
require('child_process').spawn(
|
|
18
|
+
'npx',
|
|
19
|
+
['tsx', path.join(__dirname, '..', 'src', 'index.ts'), ...process.argv.slice(2)],
|
|
20
|
+
{ stdio: 'inherit' }
|
|
21
|
+
);
|
|
22
|
+
} else {
|
|
23
|
+
// In production, run compiled JavaScript
|
|
24
|
+
try {
|
|
25
|
+
require('../dist/index.js');
|
|
26
|
+
} catch (error) {
|
|
27
|
+
if (error.code === 'MODULE_NOT_FOUND') {
|
|
28
|
+
console.error('ENTON CLI is not built yet. Run `npm run build` first.');
|
|
29
|
+
console.error('Or use `npm run dev` for development mode.');
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ENTON API Client
|
|
3
|
+
* Handles communication with the ENTON backend
|
|
4
|
+
*/
|
|
5
|
+
export interface QueryOptions {
|
|
6
|
+
stream?: boolean;
|
|
7
|
+
onThinking?: (message: string) => void;
|
|
8
|
+
onToolStart?: (tool: string, args: any) => void;
|
|
9
|
+
onToolResult?: (tool: string, success: boolean, data?: any) => void;
|
|
10
|
+
onAnswer?: (answer: string) => void;
|
|
11
|
+
}
|
|
12
|
+
export interface QueryResponse {
|
|
13
|
+
answer: string;
|
|
14
|
+
toolsUsed: string[];
|
|
15
|
+
iterations: number;
|
|
16
|
+
totalTime: number;
|
|
17
|
+
metadata?: any;
|
|
18
|
+
}
|
|
19
|
+
export declare class EntonAPIClient {
|
|
20
|
+
baseUrl: string;
|
|
21
|
+
private apiKey;
|
|
22
|
+
private client;
|
|
23
|
+
constructor(baseUrl?: string, apiKey?: string | null);
|
|
24
|
+
/**
|
|
25
|
+
* Check if client is authenticated
|
|
26
|
+
*/
|
|
27
|
+
isAuthenticated(): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Update API key
|
|
30
|
+
*/
|
|
31
|
+
setApiKey(apiKey: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* Send a query to ENTON with streaming support
|
|
34
|
+
*/
|
|
35
|
+
query(query: string, options?: QueryOptions): Promise<QueryResponse>;
|
|
36
|
+
/**
|
|
37
|
+
* Stream query with SSE
|
|
38
|
+
*/
|
|
39
|
+
private streamQuery;
|
|
40
|
+
/**
|
|
41
|
+
* Get user's portfolio (requires auth)
|
|
42
|
+
*/
|
|
43
|
+
getPortfolio(): Promise<any>;
|
|
44
|
+
/**
|
|
45
|
+
* Get stock quote
|
|
46
|
+
*/
|
|
47
|
+
getQuote(symbol: string): Promise<any>;
|
|
48
|
+
/**
|
|
49
|
+
* Get news for a symbol
|
|
50
|
+
*/
|
|
51
|
+
getNews(query: string): Promise<any>;
|
|
52
|
+
/**
|
|
53
|
+
* Place an order (requires auth)
|
|
54
|
+
*/
|
|
55
|
+
placeOrder(params: {
|
|
56
|
+
symbol: string;
|
|
57
|
+
side: 'buy' | 'sell';
|
|
58
|
+
quantity: number;
|
|
59
|
+
}): Promise<any>;
|
|
60
|
+
}
|
|
61
|
+
export default EntonAPIClient;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ENTON API Client
|
|
4
|
+
* Handles communication with the ENTON backend
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.EntonAPIClient = void 0;
|
|
11
|
+
const axios_1 = __importDefault(require("axios"));
|
|
12
|
+
const eventsource_1 = __importDefault(require("eventsource"));
|
|
13
|
+
class EntonAPIClient {
|
|
14
|
+
constructor(baseUrl = 'https://api.enton.ai', apiKey = null) {
|
|
15
|
+
this.baseUrl = baseUrl;
|
|
16
|
+
this.apiKey = apiKey;
|
|
17
|
+
this.client = axios_1.default.create({
|
|
18
|
+
baseURL: baseUrl,
|
|
19
|
+
timeout: 300000, // 5 minutes for complex queries
|
|
20
|
+
headers: {
|
|
21
|
+
'Content-Type': 'application/json',
|
|
22
|
+
...(apiKey ? { 'Authorization': `Bearer ${apiKey}` } : {})
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Check if client is authenticated
|
|
28
|
+
*/
|
|
29
|
+
isAuthenticated() {
|
|
30
|
+
return !!this.apiKey;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Update API key
|
|
34
|
+
*/
|
|
35
|
+
setApiKey(apiKey) {
|
|
36
|
+
this.apiKey = apiKey;
|
|
37
|
+
this.client.defaults.headers['Authorization'] = `Bearer ${apiKey}`;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Send a query to ENTON with streaming support
|
|
41
|
+
*/
|
|
42
|
+
async query(query, options = {}) {
|
|
43
|
+
if (options.stream !== false) {
|
|
44
|
+
return this.streamQuery(query, options);
|
|
45
|
+
}
|
|
46
|
+
// Non-streaming query
|
|
47
|
+
const response = await this.client.post('/api/ai-agent-system', {
|
|
48
|
+
task: query
|
|
49
|
+
});
|
|
50
|
+
return {
|
|
51
|
+
answer: response.data.answer || response.data.summary,
|
|
52
|
+
toolsUsed: response.data.result?.metadata?.toolsUsed || [],
|
|
53
|
+
iterations: response.data.result?.metadata?.iterations || 1,
|
|
54
|
+
totalTime: response.data.result?.metadata?.totalTime || 0,
|
|
55
|
+
metadata: response.data.result?.metadata
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Stream query with SSE
|
|
60
|
+
*/
|
|
61
|
+
streamQuery(query, options) {
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
const encodedQuery = encodeURIComponent(query);
|
|
64
|
+
const url = `${this.baseUrl}/api/ai-agent-system?task=${encodedQuery}`;
|
|
65
|
+
const headers = {};
|
|
66
|
+
if (this.apiKey) {
|
|
67
|
+
headers['Authorization'] = `Bearer ${this.apiKey}`;
|
|
68
|
+
}
|
|
69
|
+
const eventSource = new eventsource_1.default(url, { headers });
|
|
70
|
+
const toolsUsed = [];
|
|
71
|
+
let answer = '';
|
|
72
|
+
let iterations = 0;
|
|
73
|
+
let startTime = Date.now();
|
|
74
|
+
eventSource.onmessage = (event) => {
|
|
75
|
+
try {
|
|
76
|
+
const data = JSON.parse(event.data);
|
|
77
|
+
switch (data.type) {
|
|
78
|
+
case 'heartbeat':
|
|
79
|
+
// Keep-alive, ignore
|
|
80
|
+
break;
|
|
81
|
+
case 'thought':
|
|
82
|
+
case 'thinking':
|
|
83
|
+
options.onThinking?.(data.message || 'Thinking...');
|
|
84
|
+
break;
|
|
85
|
+
case 'tool_selection':
|
|
86
|
+
case 'tool_execution_start':
|
|
87
|
+
options.onToolStart?.(data.toolName || data.tool, data.toolInput || data.args);
|
|
88
|
+
if (data.toolName && !toolsUsed.includes(data.toolName)) {
|
|
89
|
+
toolsUsed.push(data.toolName);
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
case 'tool_execution_result':
|
|
93
|
+
options.onToolResult?.(data.toolName || data.tool, data.data?.success !== false, data.data);
|
|
94
|
+
break;
|
|
95
|
+
case 'final_answer_preparation':
|
|
96
|
+
case 'answer':
|
|
97
|
+
answer = data.data?.answer || data.answer || data.message;
|
|
98
|
+
options.onAnswer?.(answer);
|
|
99
|
+
break;
|
|
100
|
+
case 'final_result':
|
|
101
|
+
answer = data.answer || data.content || data.summary || answer;
|
|
102
|
+
iterations = data.result?.metadata?.iterations || iterations;
|
|
103
|
+
eventSource.close();
|
|
104
|
+
resolve({
|
|
105
|
+
answer,
|
|
106
|
+
toolsUsed,
|
|
107
|
+
iterations,
|
|
108
|
+
totalTime: Date.now() - startTime,
|
|
109
|
+
metadata: data.result?.metadata
|
|
110
|
+
});
|
|
111
|
+
break;
|
|
112
|
+
case 'error_event':
|
|
113
|
+
eventSource.close();
|
|
114
|
+
reject(new Error(data.message || 'Unknown error'));
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
// Ignore parse errors for non-JSON messages
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
eventSource.onerror = (error) => {
|
|
123
|
+
eventSource.close();
|
|
124
|
+
reject(new Error('Connection error'));
|
|
125
|
+
};
|
|
126
|
+
// Timeout after 5 minutes
|
|
127
|
+
setTimeout(() => {
|
|
128
|
+
eventSource.close();
|
|
129
|
+
reject(new Error('Request timeout'));
|
|
130
|
+
}, 300000);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get user's portfolio (requires auth)
|
|
135
|
+
*/
|
|
136
|
+
async getPortfolio() {
|
|
137
|
+
const response = await this.query('show my portfolio');
|
|
138
|
+
return response;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get stock quote
|
|
142
|
+
*/
|
|
143
|
+
async getQuote(symbol) {
|
|
144
|
+
return this.query(`What is the price of ${symbol}?`);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get news for a symbol
|
|
148
|
+
*/
|
|
149
|
+
async getNews(query) {
|
|
150
|
+
return this.query(`Get me news about ${query}`);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Place an order (requires auth)
|
|
154
|
+
*/
|
|
155
|
+
async placeOrder(params) {
|
|
156
|
+
return this.query(`${params.side} ${params.quantity} shares of ${params.symbol}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
exports.EntonAPIClient = EntonAPIClient;
|
|
160
|
+
exports.default = EntonAPIClient;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Authentication Command
|
|
4
|
+
*/
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.authCommand = authCommand;
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
const ora_1 = __importDefault(require("ora"));
|
|
12
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
13
|
+
const open_1 = __importDefault(require("open"));
|
|
14
|
+
const manager_1 = require("../config/manager");
|
|
15
|
+
const client_1 = require("../api/client");
|
|
16
|
+
async function authCommand(action) {
|
|
17
|
+
switch (action) {
|
|
18
|
+
case 'login':
|
|
19
|
+
await login();
|
|
20
|
+
break;
|
|
21
|
+
case 'logout':
|
|
22
|
+
await logout();
|
|
23
|
+
break;
|
|
24
|
+
case 'status':
|
|
25
|
+
await status();
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async function login() {
|
|
30
|
+
console.log(chalk_1.default.bold('\nš ENTON Authentication\n'));
|
|
31
|
+
const { method } = await inquirer_1.default.prompt([
|
|
32
|
+
{
|
|
33
|
+
type: 'list',
|
|
34
|
+
name: 'method',
|
|
35
|
+
message: 'How would you like to authenticate?',
|
|
36
|
+
choices: [
|
|
37
|
+
{ name: 'Open browser (recommended)', value: 'browser' },
|
|
38
|
+
{ name: 'Enter API key manually', value: 'manual' },
|
|
39
|
+
{ name: 'Use environment variable', value: 'env' }
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
]);
|
|
43
|
+
switch (method) {
|
|
44
|
+
case 'browser':
|
|
45
|
+
await browserAuth();
|
|
46
|
+
break;
|
|
47
|
+
case 'manual':
|
|
48
|
+
await manualAuth();
|
|
49
|
+
break;
|
|
50
|
+
case 'env':
|
|
51
|
+
await envAuth();
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async function browserAuth() {
|
|
56
|
+
console.log(chalk_1.default.dim('\nOpening ENTON in your browser...\n'));
|
|
57
|
+
const config = (0, manager_1.getConfig)();
|
|
58
|
+
const authUrl = `${config.apiUrl}/settings/api-keys`;
|
|
59
|
+
await (0, open_1.default)(authUrl);
|
|
60
|
+
console.log(chalk_1.default.cyan(`If the browser doesn't open, visit:\n${authUrl}\n`));
|
|
61
|
+
const { apiKey } = await inquirer_1.default.prompt([
|
|
62
|
+
{
|
|
63
|
+
type: 'password',
|
|
64
|
+
name: 'apiKey',
|
|
65
|
+
message: 'Paste your API key here:',
|
|
66
|
+
mask: '*',
|
|
67
|
+
validate: (input) => input.length > 0 || 'API key is required'
|
|
68
|
+
}
|
|
69
|
+
]);
|
|
70
|
+
await verifyAndSaveKey(apiKey);
|
|
71
|
+
}
|
|
72
|
+
async function manualAuth() {
|
|
73
|
+
const { apiKey } = await inquirer_1.default.prompt([
|
|
74
|
+
{
|
|
75
|
+
type: 'password',
|
|
76
|
+
name: 'apiKey',
|
|
77
|
+
message: 'Enter your API key:',
|
|
78
|
+
mask: '*',
|
|
79
|
+
validate: (input) => input.length > 0 || 'API key is required'
|
|
80
|
+
}
|
|
81
|
+
]);
|
|
82
|
+
await verifyAndSaveKey(apiKey);
|
|
83
|
+
}
|
|
84
|
+
async function envAuth() {
|
|
85
|
+
const envKey = process.env.ENTON_API_KEY;
|
|
86
|
+
if (!envKey) {
|
|
87
|
+
console.log(chalk_1.default.red('\nā ENTON_API_KEY environment variable not set\n'));
|
|
88
|
+
console.log(chalk_1.default.dim('Set it with:'));
|
|
89
|
+
console.log(chalk_1.default.cyan(' export ENTON_API_KEY=your_api_key\n'));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
await verifyAndSaveKey(envKey);
|
|
93
|
+
}
|
|
94
|
+
async function verifyAndSaveKey(apiKey) {
|
|
95
|
+
const spinner = (0, ora_1.default)('Verifying API key...').start();
|
|
96
|
+
try {
|
|
97
|
+
const config = (0, manager_1.getConfig)();
|
|
98
|
+
const client = new client_1.EntonAPIClient(config.apiUrl, apiKey);
|
|
99
|
+
// Test the key with a simple query
|
|
100
|
+
const response = await client.query('hello', { stream: false });
|
|
101
|
+
if (response.answer) {
|
|
102
|
+
// Save the key securely
|
|
103
|
+
await (0, manager_1.saveApiKey)(apiKey);
|
|
104
|
+
spinner.succeed(chalk_1.default.green('Successfully authenticated!'));
|
|
105
|
+
console.log(chalk_1.default.dim('\nYour API key has been saved securely.\n'));
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
spinner.fail(chalk_1.default.red('Invalid API key'));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
spinner.fail(chalk_1.default.red('Authentication failed'));
|
|
113
|
+
console.log(chalk_1.default.dim(` ${error.message}\n`));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async function logout() {
|
|
117
|
+
const { confirm } = await inquirer_1.default.prompt([
|
|
118
|
+
{
|
|
119
|
+
type: 'confirm',
|
|
120
|
+
name: 'confirm',
|
|
121
|
+
message: 'Are you sure you want to logout?',
|
|
122
|
+
default: false
|
|
123
|
+
}
|
|
124
|
+
]);
|
|
125
|
+
if (confirm) {
|
|
126
|
+
await (0, manager_1.deleteApiKey)();
|
|
127
|
+
console.log(chalk_1.default.green('\nā
Successfully logged out\n'));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async function status() {
|
|
131
|
+
console.log(chalk_1.default.bold('\nš Authentication Status\n'));
|
|
132
|
+
const apiKey = await (0, manager_1.getApiKey)();
|
|
133
|
+
const config = (0, manager_1.getConfig)();
|
|
134
|
+
console.log(` API URL: ${chalk_1.default.cyan(config.apiUrl)}`);
|
|
135
|
+
if (apiKey) {
|
|
136
|
+
console.log(` Status: ${chalk_1.default.green('ā
Authenticated')}`);
|
|
137
|
+
console.log(` API Key: ${chalk_1.default.dim(apiKey.substring(0, 8) + '...')}`);
|
|
138
|
+
// Test connection
|
|
139
|
+
const spinner = (0, ora_1.default)('Testing connection...').start();
|
|
140
|
+
try {
|
|
141
|
+
const client = new client_1.EntonAPIClient(config.apiUrl, apiKey);
|
|
142
|
+
await client.query('hello', { stream: false });
|
|
143
|
+
spinner.succeed(chalk_1.default.green('Connection OK'));
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
spinner.fail(chalk_1.default.red('Connection failed'));
|
|
147
|
+
console.log(chalk_1.default.dim(` ${error.message}`));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
console.log(` Status: ${chalk_1.default.yellow('ā ļø Not authenticated')}`);
|
|
152
|
+
console.log(chalk_1.default.dim('\n Run `enton auth login` to authenticate\n'));
|
|
153
|
+
}
|
|
154
|
+
console.log('');
|
|
155
|
+
}
|
|
156
|
+
exports.default = authCommand;
|