alpha-cli-toolkit 1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Linkxee-Tech
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,168 @@
1
+ # 🧠 Alpha CLI — Autonomous Agent Onchain Toolkit
2
+
3
+ > A terminal-first autonomous agent for crypto & TradFi markets.
4
+ > Powered by **Nansen** (on-chain analytics) + **Yahoo Finance** (traditional markets) with **Phantom Wallet** authentication.
5
+
6
+ Built for the [Nansen CLI Build Challenge](https://nansen.ai).
7
+
8
+ ---
9
+
10
+ ## Architecture
11
+
12
+ ```
13
+ alpha-cli/
14
+ ├── backend/ Express + MongoDB API server
15
+ │ ├── src/
16
+ │ │ ├── controllers/ Route handlers (auth, market, triggers)
17
+ │ │ ├── middleware/ JWT authentication guard
18
+ │ │ ├── models/ Mongoose schemas (User, Trigger, Position)
19
+ │ │ ├── routes/ Express route definitions
20
+ │ │ └── services/ Nansen API, Yahoo Finance, Trigger Engine
21
+ │ └── index.js Server entrypoint
22
+ ├── cli/ Command-line interface
23
+ │ ├── bin/alpha.js CLI entrypoint (Commander.js)
24
+ │ └── utils/auth.js Phantom wallet auth flow via browser
25
+ └── (alpha-cli-ui/) Next.js Phantom Wallet authentication UI
26
+ ```
27
+
28
+ ### Data Flow
29
+
30
+ ```
31
+ CLI (alpha login) → opens browser → Next.js UI → Phantom wallet sign
32
+ → Backend verifies signature → returns JWT → CLI stores JWT locally
33
+ → all subsequent CLI commands send JWT in Authorization header
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Setup
39
+
40
+ ### Prerequisites
41
+
42
+ - **Node.js** ≥ 18
43
+ - **MongoDB** running locally (or a remote URI)
44
+ - **Phantom Wallet** browser extension
45
+
46
+ ### 1. Backend
47
+
48
+ ```bash
49
+ cd backend
50
+ cp .env.example .env # Edit with your MongoDB URI & secrets
51
+ npm install
52
+ npm run dev # Starts on http://localhost:4000
53
+ ```
54
+
55
+ ### 2. Auth UI
56
+
57
+ ```bash
58
+ cd ../alpha-cli-ui # Separate repo
59
+ npm install
60
+ npm run dev # Starts on http://localhost:3000
61
+ ```
62
+
63
+ ### 3. CLI
64
+
65
+ ```bash
66
+ cd ../cli
67
+ npm install
68
+ npm link # Makes `alpha` available globally
69
+ ```
70
+
71
+ ---
72
+
73
+ ## CLI Commands
74
+
75
+ ### Authentication
76
+
77
+ | Command | Description |
78
+ |-----------------|-------------------------------------------|
79
+ | `alpha login` | Open browser → Phantom sign → store JWT |
80
+ | `alpha logout` | Clear stored authentication session |
81
+ | `alpha status` | Check auth status & backend health |
82
+
83
+ ### Market Data
84
+
85
+ | Command | Description |
86
+ |------------------------|---------------------------------------------------|
87
+ | `alpha market` | Trending crypto via Nansen smart-money data |
88
+ | `alpha market --sp500` | Trending S&P 500 stocks via Yahoo Finance |
89
+ | `alpha market --nsd` | Trending NASDAQ stocks via Yahoo Finance |
90
+ | `alpha lookup <id>` | Deep dive into an asset (crypto or stock ticker) |
91
+ | `alpha watch <asset>` | Live-poll asset price every 30s (Ctrl+C to stop) |
92
+
93
+ ### Paper Trading & Triggers
94
+
95
+ | Command | Description |
96
+ |------------------------------------------------------|------------------------------------------|
97
+ | `alpha positions` | View simulated portfolio & open positions |
98
+ | `alpha trigger buy <id> <condition> <target> <amt> <market>` | Set a BUY trigger |
99
+ | `alpha trigger sell <id> <condition> <target> <amt> <market>` | Set a SELL trigger |
100
+ | `alpha trigger list` | List all your triggers |
101
+ | `alpha trigger cancel <id>` | Cancel an active trigger |
102
+
103
+ **Condition types:** `PRICE_ABOVE`, `PRICE_BELOW`, `SMART_MONEY_INFLOW`, `EXCHANGE_OUTFLOW`
104
+ **Market types:** `CRYPTO`, `SP500`, `NASDAQ`
105
+
106
+ #### Example
107
+
108
+ ```bash
109
+ # Buy 10 units of SOL when price drops below $140
110
+ alpha trigger buy SOL PRICE_BELOW 140 10 CRYPTO
111
+
112
+ # Sell 5 shares of AAPL when price goes above $200
113
+ alpha trigger sell AAPL PRICE_ABOVE 200 5 SP500
114
+ ```
115
+
116
+ ---
117
+
118
+ ## Nansen API Integration
119
+
120
+ The backend integrates with **10 Nansen API endpoints** for on-chain analytics:
121
+
122
+ 1. `GET /trending/tokens` — Hot contracts & trending tokens
123
+ 2. `GET /smart-money/token-flows` — Smart money inflows/outflows
124
+ 3. `GET /wallet/:address/balances` — Token balances for a wallet
125
+ 4. `GET /token/:id/exchange-flows` — Exchange flow data
126
+ 5. `GET /token/:id/holders` — Token holder distribution
127
+ 6. `GET /smart-money/holdings` — Cross-chain smart money holdings
128
+ 7. `GET /wallet/:address/profiler` — Wallet profiler
129
+ 8. `GET /nft/indexes` — NFT market indexes
130
+ 9. `GET /entities/:name/flows` — Entity-level token flow
131
+ 10. `GET /token/:id/macro-signals` — Token God Mode macro signals
132
+
133
+ > **Note:** If `NANSEN_API_KEY` is not set in `.env`, the backend will return mock data so the CLI can be demonstrated without API access.
134
+
135
+ ---
136
+
137
+ ## Autonomous Trigger Engine
138
+
139
+ The backend runs a **cron job every 60 seconds** that:
140
+
141
+ 1. Fetches all `ACTIVE` triggers from MongoDB
142
+ 2. For each trigger, fetches the live price/flow data from Nansen or Yahoo Finance
143
+ 3. Evaluates the trigger condition (`PRICE_ABOVE`, `PRICE_BELOW`, etc.)
144
+ 4. If the condition is met, executes a **simulated paper trade**:
145
+ - **BUY:** Deducts cost from `User.simulatedBalance`, creates/updates a `Position`
146
+ - **SELL:** Adds proceeds to `User.simulatedBalance`, reduces/removes a `Position`
147
+ 5. Marks the trigger as `EXECUTED`
148
+
149
+ Each new user starts with a **$100,000 simulated paper-trading balance**.
150
+
151
+ ---
152
+
153
+ ## Tech Stack
154
+
155
+ | Component | Technology |
156
+ |---------------|------------------------------------------------|
157
+ | CLI | Node.js, Commander.js, Chalk, cli-table3 |
158
+ | Backend | Express 5, MongoDB/Mongoose, JWT |
159
+ | Auth UI | Next.js 15, Solana wallet-adapter, Tailwind CSS |
160
+ | Market Data | Nansen API, Yahoo Finance (yahoo-finance2) |
161
+ | Auth | Phantom Wallet (Ed25519 signature + tweetnacl) |
162
+ | Scheduler | node-cron |
163
+
164
+ ---
165
+
166
+ ## License
167
+
168
+ MIT
package/bin/alpha.js ADDED
@@ -0,0 +1,365 @@
1
+ #!/usr/bin/env node
2
+ const { Command } = require('commander');
3
+ const chalk = require('chalk');
4
+ const gradient = require('gradient-string');
5
+ const Table = require('cli-table3');
6
+ const { authenticateViaBrowser, getToken, clearToken } = require('../utils/auth');
7
+
8
+ const program = new Command();
9
+ const BACKEND_URL = process.env.ALPHA_BACKEND_URL || 'http://localhost:4000/api';
10
+
11
+ program
12
+ .name('alpha')
13
+ .description('Alpha CLI – Autonomous Agent Onchain Toolkit (Powered by Nansen)')
14
+ .version('1.0.0');
15
+
16
+ const checkAuth = () => {
17
+ const token = getToken();
18
+ if (!token) {
19
+ console.log(chalk.red('❌ Not authenticated. Please run `alpha login` first.'));
20
+ process.exit(1);
21
+ }
22
+ return token;
23
+ };
24
+
25
+ const authHeaders = (token) => ({ Authorization: `Bearer ${token}` });
26
+
27
+ const buildMarketTable = (items, cType) => {
28
+ const table = new Table({
29
+ head: [
30
+ chalk.bold.white('Asset'),
31
+ chalk.bold.white('Price'),
32
+ chalk.bold.white('Trend / Conviction'),
33
+ ],
34
+ colWidths: [22, 16, 32],
35
+ style: { border: ['grey'] },
36
+ });
37
+
38
+ items.forEach((asset) => {
39
+ const name = asset.name || asset.symbol;
40
+ const score = asset.trendScore ?? 0;
41
+ let colorizer = chalk.blue;
42
+ if (score >= 90) colorizer = chalk.green;
43
+ else if (score < 50) colorizer = chalk.yellow;
44
+
45
+ table.push([
46
+ colorizer(name),
47
+ chalk.white(`$${asset.price ?? '—'}`),
48
+ colorizer(`Score: ${score}`),
49
+ ]);
50
+ });
51
+
52
+ console.log(gradient.atlas(`\n─── Trending ${cType} Assets ───────────────────`));
53
+ console.log(table.toString());
54
+ };
55
+
56
+ program
57
+ .command('status')
58
+ .description('Check authentication status and backend health')
59
+ .action(async () => {
60
+ const axios = require('axios');
61
+ const token = getToken();
62
+
63
+ console.log(gradient.pastel('\n Alpha CLI – System Status\n'));
64
+
65
+ // Auth status
66
+ if (token) {
67
+ console.log(chalk.green(' ✅ Authenticated') + chalk.grey(' (JWT stored locally)'));
68
+ } else {
69
+ console.log(chalk.yellow(' ⚠️ Not authenticated') + chalk.grey(' — run `alpha login`'));
70
+ }
71
+
72
+ // Backend health
73
+ try {
74
+ const { data } = await axios.get(`http://localhost:4000/health`, { timeout: 3000 });
75
+ console.log(chalk.green(' ✅ Backend online') + chalk.grey(` (${data.timestamp})`));
76
+ } catch {
77
+ console.log(chalk.red(' ❌ Backend offline') + chalk.grey(' — start it with `npm run dev` in /backend'));
78
+ }
79
+
80
+ console.log('');
81
+ });
82
+
83
+ program
84
+ .command('login')
85
+ .description('Authenticate via Phantom Wallet through the browser')
86
+ .action(async () => {
87
+ try {
88
+ console.log(gradient.pastel.multiline('\nWelcome to Alpha CLI\nPreparing Web3 Authentication...\n'));
89
+ await authenticateViaBrowser();
90
+ console.log(chalk.green('\n✅ Successfully authenticated! Your session is stored securely.\n'));
91
+ } catch (err) {
92
+ console.error(chalk.red('\n❌ Authentication Failed:'), err.message);
93
+ }
94
+ });
95
+
96
+ program
97
+ .command('logout')
98
+ .description('Clear the stored authentication session')
99
+ .action(() => {
100
+ clearToken();
101
+ console.log(chalk.yellow('\n👋 Logged out. Your JWT has been cleared.\n'));
102
+ });
103
+
104
+ program
105
+ .command('market')
106
+ .description('Show trending assets powered by Nansen smart-money data')
107
+ .option('-c, --crypto', 'Trending crypto from Nansen (default)')
108
+ .option('-s, --sp500', 'Trending S&P 500 assets via Yahoo Finance')
109
+ .option('-n, --nsd', 'Trending NASDAQ assets via Yahoo Finance')
110
+ .action(async (options) => {
111
+ const token = checkAuth();
112
+ const axios = require('axios');
113
+
114
+ let url = `${BACKEND_URL}/market/crypto/trending`;
115
+ let cType = 'CRYPTO';
116
+
117
+ if (options.sp500) { url = `${BACKEND_URL}/market/tradfi/sp500`; cType = 'S&P 500'; }
118
+ else if (options.nsd) { url = `${BACKEND_URL}/market/tradfi/nsd`; cType = 'NASDAQ'; }
119
+
120
+ console.log(chalk.cyan(`\n🔍 Fetching ${cType} market data...`));
121
+
122
+ try {
123
+ const { data } = await axios.get(url, { headers: authHeaders(token) });
124
+ const items = data.data || data.assets || [];
125
+
126
+ if (!items.length) {
127
+ console.log(chalk.yellow('No data returned.'));
128
+ return;
129
+ }
130
+
131
+ buildMarketTable(items, cType);
132
+ } catch (err) {
133
+ console.error(chalk.red('Failed to fetch market data:'), err.response?.data?.error || err.message);
134
+ }
135
+ });
136
+
137
+ program
138
+ .command('lookup <id>')
139
+ .description('Deep dive into an asset via Nansen or Yahoo Finance')
140
+ .action(async (id) => {
141
+ const token = checkAuth();
142
+ const axios = require('axios');
143
+
144
+ console.log(chalk.cyan(`\n🔍 Fetching deep insights for ${chalk.bold(id)}...`));
145
+
146
+ try {
147
+ const { data } = await axios.get(`${BACKEND_URL}/market/lookup/${id}`, { headers: authHeaders(token) });
148
+
149
+ if (data.type === 'tradfi') {
150
+ const q = data.data;
151
+ const table = new Table({ style: { border: ['grey'] } });
152
+ table.push(
153
+ [chalk.grey('Symbol'), chalk.white(q.symbol)],
154
+ [chalk.grey('Name'), chalk.white(q.longName || q.shortName || '—')],
155
+ [chalk.grey('Price'), chalk.green(`$${q.regularMarketPrice}`)],
156
+ [chalk.grey('Change %'), (q.regularMarketChangePercent >= 0 ? chalk.green : chalk.red)(`${q.regularMarketChangePercent?.toFixed(2)}%`)],
157
+ [chalk.grey('52W High'), chalk.white(`$${q.fiftyTwoWeekHigh}`)],
158
+ [chalk.grey('52W Low'), chalk.white(`$${q.fiftyTwoWeekLow}`)],
159
+ [chalk.grey('Market Cap'), chalk.white(q.marketCap ? `$${(q.marketCap / 1e9).toFixed(2)}B` : '—')],
160
+ );
161
+ console.log(gradient.atlas(`\n─── TradFi Insight: ${id.toUpperCase()} ─────────────`));
162
+ console.log(table.toString());
163
+ } else {
164
+ console.log(gradient.atlas(`\n─── Nansen Crypto Insight: ${id.toUpperCase()} ──────`));
165
+ console.log(JSON.stringify(data.nansenInsights, null, 2));
166
+ }
167
+ } catch (err) {
168
+ console.error(chalk.red('Lookup failed:'), err.response?.data?.error || err.message);
169
+ }
170
+ });
171
+
172
+ program
173
+ .command('positions')
174
+ .description('View your simulated portfolio and open positions')
175
+ .action(async () => {
176
+ const token = checkAuth();
177
+ const axios = require('axios');
178
+
179
+ try {
180
+ const { data } = await axios.get(`${BACKEND_URL}/triggers/portfolio`, { headers: authHeaders(token) });
181
+
182
+ console.log(gradient.pastel(`\n 💰 Simulated Balance: ${chalk.bold.green('$' + data.simulatedBalance?.toLocaleString())}\n`));
183
+
184
+ if (!data.positions?.length) {
185
+ console.log(chalk.grey(' No open positions. Use `alpha trigger buy` to start paper trading.\n'));
186
+ return;
187
+ }
188
+
189
+ const table = new Table({
190
+ head: [chalk.bold.white('Asset'), chalk.bold.white('Market'), chalk.bold.white('Amount'), chalk.bold.white('Avg Buy Price')],
191
+ colWidths: [18, 12, 14, 18],
192
+ style: { border: ['grey'] },
193
+ });
194
+
195
+ data.positions.forEach((pos) => {
196
+ table.push([
197
+ chalk.cyan(pos.assetId),
198
+ chalk.white(pos.market),
199
+ chalk.white(pos.amount),
200
+ chalk.green(`$${pos.averageBuyPrice}`),
201
+ ]);
202
+ });
203
+
204
+ console.log(chalk.bold(' 📊 Open Positions:'));
205
+ console.log(table.toString());
206
+ } catch (err) {
207
+ console.error(chalk.red('Failed to fetch portfolio:'), err.response?.data?.error || err.message);
208
+ }
209
+ });
210
+
211
+ program
212
+ .command('watch <asset>')
213
+ .description('Live-poll an asset every 30s (Ctrl+C to stop)')
214
+ .option('-i, --interval <seconds>', 'Polling interval in seconds', '30')
215
+ .action(async (asset, options) => {
216
+ const token = checkAuth();
217
+ const axios = require('axios');
218
+ const intervalMs = parseInt(options.interval, 10) * 1000;
219
+
220
+ console.log(gradient.atlas(`\n👁 Watching ${chalk.bold(asset)} every ${options.interval}s — Press Ctrl+C to stop\n`));
221
+
222
+ const fetch = async () => {
223
+ const ts = new Date().toLocaleTimeString();
224
+ try {
225
+ const { data } = await axios.get(`${BACKEND_URL}/market/lookup/${asset}`, { headers: authHeaders(token) });
226
+
227
+ if (data.type === 'tradfi') {
228
+ const q = data.data;
229
+ const change = q.regularMarketChangePercent?.toFixed(2);
230
+ const arrow = change >= 0 ? '▲' : '▼';
231
+ const colorizer = change >= 0 ? chalk.green : chalk.red;
232
+ console.log(
233
+ chalk.grey(`[${ts}]`) +
234
+ chalk.white(` ${q.symbol}`) +
235
+ ` $${q.regularMarketPrice}` +
236
+ ` ` + colorizer(`${arrow} ${change}%`)
237
+ );
238
+ } else {
239
+ const flows = data.nansenInsights?.flows;
240
+ console.log(chalk.grey(`[${ts}]`), chalk.white(asset.toUpperCase()), '—', chalk.cyan('Nansen data:'), JSON.stringify(flows ?? data.nansenInsights, null, 0));
241
+ }
242
+ } catch (err) {
243
+ console.log(chalk.grey(`[${ts}]`), chalk.red('Error:'), err.response?.data?.error || err.message);
244
+ }
245
+ };
246
+
247
+ await fetch(); // Immediate first tick
248
+ setInterval(fetch, intervalMs);
249
+ });
250
+
251
+ const triggerCmd = program
252
+ .command('trigger')
253
+ .description('Manage autonomous smart-money paper-trading triggers');
254
+
255
+ // trigger buy
256
+ triggerCmd
257
+ .command('buy <id> <condition> <target> <amount> <market>')
258
+ .description('Set a BUY trigger. Example: alpha trigger buy SOL PRICE_BELOW 140 10 CRYPTO')
259
+ .action(async (id, condition, target, amount, market) => {
260
+ const token = checkAuth();
261
+ const axios = require('axios');
262
+ try {
263
+ const { data } = await axios.post(
264
+ `${BACKEND_URL}/triggers`,
265
+ { assetId: id, type: 'BUY', conditionType: condition, targetValue: Number(target), amount: Number(amount), market: market.toUpperCase() },
266
+ { headers: authHeaders(token) }
267
+ );
268
+ console.log(chalk.green(`\n✅ BUY trigger set! Agent will buy ${amount} ${id} when ${condition} reaches ${target}`));
269
+ console.log(chalk.grey(` Trigger ID: ${data.trigger._id}\n`));
270
+ } catch (err) {
271
+ console.error(chalk.red('Failed to set trigger:'), err.response?.data?.error || err.message);
272
+ }
273
+ });
274
+
275
+ // trigger sell
276
+ triggerCmd
277
+ .command('sell <id> <condition> <target> <amount> <market>')
278
+ .description('Set a SELL trigger. Example: alpha trigger sell SOL PRICE_ABOVE 200 10 CRYPTO')
279
+ .action(async (id, condition, target, amount, market) => {
280
+ const token = checkAuth();
281
+ const axios = require('axios');
282
+ try {
283
+ const { data } = await axios.post(
284
+ `${BACKEND_URL}/triggers`,
285
+ { assetId: id, type: 'SELL', conditionType: condition, targetValue: Number(target), amount: Number(amount), market: market.toUpperCase() },
286
+ { headers: authHeaders(token) }
287
+ );
288
+ console.log(chalk.green(`\n✅ SELL trigger set! Agent will sell ${amount} ${id} when ${condition} reaches ${target}`));
289
+ console.log(chalk.grey(` Trigger ID: ${data.trigger._id}\n`));
290
+ } catch (err) {
291
+ console.error(chalk.red('Failed to set trigger:'), err.response?.data?.error || err.message);
292
+ }
293
+ });
294
+
295
+ // trigger list
296
+ triggerCmd
297
+ .command('list')
298
+ .description('List all your active and past triggers')
299
+ .action(async () => {
300
+ const token = checkAuth();
301
+ const axios = require('axios');
302
+ try {
303
+ const { data } = await axios.get(`${BACKEND_URL}/triggers`, { headers: authHeaders(token) });
304
+
305
+ if (!data.triggers?.length) {
306
+ console.log(chalk.yellow('\n No triggers found. Use `alpha trigger buy` or `alpha trigger sell` to create one.\n'));
307
+ return;
308
+ }
309
+
310
+ const table = new Table({
311
+ head: [
312
+ chalk.bold.white('ID'),
313
+ chalk.bold.white('Asset'),
314
+ chalk.bold.white('Type'),
315
+ chalk.bold.white('Condition'),
316
+ chalk.bold.white('Target'),
317
+ chalk.bold.white('Amount'),
318
+ chalk.bold.white('Market'),
319
+ chalk.bold.white('Status'),
320
+ ],
321
+ colWidths: [28, 12, 8, 22, 12, 10, 10, 12],
322
+ style: { border: ['grey'] },
323
+ });
324
+
325
+ data.triggers.forEach((t) => {
326
+ const statusColor =
327
+ t.status === 'ACTIVE' ? chalk.green :
328
+ t.status === 'EXECUTED' ? chalk.blue : chalk.red;
329
+
330
+ table.push([
331
+ chalk.grey(t._id),
332
+ chalk.cyan(t.assetId),
333
+ t.type === 'BUY' ? chalk.green(t.type) : chalk.red(t.type),
334
+ chalk.white(t.conditionType),
335
+ chalk.white(t.targetValue),
336
+ chalk.white(t.amount),
337
+ chalk.white(t.market),
338
+ statusColor(t.status),
339
+ ]);
340
+ });
341
+
342
+ console.log(gradient.atlas('\n─── Triggers ──────────────────────────────'));
343
+ console.log(table.toString());
344
+ console.log('');
345
+ } catch (err) {
346
+ console.error(chalk.red('Failed to fetch triggers:'), err.response?.data?.error || err.message);
347
+ }
348
+ });
349
+
350
+ // trigger cancel
351
+ triggerCmd
352
+ .command('cancel <id>')
353
+ .description('Cancel an active trigger by its ID')
354
+ .action(async (id) => {
355
+ const token = checkAuth();
356
+ const axios = require('axios');
357
+ try {
358
+ await axios.delete(`${BACKEND_URL}/triggers/${id}`, { headers: authHeaders(token) });
359
+ console.log(chalk.yellow(`\n⛔ Trigger ${chalk.bold(id)} has been cancelled.\n`));
360
+ } catch (err) {
361
+ console.error(chalk.red('Failed to cancel trigger:'), err.response?.data?.error || err.message);
362
+ }
363
+ });
364
+
365
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "alpha-cli-toolkit",
3
+ "version": "1.0.0",
4
+ "description": "Autonomous Agent Toolkit for Crypto & TradFi market data powered by Nansen.",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "start": "node bin/alpha.js",
8
+ "dev": "node bin/alpha.js --help",
9
+ "test": "echo \"Error: no test specified\" && exit 1"
10
+ },
11
+ "bin": {
12
+ "alpha": "bin/alpha.js"
13
+ },
14
+ "files": [
15
+ "bin",
16
+ "utils",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "keywords": [
21
+ "crypto",
22
+ "nansen",
23
+ "tradfi",
24
+ "autonomous-agent",
25
+ "cli-toolkit"
26
+ ],
27
+ "author": "Linkxee-Tech",
28
+ "license": "ISC",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/Linkxee-Tech/alpha-cli.git"
32
+ },
33
+ "type": "commonjs",
34
+ "dependencies": {
35
+ "axios": "^1.14.0",
36
+ "chalk": "^4.1.2",
37
+ "cli-table3": "^0.6.5",
38
+ "commander": "^14.0.3",
39
+ "dotenv": "^17.3.1",
40
+ "gradient-string": "^2.0.2",
41
+ "inquirer": "^13.3.2",
42
+ "node-cron": "^4.2.1",
43
+ "open": "^11.0.0",
44
+ "ora": "^5.4.1"
45
+ },
46
+ "devDependencies": {
47
+ "nodemon": "^3.1.14"
48
+ }
49
+ }
package/utils/auth.js ADDED
@@ -0,0 +1,108 @@
1
+ const http = require('http');
2
+ const url = require('url');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+
7
+ // ─── Simple config store (replaces `conf` to avoid ESM issues) ───────────────
8
+ const CONFIG_DIR = path.join(os.homedir(), '.alpha-cli');
9
+ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
10
+
11
+ function readConfig() {
12
+ try {
13
+ if (!fs.existsSync(CONFIG_FILE)) return {};
14
+ return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
15
+ } catch {
16
+ return {};
17
+ }
18
+ }
19
+
20
+ function writeConfig(data) {
21
+ if (!fs.existsSync(CONFIG_DIR)) {
22
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
23
+ }
24
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2));
25
+ }
26
+
27
+ // ─── Token management ────────────────────────────────────────────────────────
28
+
29
+ function getToken() {
30
+ return readConfig().jwt || null;
31
+ }
32
+
33
+ function clearToken() {
34
+ const config = readConfig();
35
+ delete config.jwt;
36
+ writeConfig(config);
37
+ }
38
+
39
+ function setToken(token) {
40
+ const config = readConfig();
41
+ config.jwt = token;
42
+ writeConfig(config);
43
+ }
44
+
45
+ // ─── Browser-based Phantom auth flow ─────────────────────────────────────────
46
+
47
+ /**
48
+ * Spins up a local temporary server and opens the Next.js UI
49
+ * to capture the Phantom wallet authentication token.
50
+ */
51
+ async function authenticateViaBrowser() {
52
+ return new Promise(async (resolve, reject) => {
53
+ const server = http.createServer((req, res) => {
54
+ const parsedUrl = url.parse(req.url, true);
55
+
56
+ if (parsedUrl.pathname === '/') {
57
+ const token = parsedUrl.query.token;
58
+
59
+ if (token) {
60
+ setToken(token);
61
+
62
+ res.writeHead(200, { 'Content-Type': 'text/html' });
63
+ res.end(`
64
+ <html><body style="background: #0a0a1a; color: #4ade80; font-family: system-ui, sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0;">
65
+ <div style="text-align: center;">
66
+ <h1 style="font-size: 2rem; margin-bottom: 0.5rem;">✓ Authentication Complete</h1>
67
+ <p style="color: #666;">You can close this tab and return to your terminal.</p>
68
+ </div>
69
+ <script>setTimeout(() => window.close(), 3000);</script>
70
+ </body></html>
71
+ `);
72
+
73
+ server.close();
74
+ resolve(token);
75
+ } else {
76
+ res.writeHead(400, { 'Content-Type': 'text/html' });
77
+ res.end('<h1>Failed to authenticate. No token returned.</h1>');
78
+ server.close();
79
+ reject(new Error('No token returned'));
80
+ }
81
+ }
82
+ });
83
+
84
+ server.listen(0, async () => {
85
+ const port = server.address().port;
86
+ const callbackUrl = `http://localhost:${port}`;
87
+ const loginUrl = `http://localhost:3000/?callback=${encodeURIComponent(callbackUrl)}`;
88
+
89
+ // Dynamic import for `open` (ESM-only since v9)
90
+ const open = (await import('open')).default;
91
+
92
+ console.log(`Opening browser to authenticate via Phantom Wallet...`);
93
+ await open(loginUrl);
94
+ });
95
+
96
+ // Timeout after 5 minutes
97
+ setTimeout(() => {
98
+ server.close();
99
+ reject(new Error('Authentication timed out after 5 minutes'));
100
+ }, 5 * 60 * 1000);
101
+ });
102
+ }
103
+
104
+ module.exports = {
105
+ authenticateViaBrowser,
106
+ getToken,
107
+ clearToken,
108
+ };