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 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,5 @@
1
+ /**
2
+ * Authentication Command
3
+ */
4
+ export declare function authCommand(action: 'login' | 'logout' | 'status'): Promise<void>;
5
+ export default authCommand;
@@ -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;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Config Command - Manage CLI configuration
3
+ */
4
+ export declare function configCommand(action: 'set' | 'get' | 'list', key?: string, value?: string): Promise<void>;
5
+ export default configCommand;