@aiaiaichain/agent 0.1.3 → 0.1.5
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 +2 -2
- package/dist/cli.js +274 -26
- package/dist/core/ChainConfig.js +1 -1
- package/dist/core/SystemMonitor.d.ts +32 -0
- package/dist/core/SystemMonitor.js +89 -0
- package/dist/index.d.ts +17 -2
- package/dist/index.js +18 -1
- package/dist/models/ModelRegistry.js +12 -4
- package/dist/runner/AgentRunner.d.ts +2 -0
- package/dist/runner/AgentRunner.js +18 -1
- package/dist/runner/ModelClient.js +109 -48
- package/dist/session/SessionManager.d.ts +1 -0
- package/dist/session/SessionManager.js +8 -2
- package/dist/session/SessionStore.d.ts +45 -0
- package/dist/session/SessionStore.js +128 -0
- package/dist/tools/CrossTools.d.ts +52 -0
- package/dist/tools/CrossTools.js +190 -0
- package/dist/tools/MarketSentiment.js +22 -13
- package/dist/tools/NewsSentiment.js +9 -3
- package/dist/tools/PriceFeed.js +11 -4
- package/dist/tools/TechnicalAnalysis.js +2 -1
- package/dist/tools/TokenCalendar.d.ts +24 -0
- package/dist/tools/TokenCalendar.js +81 -0
- package/dist/tools/TokenSecurityScanner.d.ts +22 -0
- package/dist/tools/TokenSecurityScanner.js +102 -0
- package/dist/tools/TransactionSim.d.ts +17 -0
- package/dist/tools/TransactionSim.js +78 -0
- package/dist/tui/App.d.ts +4 -3
- package/dist/tui/App.js +371 -118
- package/dist/tui/REPL.d.ts +2 -1
- package/dist/tui/REPL.js +190 -16
- package/dist/tui/Sparkline.d.ts +21 -0
- package/dist/tui/Sparkline.js +44 -0
- package/dist/tui/StatusBar.d.ts +5 -1
- package/dist/tui/StatusBar.js +6 -4
- package/dist/tui/ThemePresets.d.ts +25 -0
- package/dist/tui/ThemePresets.js +117 -0
- package/dist/util/clipboard.d.ts +9 -0
- package/dist/util/clipboard.js +26 -0
- package/dist/util/commandSuggest.d.ts +7 -0
- package/dist/util/commandSuggest.js +44 -0
- package/dist/util/confirmation.d.ts +6 -0
- package/dist/util/confirmation.js +16 -0
- package/dist/util/errorHandler.d.ts +3 -0
- package/dist/util/errorHandler.js +28 -0
- package/dist/util/logger.d.ts +11 -0
- package/dist/util/logger.js +43 -0
- package/dist/util/processManager.d.ts +5 -0
- package/dist/util/processManager.js +39 -0
- package/dist/util/resilientFetch.d.ts +21 -0
- package/dist/util/resilientFetch.js +94 -0
- package/dist/util/responseCache.d.ts +27 -0
- package/dist/util/responseCache.js +54 -0
- package/dist/util/safeLog.d.ts +4 -5
- package/dist/util/safeLog.js +68 -30
- package/dist/util/scheduler.d.ts +14 -0
- package/dist/util/scheduler.js +75 -0
- package/dist/util/webhooks.d.ts +9 -0
- package/dist/util/webhooks.js +75 -0
- package/dist/wallet/ActionFeed.js +12 -4
- package/dist/wallet/AgentWallet.d.ts +2 -0
- package/dist/wallet/AgentWallet.js +31 -5
- package/dist/wallet/ProfitTracker.d.ts +30 -0
- package/dist/wallet/ProfitTracker.js +93 -0
- package/docs/COMMANDS.md +40 -9
- package/docs/README.md +2 -0
- package/package.json +5 -3
- package/scripts/postinstall.js +34 -0
package/dist/tui/App.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
|
-
* App — root Ink component. Multi-pane TUI with sidebar
|
|
4
|
-
* and
|
|
3
|
+
* App — root Ink component. Multi-pane TUI with sidebar, system monitor,
|
|
4
|
+
* session persistence, and full GMGN command access.
|
|
5
5
|
*/
|
|
6
6
|
import { useState, useCallback, useEffect, useRef, useMemo } from "react";
|
|
7
7
|
import { Box, Text, useApp, useInput } from "ink";
|
|
@@ -16,7 +16,6 @@ import { makeTheme, T, AIAIAI_COLORS } from "./theme.js";
|
|
|
16
16
|
import { AgentRunner } from "../runner/AgentRunner.js";
|
|
17
17
|
import { SessionManager } from "../session/SessionManager.js";
|
|
18
18
|
import { resolveModelConfig } from "../runner/ModelClient.js";
|
|
19
|
-
import { modelRegistry } from "../models/ModelRegistry.js";
|
|
20
19
|
import { priceFeed } from "../tools/PriceFeed.js";
|
|
21
20
|
import { getNewsTool, getNewsParams, newsFeed } from "../tools/NewsSentiment.js";
|
|
22
21
|
import { analyzeTAParams, getCandlesParams, getCandlesTool, fullAnalysis } from "../tools/TechnicalAnalysis.js";
|
|
@@ -25,9 +24,19 @@ import { contextStore } from "../session/ContextStore.js";
|
|
|
25
24
|
import { goalManager } from "../session/GoalManager.js";
|
|
26
25
|
import { memoryStore } from "../session/MemoryStore.js";
|
|
27
26
|
import { agentScheduler } from "../scheduler/AgentScheduler.js";
|
|
27
|
+
import { sessionStore } from "../session/SessionStore.js";
|
|
28
28
|
import { agentWallet, ACTION_WALLET, DEPOSIT_WALLET, SIGNER } from "../wallet/AgentWallet.js";
|
|
29
29
|
import { actionFeed } from "../wallet/ActionFeed.js";
|
|
30
30
|
import { gmgnTool, gmgnToolParams, gmgnHelp, gmgnStatus, gmgnMarketTool, gmgnMarketToolParams } from "../tools/GmgnIntegration.js";
|
|
31
|
+
import { watchTokenTool, watchTokenParams, removeWatchTool, removeWatchParams, listWatchTool, listWatchParams, addAlertTool, addAlertParams, checkAlertsTool, checkAlertsParams, compareTokensTool, compareTokensParams, portfolioTool, portfolioParams, getWatchList as _getWatchList, checkAlerts as _checkAlerts, } from "../tools/CrossTools.js";
|
|
32
|
+
import { systemMonitor } from "../core/SystemMonitor.js";
|
|
33
|
+
import { Sparkline, PriceHistory } from "./Sparkline.js";
|
|
34
|
+
import { launchCalendarTool, getLaunchSummary } from "../tools/TokenCalendar.js";
|
|
35
|
+
import { simulateTxTool } from "../tools/TransactionSim.js";
|
|
36
|
+
import { getPresetColors, listThemes, setTheme as setThemePreset } from "./ThemePresets.js";
|
|
37
|
+
import { copyToClipboard, clipboardSupported } from "../util/clipboard.js";
|
|
38
|
+
import { suggestCommands } from "../util/commandSuggest.js";
|
|
39
|
+
import { notify as notifyWebhook, hasWebhooks } from "../util/webhooks.js";
|
|
31
40
|
function getContextBar(session) {
|
|
32
41
|
if (!session)
|
|
33
42
|
return { pct: 0, bar: "░".repeat(20), color: AIAIAI_COLORS.dim };
|
|
@@ -95,9 +104,16 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
95
104
|
const [fees, setFees] = useState({ buyFees: 0, burnFees: 0, total: 0 });
|
|
96
105
|
const [showModelSelector, setShowModelSelector] = useState(false);
|
|
97
106
|
const [modelSelectorInitialQuery, setModelSelectorInitialQuery] = useState("");
|
|
107
|
+
const [cpu, setCpu] = useState(0);
|
|
108
|
+
const [ram, setRam] = useState(0);
|
|
109
|
+
const [uptime, setUptime] = useState("0m");
|
|
110
|
+
const [apiCalls, setApiCalls] = useState(0);
|
|
111
|
+
const [watchList, setWatchList] = useState([]);
|
|
112
|
+
const priceHistoryRef = useRef(new PriceHistory(40));
|
|
98
113
|
const runnerRef = useRef(null);
|
|
99
114
|
const sessionRef = useRef(null);
|
|
100
115
|
const sessionCtxRef = useRef(null);
|
|
116
|
+
const messagesRef = useRef([]);
|
|
101
117
|
const theme = makeTheme();
|
|
102
118
|
let modelName = "no-model";
|
|
103
119
|
try {
|
|
@@ -105,7 +121,11 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
105
121
|
}
|
|
106
122
|
catch { /* shown via banner */ }
|
|
107
123
|
const push = useCallback((msg) => {
|
|
108
|
-
setMessages(prev =>
|
|
124
|
+
setMessages(prev => {
|
|
125
|
+
const next = [...prev, { ...msg, id: nextId(), ts: Date.now() }];
|
|
126
|
+
messagesRef.current = next;
|
|
127
|
+
return next;
|
|
128
|
+
});
|
|
109
129
|
}, []);
|
|
110
130
|
const notify = useCallback((content) => { push({ role: "notify", content }); }, [push]);
|
|
111
131
|
const setStatus = useCallback((key, value) => {
|
|
@@ -120,6 +140,7 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
120
140
|
},
|
|
121
141
|
theme,
|
|
122
142
|
};
|
|
143
|
+
// ── Register ALL tools ──────────────────────────────────────────────────
|
|
123
144
|
function registerBuiltinTools() {
|
|
124
145
|
if (!modelReg)
|
|
125
146
|
return;
|
|
@@ -130,11 +151,11 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
130
151
|
if (costTracker) {
|
|
131
152
|
registry.addTool({ name: "cost_report", label: "Cost Report", description: "Show session and lifetime token usage.", parameters: costTracker.costReportParams, execute: () => costTracker.costReportTool() });
|
|
132
153
|
}
|
|
133
|
-
registry.addTool({ name: "get_aiaiai_price", label: "AIAIAI Price", description: "Get
|
|
134
|
-
registry.addTool({ name: "get_token_price", label: "Token Price", description: "Get
|
|
135
|
-
registry.addTool({ name: "get_news", label: "Get News", description: "Crypto news
|
|
136
|
-
registry.addTool({ name: "get_candles", label: "Get Candles", description: "
|
|
137
|
-
registry.addTool({ name: "analyze_ta", label: "Technical Analysis", description: "
|
|
154
|
+
registry.addTool({ name: "get_aiaiai_price", label: "AIAIAI Price", description: "Get $AIAIAI price, liquidity, MCap, volume.", parameters: priceFeed.getAiaiaiPriceParams, execute: () => priceFeed.getAiaiaiPriceTool() });
|
|
155
|
+
registry.addTool({ name: "get_token_price", label: "Token Price", description: "Get any Solana token price.", parameters: priceFeed.getTokenPriceParams, execute: (id, p) => priceFeed.getTokenPriceTool(id, p) });
|
|
156
|
+
registry.addTool({ name: "get_news", label: "Get News", description: "Crypto news with sentiment.", parameters: getNewsParams, execute: (id, p) => getNewsTool(id, p) });
|
|
157
|
+
registry.addTool({ name: "get_candles", label: "Get Candles", description: "OHLCV from Binance + TA.", parameters: getCandlesParams, execute: (id, p) => getCandlesTool(id, p) });
|
|
158
|
+
registry.addTool({ name: "analyze_ta", label: "Technical Analysis", description: "RSI, MACD, Bollinger, ATR.", parameters: analyzeTAParams, execute: async (_id, p) => {
|
|
138
159
|
const closes = p.prices;
|
|
139
160
|
const candles = closes.map((c, i) => ({ timestamp: 0, open: c, high: p.highs?.[i] ?? c, low: p.lows?.[i] ?? c, close: c, volume: p.volumes?.[i] ?? 0 }));
|
|
140
161
|
const results = fullAnalysis(candles);
|
|
@@ -146,45 +167,60 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
146
167
|
}).join("\n");
|
|
147
168
|
return { content: [{ type: "text", text: `Technical Analysis:\n${text}` }], details: { results, overall_signal: summary?.signal } };
|
|
148
169
|
} });
|
|
149
|
-
registry.addTool({ name: "get_fear_greed", label: "Fear & Greed", description: "Crypto Fear & Greed Index
|
|
150
|
-
registry.addTool({ name: "get_funding_rates", label: "Funding Rates", description: "Binance
|
|
151
|
-
registry.addTool({ name: "get_btc_mempool", label: "BTC Mempool", description: "
|
|
152
|
-
registry.addTool({ name: "get_defi_tvl", label: "DeFi TVL", description: "DeFiLlama TVL
|
|
153
|
-
registry.addTool({ name: "get_solana_stats", label: "Solana Stats", description: "Solana
|
|
170
|
+
registry.addTool({ name: "get_fear_greed", label: "Fear & Greed", description: "Crypto Fear & Greed Index.", parameters: fearGreedParams, execute: () => getFearGreedTool() });
|
|
171
|
+
registry.addTool({ name: "get_funding_rates", label: "Funding Rates", description: "Binance funding rates.", parameters: fundingRatesParams, execute: (id, p) => getFundingRatesTool(id, p) });
|
|
172
|
+
registry.addTool({ name: "get_btc_mempool", label: "BTC Mempool", description: "BTC mempool stats.", parameters: btcMempoolParams, execute: () => getBtcMempoolTool() });
|
|
173
|
+
registry.addTool({ name: "get_defi_tvl", label: "DeFi TVL", description: "DeFiLlama TVL.", parameters: defiTvlParams, execute: (id, p) => getDefiTvlTool(id, p) });
|
|
174
|
+
registry.addTool({ name: "get_solana_stats", label: "Solana Stats", description: "Solana epoch, slot, SOL price.", parameters: solanaStatsParams, execute: () => getSolanaStatsTool() });
|
|
175
|
+
registry.addTool({ name: "get_agent_balance", label: "Agent Balance", description: "Cold + action wallet balances.", parameters: agentWallet.getAgentBalanceParams, execute: () => agentWallet.getAgentBalanceTool() });
|
|
176
|
+
registry.addTool({ name: "get_deposit_balance", label: "Deposit Balance", description: "Deposit wallet + instructions.", parameters: agentWallet.getDepositBalanceParams, execute: () => agentWallet.getDepositBalanceTool() });
|
|
177
|
+
registry.addTool({ name: "get_actions", label: "Agent Actions", description: "Recent buy/burn actions.", parameters: actionFeed.getActionsParams, execute: (id, p) => actionFeed.getActionsTool(id, p) });
|
|
178
|
+
registry.addTool({ name: "get_fees", label: "Agent Fees", description: "Accumulated fees.", parameters: actionFeed.getFeesParams, execute: () => actionFeed.getFeesTool() });
|
|
154
179
|
registry.addTool({ name: "read_task_context", label: "Read Task Context", description: "Read saved task context.", parameters: Type.Object({ taskId: Type.String({ description: "Task ID" }) }), execute: (id, p) => contextStore.readContextTool(id, p) });
|
|
155
180
|
registry.addTool({ name: "list_tasks", label: "List Tasks", description: "List saved task contexts.", parameters: Type.Object({}), execute: () => contextStore.listTasksTool() });
|
|
156
|
-
registry.addTool({ name: "set_goal", label: "Set Goal", description: "Set a persistent
|
|
181
|
+
registry.addTool({ name: "set_goal", label: "Set Goal", description: "Set a persistent goal.", parameters: goalManager.setGoalParams, execute: (id, p) => goalManager.setGoalTool(id, p) });
|
|
157
182
|
registry.addTool({ name: "complete_goal", label: "Complete Goal", description: "Mark a goal complete.", parameters: goalManager.completeGoalParams, execute: (id, p) => goalManager.completeGoalTool(id, p) });
|
|
158
183
|
registry.addTool({ name: "list_goals", label: "List Goals", description: "List goals.", parameters: goalManager.listGoalsParams, execute: (id, p) => goalManager.listGoalsTool(id, p) });
|
|
159
184
|
registry.addTool({ name: "add_goal_note", label: "Add Goal Note", description: "Add a note to a goal.", parameters: goalManager.addGoalNoteParams, execute: (id, p) => goalManager.addGoalNoteTool(id, p) });
|
|
160
|
-
registry.addTool({ name: "schedule_task", label: "Schedule Task", description: "Schedule a recurring
|
|
185
|
+
registry.addTool({ name: "schedule_task", label: "Schedule Task", description: "Schedule a recurring task.", parameters: agentScheduler.addTaskParams, execute: (id, p) => agentScheduler.addTaskTool(id, p) });
|
|
161
186
|
registry.addTool({ name: "list_schedule", label: "List Schedule", description: "List scheduled tasks.", parameters: agentScheduler.listTasksParams, execute: (id, p) => agentScheduler.listTasksTool(id, p) });
|
|
162
187
|
registry.addTool({ name: "remove_schedule", label: "Remove Schedule", description: "Remove a scheduled task.", parameters: agentScheduler.removeTaskParams, execute: (id, p) => agentScheduler.removeTaskTool(id, p) });
|
|
163
188
|
// GMGN tools
|
|
164
|
-
registry.addTool({ name: "gmgn", label: "GMGN Token Research", description: "
|
|
165
|
-
registry.addTool({ name: "gmgn_market", label: "GMGN Market Data", description: "Market data
|
|
166
|
-
//
|
|
167
|
-
registry.addTool({ name: "
|
|
168
|
-
registry.addTool({ name: "
|
|
169
|
-
registry.addTool({ name: "
|
|
170
|
-
registry.addTool({ name: "
|
|
189
|
+
registry.addTool({ name: "gmgn", label: "GMGN Token Research", description: "Full GMGN access: token info, security, holders, traders, tracking, market data.", parameters: gmgnToolParams, execute: (id, p) => gmgnTool(id, p) });
|
|
190
|
+
registry.addTool({ name: "gmgn_market", label: "GMGN Market Data", description: "Market data: trending, trenches, kline, signals.", parameters: gmgnMarketToolParams, execute: (id, p) => gmgnMarketTool(id, p) });
|
|
191
|
+
// Cross tools
|
|
192
|
+
registry.addTool({ name: "watch_token", label: "Watch Token", description: "Add token to watch list.", parameters: watchTokenParams, execute: (id, p) => watchTokenTool(id, p) });
|
|
193
|
+
registry.addTool({ name: "unwatch_token", label: "Unwatch Token", description: "Remove token from watch list.", parameters: removeWatchParams, execute: (id, p) => removeWatchTool(id, p) });
|
|
194
|
+
registry.addTool({ name: "watch_list", label: "Watch List", description: "Show watched tokens.", parameters: listWatchParams, execute: () => listWatchTool() });
|
|
195
|
+
registry.addTool({ name: "add_alert", label: "Price Alert", description: "Set a price alert.", parameters: addAlertParams, execute: (id, p) => addAlertTool(id, p) });
|
|
196
|
+
registry.addTool({ name: "check_alerts", label: "Check Alerts", description: "List active alerts.", parameters: checkAlertsParams, execute: () => checkAlertsTool() });
|
|
197
|
+
registry.addTool({ name: "compare_tokens", label: "Compare Tokens", description: "Side-by-side token comparison.", parameters: compareTokensParams, execute: (id, p) => compareTokensTool(id, p) });
|
|
198
|
+
registry.addTool({ name: "portfolio", label: "Portfolio", description: "Wallet portfolio analysis.", parameters: portfolioParams, execute: (id, p) => portfolioTool(id, p) });
|
|
171
199
|
}
|
|
172
|
-
|
|
173
|
-
useEffect(() => {
|
|
174
|
-
onModelSelectorReady?.((initialQuery) => {
|
|
175
|
-
setModelSelectorInitialQuery(initialQuery ?? "");
|
|
176
|
-
setShowModelSelector(true);
|
|
177
|
-
});
|
|
178
|
-
}, [onModelSelectorReady]);
|
|
200
|
+
// ── Main effect: setup + intervals ──────────────────────────────────────
|
|
179
201
|
useEffect(() => {
|
|
180
202
|
registerBuiltinTools();
|
|
181
|
-
// Initial price fetch
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
203
|
+
// Initial price fetch with retry
|
|
204
|
+
const fetchPriceWithRetry = async (retries = 3, delay = 2000) => {
|
|
205
|
+
for (let i = 0; i < retries; i++) {
|
|
206
|
+
try {
|
|
207
|
+
const p = await priceFeed.getAiaiaiPrice();
|
|
208
|
+
setAiaiPrice({ priceUsd: p.priceUsd, change: p.priceChange24h, mcap: p.marketCap, liq: p.liquidityUsd });
|
|
209
|
+
if (p.priceUsd)
|
|
210
|
+
setPriceBadge(`$${parseFloat(p.priceUsd).toFixed(6)}`);
|
|
211
|
+
actionFeed.setPrice(parseFloat(p.priceUsd || "0.0004"));
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
catch (e) {
|
|
215
|
+
if (i === retries - 1)
|
|
216
|
+
throw e;
|
|
217
|
+
await new Promise(r => setTimeout(r, delay * (i + 1)));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
fetchPriceWithRetry().catch(() => {
|
|
222
|
+
console.error('Initial price fetch failed after retries');
|
|
223
|
+
});
|
|
188
224
|
// Initial wallet fetches
|
|
189
225
|
agentWallet.getAll().then(w => {
|
|
190
226
|
setColdBalance({ sol: w.cold.sol, aiaiai: w.cold.aiaiai, usdc: w.cold.usdc });
|
|
@@ -201,8 +237,11 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
201
237
|
setSidebarNews(report.items.slice(0, 6).map(i => ({ title: i.title, sentiment: i.sentiment ?? 0, source: i.source })));
|
|
202
238
|
setNewsBadge(newsFeed.statusBadge());
|
|
203
239
|
}).catch(() => { });
|
|
240
|
+
// Load watch list
|
|
241
|
+
setWatchList(_getWatchList());
|
|
242
|
+
// Scheduler
|
|
204
243
|
agentScheduler.start((task) => {
|
|
205
|
-
push({ role: "system", content: T.muted(`⏰ Scheduled
|
|
244
|
+
push({ role: "system", content: T.muted(`⏰ Scheduled: "${task.name}"`) });
|
|
206
245
|
if (!disabled && runnerRef.current) {
|
|
207
246
|
setDisabled(true);
|
|
208
247
|
setStreaming("");
|
|
@@ -214,9 +253,20 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
214
253
|
const priceInterval = setInterval(() => {
|
|
215
254
|
priceFeed.getAiaiaiPrice().then(p => {
|
|
216
255
|
setAiaiPrice({ priceUsd: p.priceUsd, change: p.priceChange24h, mcap: p.marketCap, liq: p.liquidityUsd });
|
|
217
|
-
if (p.priceUsd)
|
|
256
|
+
if (p.priceUsd) {
|
|
218
257
|
setPriceBadge(`$${parseFloat(p.priceUsd).toFixed(6)}`);
|
|
258
|
+
priceHistoryRef.current.push(parseFloat(p.priceUsd));
|
|
259
|
+
}
|
|
219
260
|
actionFeed.setPrice(parseFloat(p.priceUsd || "0.0004"));
|
|
261
|
+
systemMonitor.trackApiCall();
|
|
262
|
+
// Check alerts
|
|
263
|
+
if (p.priceUsd) {
|
|
264
|
+
const triggered = _checkAlerts(parseFloat(p.priceUsd), "AIAIAI");
|
|
265
|
+
for (const alert of triggered) {
|
|
266
|
+
notify(`🔔 Alert triggered: ${alert.type} $${alert.price} — $AIAIAI is now $${p.priceUsd}`);
|
|
267
|
+
notifyWebhook(`🔔 **Alert**: ${alert.type} $${alert.price} — $AIAIAI now $${p.priceUsd}`).catch(() => { });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
220
270
|
}).catch(() => { });
|
|
221
271
|
if (costTracker)
|
|
222
272
|
setCostBadge(costTracker.statusLine());
|
|
@@ -240,7 +290,21 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
240
290
|
setNewsBadge(newsFeed.statusBadge());
|
|
241
291
|
}).catch(() => { });
|
|
242
292
|
}, 300_000);
|
|
243
|
-
|
|
293
|
+
// System monitor interval (10s)
|
|
294
|
+
const sysInterval = setInterval(() => {
|
|
295
|
+
const stats = systemMonitor.getStats();
|
|
296
|
+
setCpu(stats.cpuPercent);
|
|
297
|
+
setRam(stats.ramPercent);
|
|
298
|
+
setUptime(stats.uptime);
|
|
299
|
+
setApiCalls(systemMonitor.resetApiCounter());
|
|
300
|
+
}, 10_000);
|
|
301
|
+
// Session auto-save (30s) — use ref to avoid stale closure
|
|
302
|
+
sessionStore.startAutoSave(() => messagesRef.current);
|
|
303
|
+
sessionStore.setCurrentId(sessionId);
|
|
304
|
+
const saveInterval = setInterval(() => {
|
|
305
|
+
if (messagesRef.current.length > 0)
|
|
306
|
+
sessionStore.save(messagesRef.current);
|
|
307
|
+
}, 30_000);
|
|
244
308
|
const session = new SessionManager();
|
|
245
309
|
session.setSystemPrompt(registry.getSystemPrompt() || systemPrompt || "You are AIAIAI Chain Agent.");
|
|
246
310
|
sessionRef.current = session;
|
|
@@ -304,9 +368,13 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
304
368
|
if (sessionCtxRef.current)
|
|
305
369
|
registry.fireHook("session_end", sessionCtxRef.current).catch(() => { });
|
|
306
370
|
agentScheduler.stop();
|
|
371
|
+
sessionStore.stopAutoSave();
|
|
372
|
+
if (messages.length > 0)
|
|
373
|
+
sessionStore.save(messages);
|
|
307
374
|
clearInterval(priceInterval);
|
|
308
375
|
clearInterval(walletInterval);
|
|
309
376
|
clearInterval(newsInterval);
|
|
377
|
+
clearInterval(sysInterval);
|
|
310
378
|
clearInterval(saveInterval);
|
|
311
379
|
costTracker?.saveLifetime();
|
|
312
380
|
};
|
|
@@ -314,10 +382,13 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
314
382
|
useInput((_input, key) => {
|
|
315
383
|
if (key.ctrl && _input === "c") {
|
|
316
384
|
push({ role: "system", content: T.muted("Goodbye 🤖") });
|
|
385
|
+
if (messages.length > 0)
|
|
386
|
+
sessionStore.save(messages);
|
|
317
387
|
costTracker?.saveLifetime();
|
|
318
388
|
setTimeout(exit, 200);
|
|
319
389
|
}
|
|
320
390
|
});
|
|
391
|
+
// ── Submit handler ─────────────────────────────────────────────────────
|
|
321
392
|
const handleSubmit = useCallback(async (raw) => {
|
|
322
393
|
const input = raw.trim();
|
|
323
394
|
if (!input)
|
|
@@ -326,16 +397,157 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
326
397
|
const [cmd, ...rest] = input.slice(1).split(" ");
|
|
327
398
|
const args = rest.join(" ");
|
|
328
399
|
if (cmd === "exit" || cmd === "quit") {
|
|
329
|
-
|
|
400
|
+
if (messages.length > 0)
|
|
401
|
+
sessionStore.save(messages);
|
|
330
402
|
costTracker?.saveLifetime();
|
|
403
|
+
push({ role: "system", content: T.muted("Goodbye 🤖") });
|
|
331
404
|
setTimeout(exit, 200);
|
|
332
405
|
return;
|
|
333
406
|
}
|
|
334
407
|
if (cmd === "help") {
|
|
335
|
-
|
|
336
|
-
|
|
408
|
+
notify("Commands:\n/token info|security|holders|traders\n/track kol|smartmoney|follow-wallet\n/market kline|trending|trenches|signal\n/watch /unwatch /watchlist /alerts /alert /compare /portfolio\n/keys /wallet /deposit /burn /actions /fees\n/models /cost /goals /schedule /update /diff\n/sessions /resume /memory /clear /exit\n/launches /theme /copy /simulate /explain /webhook /clipboard\n\nTab to auto-complete, ↑↓ for history, Shift+Enter for multi-line");
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
// ── GMGN direct commands ──────────────────────────────────────────
|
|
412
|
+
if (cmd === "token") {
|
|
413
|
+
// /token info --chain sol --address <addr>
|
|
414
|
+
const parts = args.split(" ");
|
|
415
|
+
const subcmd = parts[0];
|
|
416
|
+
if (!subcmd || subcmd === "help") {
|
|
417
|
+
notify("Token commands:\n/token info --chain <sol|bsc|base|eth> --address <addr>\n/token security --chain <chain> --address <addr>\n/token holders --chain <chain> --address <addr>\n/token traders --chain <chain> --address <addr>");
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const chain = parts[parts.indexOf("--chain") + 1] || "sol";
|
|
421
|
+
const address = parts[parts.indexOf("--address") + 1];
|
|
422
|
+
if (!address && subcmd !== "help") {
|
|
423
|
+
notify(T.error("--address is required"));
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
const result = await gmgnTool("", { subcommand: subcmd, chain, address });
|
|
427
|
+
notify(result.content[0].text);
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
if (cmd === "track") {
|
|
431
|
+
// /track kol --chain sol
|
|
432
|
+
const parts = args.split(" ");
|
|
433
|
+
const subcmd = parts[0];
|
|
434
|
+
if (!subcmd || subcmd === "help") {
|
|
435
|
+
notify("Track commands:\n/track kol --chain <sol|bsc|base|eth>\n/track smartmoney --chain <chain>\n/track follow-wallet --chain <chain>");
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
const chain = parts[parts.indexOf("--chain") + 1] || "sol";
|
|
439
|
+
const result = await gmgnTool("", { subcommand: `track ${subcmd}`, chain, limit: 10 });
|
|
440
|
+
notify(result.content[0].text);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
if (cmd === "market") {
|
|
444
|
+
// /market kline --chain sol --address <addr> --resolution 1h
|
|
445
|
+
const parts = args.split(" ");
|
|
446
|
+
const subcmd = parts[0];
|
|
447
|
+
if (!subcmd || subcmd === "help") {
|
|
448
|
+
notify("Market commands:\n/market kline --chain <chain> --address <addr> --resolution <1h|4h|1d>\n/market trending --chain <chain> --interval <1h|6h|24h>\n/market trenches --chain <chain> --type <new_creation|near_completion|completed>\n/market signal --chain <sol|bsc>");
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
const chain = parts[parts.indexOf("--chain") + 1] || "sol";
|
|
452
|
+
const address = parts[parts.indexOf("--address") + 1];
|
|
453
|
+
const resolution = parts[parts.indexOf("--resolution") + 1];
|
|
454
|
+
const interval = parts[parts.indexOf("--interval") + 1];
|
|
455
|
+
const trenchesType = parts[parts.indexOf("--type") + 1];
|
|
456
|
+
const toolParams = { subcommand: `market ${subcmd}`, chain };
|
|
457
|
+
if (address)
|
|
458
|
+
toolParams.address = address;
|
|
459
|
+
if (resolution)
|
|
460
|
+
toolParams.resolution = resolution;
|
|
461
|
+
if (interval)
|
|
462
|
+
toolParams.interval = interval;
|
|
463
|
+
if (trenchesType)
|
|
464
|
+
toolParams.type = trenchesType;
|
|
465
|
+
const result = await gmgnTool("", toolParams);
|
|
466
|
+
notify(result.content[0].text);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
// ── Cross tools ────────────────────────────────────────────────────
|
|
470
|
+
if (cmd === "watch") {
|
|
471
|
+
const result = await watchTokenTool("", { address: args || "AVPJS61gZmWKtaEpb7qYPKo8Fk2xQUsayYQxPiPMpump" });
|
|
472
|
+
setWatchList(_getWatchList());
|
|
473
|
+
notify(result.content[0].text);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
if (cmd === "unwatch") {
|
|
477
|
+
const result = await removeWatchTool("", { address: args });
|
|
478
|
+
setWatchList(_getWatchList());
|
|
479
|
+
notify(result.content[0].text);
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if (cmd === "watchlist") {
|
|
483
|
+
const result = await listWatchTool();
|
|
484
|
+
notify(result.content[0].text);
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
if (cmd === "alerts") {
|
|
488
|
+
const result = await checkAlertsTool();
|
|
489
|
+
notify(result.content[0].text);
|
|
337
490
|
return;
|
|
338
491
|
}
|
|
492
|
+
if (cmd === "alert") {
|
|
493
|
+
// /alert above 0.001 AIAIAI
|
|
494
|
+
const parts = args.split(" ");
|
|
495
|
+
const type = parts[0];
|
|
496
|
+
const price = parseFloat(parts[1]);
|
|
497
|
+
if (!type || isNaN(price)) {
|
|
498
|
+
notify(T.error("Usage: /alert above|below <price>"));
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
const result = await addAlertTool("", { token: "AIAIAI", type, price });
|
|
502
|
+
notify(result.content[0].text);
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
if (cmd === "compare") {
|
|
506
|
+
// /compare <addr1> <addr2>
|
|
507
|
+
const parts = args.split(" ");
|
|
508
|
+
if (parts.length < 2) {
|
|
509
|
+
notify(T.error("Usage: /compare <address1> <address2>"));
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
const result = await compareTokensTool("", { address1: parts[0], address2: parts[1] });
|
|
513
|
+
notify(result.content[0].text);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
if (cmd === "portfolio") {
|
|
517
|
+
const addr = args || "A11iZoqEt6hU7HyggqC67ee4AtYmaJjwKCvJLerJRV2J";
|
|
518
|
+
const result = await portfolioTool("", { address: addr });
|
|
519
|
+
notify(result.content[0].text);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
// ── Session management ─────────────────────────────────────────────
|
|
523
|
+
if (cmd === "sessions") {
|
|
524
|
+
const sessions = sessionStore.list();
|
|
525
|
+
if (sessions.length === 0) {
|
|
526
|
+
notify("No saved sessions.");
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
const lines = sessions.slice(0, 10).map((s, i) => ` ${i + 1}. ${s.title} (${s.messageCount} msgs, ${new Date(s.updatedAt).toLocaleDateString()})`);
|
|
530
|
+
notify(`Recent Sessions:\n${lines.join("\n")}\n\nUse /resume <number> to resume.`);
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
if (cmd === "resume") {
|
|
534
|
+
const sessions = sessionStore.list();
|
|
535
|
+
const idx = parseInt(args) - 1;
|
|
536
|
+
if (isNaN(idx) || idx < 0 || idx >= sessions.length) {
|
|
537
|
+
notify(T.error("Invalid session number."));
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
const session = sessionStore.load(sessions[idx].id);
|
|
541
|
+
if (!session) {
|
|
542
|
+
notify(T.error("Session not found."));
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
setMessages(session.messages);
|
|
546
|
+
sessionStore.setCurrentId(session.id);
|
|
547
|
+
notify(`Resumed: ${session.title} (${session.messages.length} messages)`);
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
// ── Standard commands ───────────────────────────────────────────────
|
|
339
551
|
if (cmd === "price") {
|
|
340
552
|
const result = await priceFeed.getAiaiaiPriceTool();
|
|
341
553
|
notify(result.content[0].text);
|
|
@@ -384,20 +596,7 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
384
596
|
return;
|
|
385
597
|
}
|
|
386
598
|
if (cmd === "burn") {
|
|
387
|
-
notify([
|
|
388
|
-
"🔥 Burn $AIAIAI",
|
|
389
|
-
"",
|
|
390
|
-
`Deposit wallet: ${DEPOSIT_WALLET}`,
|
|
391
|
-
`Send SOL or USDC here to fuel agent burns.`,
|
|
392
|
-
"",
|
|
393
|
-
`The agent detects deposits and automatically`,
|
|
394
|
-
`sends funds to the action wallet to burn tokens.`,
|
|
395
|
-
"",
|
|
396
|
-
`Action wallet: ${ACTION_WALLET}`,
|
|
397
|
-
`Signer: ${SIGNER}`,
|
|
398
|
-
"",
|
|
399
|
-
`Burn destination: tokens are removed from circulation.`,
|
|
400
|
-
].join("\n"));
|
|
599
|
+
notify(["🔥 Burn $AIAIAI", "", `Deposit: ${DEPOSIT_WALLET}`, "Send SOL or USDC here.", "", `Action: ${ACTION_WALLET}`, `Signer: ${SIGNER}`].join("\n"));
|
|
401
600
|
return;
|
|
402
601
|
}
|
|
403
602
|
if (cmd === "actions" || cmd === "activity") {
|
|
@@ -410,6 +609,10 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
410
609
|
notify(result.content[0].text);
|
|
411
610
|
return;
|
|
412
611
|
}
|
|
612
|
+
if (cmd === "keys" || cmd === "providers") {
|
|
613
|
+
notify("Switch to CLI for key management:\n aiaiai keys");
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
413
616
|
if (cmd === "goals" || cmd === "goal") {
|
|
414
617
|
const subCmd = args.trim().split(" ")[0];
|
|
415
618
|
if (subCmd === "add") {
|
|
@@ -441,29 +644,23 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
441
644
|
notify(result.content[0].text);
|
|
442
645
|
return;
|
|
443
646
|
}
|
|
444
|
-
if (cmd === "
|
|
445
|
-
|
|
446
|
-
if (pending) {
|
|
447
|
-
pending(true);
|
|
448
|
-
push({ role: "system", content: T.success("✅ Tool approved.") });
|
|
449
|
-
}
|
|
450
|
-
else
|
|
451
|
-
notify(T.error("No pending approval."));
|
|
647
|
+
if (cmd === "update" || cmd === "upgrade") {
|
|
648
|
+
notify("Switch to CLI:\n aiaiai update");
|
|
452
649
|
return;
|
|
453
650
|
}
|
|
454
|
-
if (cmd === "
|
|
455
|
-
const
|
|
456
|
-
if (
|
|
457
|
-
|
|
458
|
-
|
|
651
|
+
if (cmd === "memory" && args.trim()) {
|
|
652
|
+
const results = memoryStore.search(args.trim(), 8);
|
|
653
|
+
if (results.length === 0) {
|
|
654
|
+
notify(T.muted("No memories found."));
|
|
655
|
+
return;
|
|
459
656
|
}
|
|
460
|
-
|
|
461
|
-
|
|
657
|
+
const lines = results.map(r => `[${new Date(r.ts).toLocaleDateString()} ${r.role}] ${r.content.slice(0, 120)}`);
|
|
658
|
+
notify(`Memory: "${args.trim()}"\n${lines.join("\n")}`);
|
|
462
659
|
return;
|
|
463
660
|
}
|
|
464
661
|
if (cmd === "gmgn" || cmd === "gmgnhelp") {
|
|
465
662
|
const gmgnArgs = rest.join(" ");
|
|
466
|
-
if (!gmgnArgs || gmgnArgs === "help"
|
|
663
|
+
if (!gmgnArgs || gmgnArgs === "help") {
|
|
467
664
|
notify(gmgnHelp());
|
|
468
665
|
return;
|
|
469
666
|
}
|
|
@@ -471,61 +668,116 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
471
668
|
notify(gmgnStatus());
|
|
472
669
|
return;
|
|
473
670
|
}
|
|
474
|
-
if (gmgnArgs.startsWith("setup")
|
|
475
|
-
notify("
|
|
671
|
+
if (gmgnArgs.startsWith("setup")) {
|
|
672
|
+
notify("Run in CLI: aiaiai gmgn setup");
|
|
476
673
|
return;
|
|
477
674
|
}
|
|
478
|
-
|
|
479
|
-
const result = await gmgnTool("", { subcommand: gmgnArgs.split(" ")[0], chain: "sol", ...Object.fromEntries(gmgnArgs.split(" ").slice(1).reduce((acc, val, i) => { if (i % 2 === 0)
|
|
480
|
-
acc.push([val]);
|
|
481
|
-
else
|
|
482
|
-
acc[acc.length - 1].push(val); return acc; }, []).map(([k, v]) => [k, v || true])) });
|
|
675
|
+
const result = await gmgnTool("", { subcommand: gmgnArgs.split(" ")[0], chain: "sol" });
|
|
483
676
|
notify(result.content[0].text);
|
|
484
677
|
return;
|
|
485
678
|
}
|
|
486
679
|
if (cmd === "gmgnmarket" || cmd === "market") {
|
|
487
|
-
const
|
|
488
|
-
const result = await gmgnMarketTool("", { query: query || "trending", chain: "sol" });
|
|
680
|
+
const result = await gmgnMarketTool("", { query: args || "trending", chain: "sol" });
|
|
489
681
|
notify(result.content[0].text);
|
|
490
682
|
return;
|
|
491
683
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
"Switch to CLI to check for updates:",
|
|
497
|
-
" aiaiai update",
|
|
498
|
-
"",
|
|
499
|
-
"This will check npm for the latest version",
|
|
500
|
-
"and install it if available.",
|
|
501
|
-
].join("\n"));
|
|
684
|
+
// ── New feature commands ─────────────────────────────────────────
|
|
685
|
+
if (cmd === "launches" || cmd === "calendar") {
|
|
686
|
+
const result = await launchCalendarTool("", { chain: args || "solana", limit: 15 });
|
|
687
|
+
notify(result.content[0].text);
|
|
502
688
|
return;
|
|
503
689
|
}
|
|
504
|
-
if (cmd === "
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
""
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
690
|
+
if (cmd === "diff" || cmd === "compared") {
|
|
691
|
+
const sessions = sessionStore.list();
|
|
692
|
+
if (sessions.length < 2) {
|
|
693
|
+
notify("Need at least 2 saved sessions to compare. Start a new session with /exit and restart.");
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
const latest = sessions[0];
|
|
697
|
+
const prev = sessions[1];
|
|
698
|
+
if (!latest || !prev) {
|
|
699
|
+
notify("Could not load sessions for comparison.");
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
const lines = [
|
|
703
|
+
`📊 Session Diff`,
|
|
704
|
+
`Current: ${latest.title} (${latest.messageCount} msgs)`,
|
|
705
|
+
`Previous: ${prev.title} (${prev.messageCount} msgs)`,
|
|
706
|
+
`Difference: ${latest.messageCount - prev.messageCount} messages`,
|
|
707
|
+
`Last active: ${new Date(latest.updatedAt).toLocaleString()}`,
|
|
708
|
+
];
|
|
709
|
+
notify(lines.join("\n"));
|
|
514
710
|
return;
|
|
515
711
|
}
|
|
516
|
-
if (cmd === "
|
|
517
|
-
const
|
|
518
|
-
|
|
519
|
-
|
|
712
|
+
if (cmd === "copy" && args.trim()) {
|
|
713
|
+
const ok = copyToClipboard(args.trim());
|
|
714
|
+
notify(ok ? T.success(`✓ Copied to clipboard: ${args.slice(0, 30)}…`) : T.error("Clipboard not supported in this terminal. Try: " + clipboardSupported()));
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
if (cmd === "theme") {
|
|
718
|
+
if (!args || args === "list") {
|
|
719
|
+
const current = getPresetColors();
|
|
720
|
+
notify(`🎨 Themes\n${listThemes()}\n\nApply: /theme <name>`);
|
|
721
|
+
}
|
|
722
|
+
else if (setThemePreset(args)) {
|
|
723
|
+
notify(T.success(`✓ Theme set to "${args}". Restart to apply.`));
|
|
724
|
+
}
|
|
725
|
+
else {
|
|
726
|
+
notify(T.error(`Unknown theme "${args}". Try: /theme list`));
|
|
727
|
+
}
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
if (cmd === "simulate" || cmd === "sim") {
|
|
731
|
+
if (!args.trim()) {
|
|
732
|
+
notify(T.error("Usage: /simulate <base64-tx>"));
|
|
520
733
|
return;
|
|
521
734
|
}
|
|
522
|
-
const
|
|
523
|
-
notify(
|
|
735
|
+
const result = await simulateTxTool("", { transaction: args.trim() });
|
|
736
|
+
notify(result.content[0].text);
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
if (cmd === "explain") {
|
|
740
|
+
if (!modelReg) {
|
|
741
|
+
notify(T.error("Model registry not available"));
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
const lastAssistant = [...messages].reverse().find(m => m.role === "tool");
|
|
745
|
+
if (!lastAssistant) {
|
|
746
|
+
notify("No tool result to explain. Run a tool first.");
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
push({ role: "user", content: `Explain this tool result in simple terms:\n${lastAssistant.content.slice(0, 2000)}` });
|
|
750
|
+
setDisabled(true);
|
|
751
|
+
setStreaming("");
|
|
752
|
+
try {
|
|
753
|
+
await runnerRef.current.run(`Explain this tool result simply:\n${lastAssistant.content.slice(0, 2000)}`);
|
|
754
|
+
}
|
|
755
|
+
catch (e) {
|
|
756
|
+
setDisabled(false);
|
|
757
|
+
notify(T.error(`Error: ${e.message}`));
|
|
758
|
+
}
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
if (cmd === "webhook" || cmd === "webhooks") {
|
|
762
|
+
if (hasWebhooks()) {
|
|
763
|
+
notify("✅ Webhooks configured\n\nUsage: /webhook test\nConfig: WEBHOOK_DISCORD / WEBHOOK_TELEGRAM in ~/.aiaiai/.env");
|
|
764
|
+
}
|
|
765
|
+
else {
|
|
766
|
+
notify("No webhooks configured.\nAdd WEBHOOK_DISCORD=... or WEBHOOK_TELEGRAM=... to ~/.aiaiai/.env");
|
|
767
|
+
}
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
if (cmd === "clipboard") {
|
|
771
|
+
notify(clipboardSupported());
|
|
524
772
|
return;
|
|
525
773
|
}
|
|
526
774
|
const def = registry.getCommand(cmd);
|
|
527
775
|
if (!def) {
|
|
528
|
-
|
|
776
|
+
const suggestions = suggestCommands(cmd);
|
|
777
|
+
const tip = suggestions.length > 0
|
|
778
|
+
? `\nDid you mean: ${suggestions.map(s => `/${s}`).join(", ")}?`
|
|
779
|
+
: "";
|
|
780
|
+
notify(T.error(`Unknown: /${cmd} — try /help${tip}`));
|
|
529
781
|
return;
|
|
530
782
|
}
|
|
531
783
|
try {
|
|
@@ -552,6 +804,7 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
552
804
|
notify(T.error(`Error: ${e.message}`));
|
|
553
805
|
}
|
|
554
806
|
}, [registry, exit, push, notify, uiCtx, modelReg, costTracker]);
|
|
807
|
+
// ── Model selector ─────────────────────────────────────────────────────
|
|
555
808
|
const handleModelSelect = useCallback((modelId) => {
|
|
556
809
|
setShowModelSelector(false);
|
|
557
810
|
try {
|
|
@@ -584,22 +837,22 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
584
837
|
}, [modelReg]);
|
|
585
838
|
const ctx = getContextBar(sessionRef.current);
|
|
586
839
|
const statusLine = [costBadge, newsBadge, ...Object.values(statusBadges)].filter(Boolean).join(" ") || null;
|
|
587
|
-
const changeColor = (pct) => pct > 0 ? AIAIAI_COLORS.success : pct < 0 ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
|
|
588
840
|
const SIDEBAR_W = 44;
|
|
589
841
|
const SIDEBAR_MIN = 38;
|
|
590
|
-
const
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
}
|
|
594
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusBar, { model: modelName, chain: chain, price: priceBadge, toolRunning: toolRunning, connected: true, statusLine: statusLine }), _jsxs(Box, { flexDirection: "row", flexGrow: 1, overflow: "hidden", children: [_jsxs(Box, { flexDirection: "column", width: SIDEBAR_W, minWidth: SIDEBAR_MIN, flexShrink: 0, overflow: "hidden", children: [_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.accent, paddingX: 1, children: [_jsx(Text, { bold: true, color: AIAIAI_COLORS.accent, children: "\uD83E\uDD16 $AIAIAI" }), aiaiPrice ? (_jsxs(_Fragment, { children: [_jsxs(Text, { color: changeColor(aiaiPrice.change), children: [aiaiPrice.priceUsd ? `$${parseFloat(aiaiPrice.priceUsd).toFixed(8)}` : "N/A", " ", aiaiPrice.change > 0 ? "▲" : aiaiPrice.change < 0 ? "▼" : "─", Math.abs(aiaiPrice.change).toFixed(1), "%"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["MCap: ", aiaiPrice.mcap ? `$${(aiaiPrice.mcap / 1000).toFixed(1)}k` : "N/A"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Liq: $", (aiaiPrice.liq / 1000).toFixed(1), "k"] })] })) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Loading\u2026" })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDCBC Wallets" }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Cold: ", coldBalance.sol.toFixed(2), " SOL"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [" AIAIAI: ", coldBalance.aiaiai.toLocaleString(undefined, { maximumFractionDigits: 0 })] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Action: ", actionBalance.sol.toFixed(2), " SOL"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [" AIAIAI: ", actionBalance.aiaiai.toLocaleString(undefined, { maximumFractionDigits: 0 })] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Deposit: ", depositBalance.usdc.toFixed(2), " USDC"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [" SOL: ", depositBalance.sol.toFixed(2)] })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\u26A1 Actions" }), recentActions.length > 0 ? recentActions.map((a, i) => {
|
|
842
|
+
const changeColor = (pct) => pct > 0 ? AIAIAI_COLORS.success : pct < 0 ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
|
|
843
|
+
const actionIcon = (type) => type === "buy" ? "↗" : type === "burn" ? "🔥" : "→";
|
|
844
|
+
// Single StatusBar wrapper — shared across both views to prevent duplication
|
|
845
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusBar, { model: modelName, chain: chain, price: priceBadge, toolRunning: toolRunning, connected: true, statusLine: statusLine, cpu: cpu, ram: ram, uptime: uptime, apiCalls: apiCalls }), showModelSelector ? (_jsx(ModelSelector, { models: modelList, currentModelId: process.env.DEFAULT_MODEL ?? "", onSelect: handleModelSelect, onCancel: handleModelCancel, initialQuery: modelSelectorInitialQuery })) : (_jsxs(Box, { flexDirection: "row", flexGrow: 1, overflow: "hidden", children: [_jsxs(Box, { flexDirection: "column", width: SIDEBAR_W, minWidth: SIDEBAR_MIN, flexShrink: 0, overflow: "hidden", children: [_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.accent, paddingX: 1, children: [_jsx(Text, { bold: true, color: AIAIAI_COLORS.accent, children: "\uD83E\uDD16 $AIAIAI" }), aiaiPrice ? (_jsxs(_Fragment, { children: [_jsxs(Text, { color: changeColor(aiaiPrice.change), children: [aiaiPrice.priceUsd ? `$${parseFloat(aiaiPrice.priceUsd).toFixed(8)}` : "N/A", " ", aiaiPrice.change > 0 ? "▲" : aiaiPrice.change < 0 ? "▼" : "─", Math.abs(aiaiPrice.change).toFixed(1), "%"] }), _jsx(Sparkline, { data: priceHistoryRef.current.getData(), width: 38, color: changeColor(aiaiPrice.change), label: "" }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["MCap: ", aiaiPrice.mcap ? `$${(aiaiPrice.mcap / 1000).toFixed(1)}k` : "N/A"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Liq: $", (aiaiPrice.liq / 1000).toFixed(1), "k"] })] })) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Loading\u2026" })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDCBC Wallets" }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Cold: ", coldBalance.sol.toFixed(2), " SOL"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [" AIAIAI: ", coldBalance.aiaiai.toLocaleString(undefined, { maximumFractionDigits: 0 })] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Action: ", actionBalance.sol.toFixed(2), " SOL"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [" AIAIAI: ", actionBalance.aiaiai.toLocaleString(undefined, { maximumFractionDigits: 0 })] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Deposit: ", depositBalance.usdc.toFixed(2), " USDC"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [" SOL: ", depositBalance.sol.toFixed(2)] })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\u26A1 Actions" }), recentActions.length > 0 ? recentActions.map((a, i) => {
|
|
595
846
|
const col = a.type === "buy" ? AIAIAI_COLORS.success : a.type === "burn" ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
|
|
596
847
|
const time = new Date(a.timestamp).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
597
848
|
return _jsxs(Text, { color: col, children: [actionIcon(a.type), " ", time, " ", a.type, " ", a.amount.toLocaleString()] }, i);
|
|
598
|
-
}) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Waiting
|
|
849
|
+
}) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Waiting\u2026" })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDCB5 Fees (0.5%)" }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Buy: $", fees.buyFees.toFixed(4)] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Burn: $", fees.burnFees.toFixed(4)] }), _jsxs(Text, { color: AIAIAI_COLORS.accent, bold: true, children: ["Total: $", fees.total.toFixed(4)] })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDC41\uFE0F Watch" }), watchList.length > 0 ? watchList.slice(0, 4).map((addr, i) => {
|
|
850
|
+
return _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [addr.slice(0, 8), "\u2026", addr.slice(-6)] }, i);
|
|
851
|
+
}) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "No tokens watched" })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDCF0 News" }), sidebarNews.length > 0 ? sidebarNews.slice(0, 4).map((n, i) => {
|
|
599
852
|
const sentColor = n.sentiment > 0.3 ? AIAIAI_COLORS.success : n.sentiment < -0.3 ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
|
|
600
853
|
const title = n.title.length > 30 ? n.title.slice(0, 28) + "…" : n.title;
|
|
601
854
|
return _jsx(Text, { color: sentColor, children: title }, i);
|
|
602
|
-
}) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Loading\u2026" })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "Context" }), _jsxs(Text, { color: ctx.color, children: [ctx.bar, " ", ctx.pct, "%"] })] })] }), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: _jsx(REPL, { messages: messages, streamingText: streaming, toolRunning: toolRunning, onSubmit: handleSubmit, disabled: disabled, onAbort: () => {
|
|
855
|
+
}) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Loading\u2026" })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "Context" }), _jsxs(Text, { color: ctx.color, children: [ctx.bar, " ", ctx.pct, "%"] })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDE80 Recent" }), _jsx(Text, { color: AIAIAI_COLORS.muted, children: getLaunchSummary() })] })] }), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: _jsx(REPL, { messages: messages, streamingText: streaming, toolRunning: toolRunning, onSubmit: handleSubmit, disabled: disabled, onAbort: () => {
|
|
603
856
|
runnerRef.current?.abort();
|
|
604
857
|
setDisabled(false);
|
|
605
858
|
setToolRunning(null);
|
|
@@ -609,6 +862,6 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
609
862
|
return "";
|
|
610
863
|
});
|
|
611
864
|
push({ role: "system", content: T.dim("— stream interrupted —") });
|
|
612
|
-
} }) })] })] }));
|
|
865
|
+
} }) })] }))] }));
|
|
613
866
|
}
|
|
614
867
|
//# sourceMappingURL=App.js.map
|