@aiaiaichain/agent 0.1.2 → 0.1.4
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 +259 -0
- package/dist/core/EnvLoader.d.ts +4 -1
- package/dist/core/EnvLoader.js +47 -15
- package/dist/core/SystemMonitor.d.ts +35 -0
- package/dist/core/SystemMonitor.js +115 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.js +8 -0
- package/dist/models/CostTracker.js +2 -8
- package/dist/models/ModelRegistry.d.ts +14 -39
- package/dist/models/ModelRegistry.js +100 -105
- package/dist/providers/ProviderRegistry.d.ts +16 -0
- package/dist/providers/ProviderRegistry.js +44 -0
- package/dist/session/SessionStore.d.ts +45 -0
- package/dist/session/SessionStore.js +123 -0
- package/dist/tools/CrossTools.d.ts +52 -0
- package/dist/tools/CrossTools.js +182 -0
- package/dist/tools/GmgnIntegration.d.ts +38 -0
- package/dist/tools/GmgnIntegration.js +264 -0
- package/dist/tui/App.d.ts +4 -4
- package/dist/tui/App.js +260 -71
- package/dist/tui/REPL.d.ts +2 -1
- package/dist/tui/REPL.js +189 -15
- package/dist/tui/StatusBar.d.ts +5 -1
- package/dist/tui/StatusBar.js +6 -4
- package/dist/wallet/ActionFeed.d.ts +4 -10
- package/dist/wallet/ActionFeed.js +62 -55
- package/dist/wallet/AgentWallet.d.ts +1 -0
- package/dist/wallet/AgentWallet.js +1 -0
- package/docs/AGENT.md +42 -0
- package/docs/API.md +96 -0
- package/docs/COMMANDS.md +93 -0
- package/docs/CORE.md +67 -0
- package/docs/GMGN.md +116 -0
- package/docs/PROVIDERS.md +71 -0
- package/docs/README.md +106 -0
- package/docs/TOOLS.md +81 -0
- package/package.json +6 -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";
|
|
@@ -24,8 +24,12 @@ import { contextStore } from "../session/ContextStore.js";
|
|
|
24
24
|
import { goalManager } from "../session/GoalManager.js";
|
|
25
25
|
import { memoryStore } from "../session/MemoryStore.js";
|
|
26
26
|
import { agentScheduler } from "../scheduler/AgentScheduler.js";
|
|
27
|
+
import { sessionStore } from "../session/SessionStore.js";
|
|
27
28
|
import { agentWallet, ACTION_WALLET, DEPOSIT_WALLET, SIGNER } from "../wallet/AgentWallet.js";
|
|
28
29
|
import { actionFeed } from "../wallet/ActionFeed.js";
|
|
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";
|
|
29
33
|
function getContextBar(session) {
|
|
30
34
|
if (!session)
|
|
31
35
|
return { pct: 0, bar: "░".repeat(20), color: AIAIAI_COLORS.dim };
|
|
@@ -90,9 +94,14 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
90
94
|
const [actionBalance, setActionBalance] = useState({ sol: 0, aiaiai: 0, usdc: 0 });
|
|
91
95
|
const [depositBalance, setDepositBalance] = useState({ sol: 0, aiaiai: 0, usdc: 0 });
|
|
92
96
|
const [recentActions, setRecentActions] = useState([]);
|
|
93
|
-
const [fees, setFees] = useState({ buyFees: 0, burnFees: 0,
|
|
97
|
+
const [fees, setFees] = useState({ buyFees: 0, burnFees: 0, total: 0 });
|
|
94
98
|
const [showModelSelector, setShowModelSelector] = useState(false);
|
|
95
99
|
const [modelSelectorInitialQuery, setModelSelectorInitialQuery] = useState("");
|
|
100
|
+
const [cpu, setCpu] = useState(0);
|
|
101
|
+
const [ram, setRam] = useState(0);
|
|
102
|
+
const [uptime, setUptime] = useState("0m");
|
|
103
|
+
const [apiCalls, setApiCalls] = useState(0);
|
|
104
|
+
const [watchList, setWatchList] = useState([]);
|
|
96
105
|
const runnerRef = useRef(null);
|
|
97
106
|
const sessionRef = useRef(null);
|
|
98
107
|
const sessionCtxRef = useRef(null);
|
|
@@ -118,6 +127,7 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
118
127
|
},
|
|
119
128
|
theme,
|
|
120
129
|
};
|
|
130
|
+
// ── Register ALL tools ──────────────────────────────────────────────────
|
|
121
131
|
function registerBuiltinTools() {
|
|
122
132
|
if (!modelReg)
|
|
123
133
|
return;
|
|
@@ -128,11 +138,11 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
128
138
|
if (costTracker) {
|
|
129
139
|
registry.addTool({ name: "cost_report", label: "Cost Report", description: "Show session and lifetime token usage.", parameters: costTracker.costReportParams, execute: () => costTracker.costReportTool() });
|
|
130
140
|
}
|
|
131
|
-
registry.addTool({ name: "get_aiaiai_price", label: "AIAIAI Price", description: "Get
|
|
132
|
-
registry.addTool({ name: "get_token_price", label: "Token Price", description: "Get
|
|
133
|
-
registry.addTool({ name: "get_news", label: "Get News", description: "Crypto news
|
|
134
|
-
registry.addTool({ name: "get_candles", label: "Get Candles", description: "
|
|
135
|
-
registry.addTool({ name: "analyze_ta", label: "Technical Analysis", description: "
|
|
141
|
+
registry.addTool({ name: "get_aiaiai_price", label: "AIAIAI Price", description: "Get $AIAIAI price, liquidity, MCap, volume.", parameters: priceFeed.getAiaiaiPriceParams, execute: () => priceFeed.getAiaiaiPriceTool() });
|
|
142
|
+
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) });
|
|
143
|
+
registry.addTool({ name: "get_news", label: "Get News", description: "Crypto news with sentiment.", parameters: getNewsParams, execute: (id, p) => getNewsTool(id, p) });
|
|
144
|
+
registry.addTool({ name: "get_candles", label: "Get Candles", description: "OHLCV from Binance + TA.", parameters: getCandlesParams, execute: (id, p) => getCandlesTool(id, p) });
|
|
145
|
+
registry.addTool({ name: "analyze_ta", label: "Technical Analysis", description: "RSI, MACD, Bollinger, ATR.", parameters: analyzeTAParams, execute: async (_id, p) => {
|
|
136
146
|
const closes = p.prices;
|
|
137
147
|
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 }));
|
|
138
148
|
const results = fullAnalysis(candles);
|
|
@@ -144,33 +154,37 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
144
154
|
}).join("\n");
|
|
145
155
|
return { content: [{ type: "text", text: `Technical Analysis:\n${text}` }], details: { results, overall_signal: summary?.signal } };
|
|
146
156
|
} });
|
|
147
|
-
registry.addTool({ name: "get_fear_greed", label: "Fear & Greed", description: "Crypto Fear & Greed Index
|
|
148
|
-
registry.addTool({ name: "get_funding_rates", label: "Funding Rates", description: "Binance
|
|
149
|
-
registry.addTool({ name: "get_btc_mempool", label: "BTC Mempool", description: "
|
|
150
|
-
registry.addTool({ name: "get_defi_tvl", label: "DeFi TVL", description: "DeFiLlama TVL
|
|
151
|
-
registry.addTool({ name: "get_solana_stats", label: "Solana Stats", description: "Solana
|
|
157
|
+
registry.addTool({ name: "get_fear_greed", label: "Fear & Greed", description: "Crypto Fear & Greed Index.", parameters: fearGreedParams, execute: () => getFearGreedTool() });
|
|
158
|
+
registry.addTool({ name: "get_funding_rates", label: "Funding Rates", description: "Binance funding rates.", parameters: fundingRatesParams, execute: (id, p) => getFundingRatesTool(id, p) });
|
|
159
|
+
registry.addTool({ name: "get_btc_mempool", label: "BTC Mempool", description: "BTC mempool stats.", parameters: btcMempoolParams, execute: () => getBtcMempoolTool() });
|
|
160
|
+
registry.addTool({ name: "get_defi_tvl", label: "DeFi TVL", description: "DeFiLlama TVL.", parameters: defiTvlParams, execute: (id, p) => getDefiTvlTool(id, p) });
|
|
161
|
+
registry.addTool({ name: "get_solana_stats", label: "Solana Stats", description: "Solana epoch, slot, SOL price.", parameters: solanaStatsParams, execute: () => getSolanaStatsTool() });
|
|
162
|
+
registry.addTool({ name: "get_agent_balance", label: "Agent Balance", description: "Cold + action wallet balances.", parameters: agentWallet.getAgentBalanceParams, execute: () => agentWallet.getAgentBalanceTool() });
|
|
163
|
+
registry.addTool({ name: "get_deposit_balance", label: "Deposit Balance", description: "Deposit wallet + instructions.", parameters: agentWallet.getDepositBalanceParams, execute: () => agentWallet.getDepositBalanceTool() });
|
|
164
|
+
registry.addTool({ name: "get_actions", label: "Agent Actions", description: "Recent buy/burn actions.", parameters: actionFeed.getActionsParams, execute: (id, p) => actionFeed.getActionsTool(id, p) });
|
|
165
|
+
registry.addTool({ name: "get_fees", label: "Agent Fees", description: "Accumulated fees.", parameters: actionFeed.getFeesParams, execute: () => actionFeed.getFeesTool() });
|
|
152
166
|
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) });
|
|
153
167
|
registry.addTool({ name: "list_tasks", label: "List Tasks", description: "List saved task contexts.", parameters: Type.Object({}), execute: () => contextStore.listTasksTool() });
|
|
154
|
-
registry.addTool({ name: "set_goal", label: "Set Goal", description: "Set a persistent
|
|
168
|
+
registry.addTool({ name: "set_goal", label: "Set Goal", description: "Set a persistent goal.", parameters: goalManager.setGoalParams, execute: (id, p) => goalManager.setGoalTool(id, p) });
|
|
155
169
|
registry.addTool({ name: "complete_goal", label: "Complete Goal", description: "Mark a goal complete.", parameters: goalManager.completeGoalParams, execute: (id, p) => goalManager.completeGoalTool(id, p) });
|
|
156
170
|
registry.addTool({ name: "list_goals", label: "List Goals", description: "List goals.", parameters: goalManager.listGoalsParams, execute: (id, p) => goalManager.listGoalsTool(id, p) });
|
|
157
171
|
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) });
|
|
158
|
-
registry.addTool({ name: "schedule_task", label: "Schedule Task", description: "Schedule a recurring
|
|
172
|
+
registry.addTool({ name: "schedule_task", label: "Schedule Task", description: "Schedule a recurring task.", parameters: agentScheduler.addTaskParams, execute: (id, p) => agentScheduler.addTaskTool(id, p) });
|
|
159
173
|
registry.addTool({ name: "list_schedule", label: "List Schedule", description: "List scheduled tasks.", parameters: agentScheduler.listTasksParams, execute: (id, p) => agentScheduler.listTasksTool(id, p) });
|
|
160
174
|
registry.addTool({ name: "remove_schedule", label: "Remove Schedule", description: "Remove a scheduled task.", parameters: agentScheduler.removeTaskParams, execute: (id, p) => agentScheduler.removeTaskTool(id, p) });
|
|
161
|
-
//
|
|
162
|
-
registry.addTool({ name: "
|
|
163
|
-
registry.addTool({ name: "
|
|
164
|
-
|
|
165
|
-
registry.addTool({ name: "
|
|
175
|
+
// GMGN tools
|
|
176
|
+
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) });
|
|
177
|
+
registry.addTool({ name: "gmgn_market", label: "GMGN Market Data", description: "Market data: trending, trenches, kline, signals.", parameters: gmgnMarketToolParams, execute: (id, p) => gmgnMarketTool(id, p) });
|
|
178
|
+
// Cross tools
|
|
179
|
+
registry.addTool({ name: "watch_token", label: "Watch Token", description: "Add token to watch list.", parameters: watchTokenParams, execute: (id, p) => watchTokenTool(id, p) });
|
|
180
|
+
registry.addTool({ name: "unwatch_token", label: "Unwatch Token", description: "Remove token from watch list.", parameters: removeWatchParams, execute: (id, p) => removeWatchTool(id, p) });
|
|
181
|
+
registry.addTool({ name: "watch_list", label: "Watch List", description: "Show watched tokens.", parameters: listWatchParams, execute: () => listWatchTool() });
|
|
182
|
+
registry.addTool({ name: "add_alert", label: "Price Alert", description: "Set a price alert.", parameters: addAlertParams, execute: (id, p) => addAlertTool(id, p) });
|
|
183
|
+
registry.addTool({ name: "check_alerts", label: "Check Alerts", description: "List active alerts.", parameters: checkAlertsParams, execute: () => checkAlertsTool() });
|
|
184
|
+
registry.addTool({ name: "compare_tokens", label: "Compare Tokens", description: "Side-by-side token comparison.", parameters: compareTokensParams, execute: (id, p) => compareTokensTool(id, p) });
|
|
185
|
+
registry.addTool({ name: "portfolio", label: "Portfolio", description: "Wallet portfolio analysis.", parameters: portfolioParams, execute: (id, p) => portfolioTool(id, p) });
|
|
166
186
|
}
|
|
167
|
-
|
|
168
|
-
useEffect(() => {
|
|
169
|
-
onModelSelectorReady?.((initialQuery) => {
|
|
170
|
-
setModelSelectorInitialQuery(initialQuery ?? "");
|
|
171
|
-
setShowModelSelector(true);
|
|
172
|
-
});
|
|
173
|
-
}, [onModelSelectorReady]);
|
|
187
|
+
// ── Main effect: setup + intervals ──────────────────────────────────────
|
|
174
188
|
useEffect(() => {
|
|
175
189
|
registerBuiltinTools();
|
|
176
190
|
// Initial price fetch
|
|
@@ -196,8 +210,11 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
196
210
|
setSidebarNews(report.items.slice(0, 6).map(i => ({ title: i.title, sentiment: i.sentiment ?? 0, source: i.source })));
|
|
197
211
|
setNewsBadge(newsFeed.statusBadge());
|
|
198
212
|
}).catch(() => { });
|
|
213
|
+
// Load watch list
|
|
214
|
+
setWatchList(_getWatchList());
|
|
215
|
+
// Scheduler
|
|
199
216
|
agentScheduler.start((task) => {
|
|
200
|
-
push({ role: "system", content: T.muted(`⏰ Scheduled
|
|
217
|
+
push({ role: "system", content: T.muted(`⏰ Scheduled: "${task.name}"`) });
|
|
201
218
|
if (!disabled && runnerRef.current) {
|
|
202
219
|
setDisabled(true);
|
|
203
220
|
setStreaming("");
|
|
@@ -212,6 +229,14 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
212
229
|
if (p.priceUsd)
|
|
213
230
|
setPriceBadge(`$${parseFloat(p.priceUsd).toFixed(6)}`);
|
|
214
231
|
actionFeed.setPrice(parseFloat(p.priceUsd || "0.0004"));
|
|
232
|
+
systemMonitor.trackApiCall();
|
|
233
|
+
// Check alerts
|
|
234
|
+
if (p.priceUsd) {
|
|
235
|
+
const triggered = _checkAlerts(parseFloat(p.priceUsd), "AIAIAI");
|
|
236
|
+
for (const alert of triggered) {
|
|
237
|
+
notify(`🔔 Alert triggered: ${alert.type} $${alert.price} — $AIAIAI is now $${p.priceUsd}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
215
240
|
}).catch(() => { });
|
|
216
241
|
if (costTracker)
|
|
217
242
|
setCostBadge(costTracker.statusLine());
|
|
@@ -235,7 +260,21 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
235
260
|
setNewsBadge(newsFeed.statusBadge());
|
|
236
261
|
}).catch(() => { });
|
|
237
262
|
}, 300_000);
|
|
238
|
-
|
|
263
|
+
// System monitor interval (10s)
|
|
264
|
+
const sysInterval = setInterval(() => {
|
|
265
|
+
const stats = systemMonitor.getStats();
|
|
266
|
+
setCpu(stats.cpuPercent);
|
|
267
|
+
setRam(stats.ramPercent);
|
|
268
|
+
setUptime(stats.uptime);
|
|
269
|
+
setApiCalls(systemMonitor.resetApiCounter());
|
|
270
|
+
}, 10_000);
|
|
271
|
+
// Session auto-save (30s)
|
|
272
|
+
sessionStore.startAutoSave(() => messages);
|
|
273
|
+
sessionStore.setCurrentId(sessionId);
|
|
274
|
+
const saveInterval = setInterval(() => {
|
|
275
|
+
if (messages.length > 0)
|
|
276
|
+
sessionStore.save(messages);
|
|
277
|
+
}, 30_000);
|
|
239
278
|
const session = new SessionManager();
|
|
240
279
|
session.setSystemPrompt(registry.getSystemPrompt() || systemPrompt || "You are AIAIAI Chain Agent.");
|
|
241
280
|
sessionRef.current = session;
|
|
@@ -299,9 +338,13 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
299
338
|
if (sessionCtxRef.current)
|
|
300
339
|
registry.fireHook("session_end", sessionCtxRef.current).catch(() => { });
|
|
301
340
|
agentScheduler.stop();
|
|
341
|
+
sessionStore.stopAutoSave();
|
|
342
|
+
if (messages.length > 0)
|
|
343
|
+
sessionStore.save(messages);
|
|
302
344
|
clearInterval(priceInterval);
|
|
303
345
|
clearInterval(walletInterval);
|
|
304
346
|
clearInterval(newsInterval);
|
|
347
|
+
clearInterval(sysInterval);
|
|
305
348
|
clearInterval(saveInterval);
|
|
306
349
|
costTracker?.saveLifetime();
|
|
307
350
|
};
|
|
@@ -309,10 +352,13 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
309
352
|
useInput((_input, key) => {
|
|
310
353
|
if (key.ctrl && _input === "c") {
|
|
311
354
|
push({ role: "system", content: T.muted("Goodbye 🤖") });
|
|
355
|
+
if (messages.length > 0)
|
|
356
|
+
sessionStore.save(messages);
|
|
312
357
|
costTracker?.saveLifetime();
|
|
313
358
|
setTimeout(exit, 200);
|
|
314
359
|
}
|
|
315
360
|
});
|
|
361
|
+
// ── Submit handler ─────────────────────────────────────────────────────
|
|
316
362
|
const handleSubmit = useCallback(async (raw) => {
|
|
317
363
|
const input = raw.trim();
|
|
318
364
|
if (!input)
|
|
@@ -321,16 +367,157 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
321
367
|
const [cmd, ...rest] = input.slice(1).split(" ");
|
|
322
368
|
const args = rest.join(" ");
|
|
323
369
|
if (cmd === "exit" || cmd === "quit") {
|
|
324
|
-
|
|
370
|
+
if (messages.length > 0)
|
|
371
|
+
sessionStore.save(messages);
|
|
325
372
|
costTracker?.saveLifetime();
|
|
373
|
+
push({ role: "system", content: T.muted("Goodbye 🤖") });
|
|
326
374
|
setTimeout(exit, 200);
|
|
327
375
|
return;
|
|
328
376
|
}
|
|
329
377
|
if (cmd === "help") {
|
|
330
|
-
|
|
331
|
-
|
|
378
|
+
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\n/sessions /resume /memory /clear /exit\n\nTab to auto-complete, ↑↓ for history, Shift+Enter for multi-line");
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
// ── GMGN direct commands ──────────────────────────────────────────
|
|
382
|
+
if (cmd === "token") {
|
|
383
|
+
// /token info --chain sol --address <addr>
|
|
384
|
+
const parts = args.split(" ");
|
|
385
|
+
const subcmd = parts[0];
|
|
386
|
+
if (!subcmd || subcmd === "help") {
|
|
387
|
+
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>");
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
const chain = parts[parts.indexOf("--chain") + 1] || "sol";
|
|
391
|
+
const address = parts[parts.indexOf("--address") + 1];
|
|
392
|
+
if (!address && subcmd !== "help") {
|
|
393
|
+
notify(T.error("--address is required"));
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
const result = await gmgnTool("", { subcommand: subcmd, chain, address });
|
|
397
|
+
notify(result.content[0].text);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
if (cmd === "track") {
|
|
401
|
+
// /track kol --chain sol
|
|
402
|
+
const parts = args.split(" ");
|
|
403
|
+
const subcmd = parts[0];
|
|
404
|
+
if (!subcmd || subcmd === "help") {
|
|
405
|
+
notify("Track commands:\n/track kol --chain <sol|bsc|base|eth>\n/track smartmoney --chain <chain>\n/track follow-wallet --chain <chain>");
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
const chain = parts[parts.indexOf("--chain") + 1] || "sol";
|
|
409
|
+
const result = await gmgnTool("", { subcommand: `track ${subcmd}`, chain, limit: 10 });
|
|
410
|
+
notify(result.content[0].text);
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
if (cmd === "market") {
|
|
414
|
+
// /market kline --chain sol --address <addr> --resolution 1h
|
|
415
|
+
const parts = args.split(" ");
|
|
416
|
+
const subcmd = parts[0];
|
|
417
|
+
if (!subcmd || subcmd === "help") {
|
|
418
|
+
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>");
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const chain = parts[parts.indexOf("--chain") + 1] || "sol";
|
|
422
|
+
const address = parts[parts.indexOf("--address") + 1];
|
|
423
|
+
const resolution = parts[parts.indexOf("--resolution") + 1];
|
|
424
|
+
const interval = parts[parts.indexOf("--interval") + 1];
|
|
425
|
+
const trenchesType = parts[parts.indexOf("--type") + 1];
|
|
426
|
+
const toolParams = { subcommand: `market ${subcmd}`, chain };
|
|
427
|
+
if (address)
|
|
428
|
+
toolParams.address = address;
|
|
429
|
+
if (resolution)
|
|
430
|
+
toolParams.resolution = resolution;
|
|
431
|
+
if (interval)
|
|
432
|
+
toolParams.interval = interval;
|
|
433
|
+
if (trenchesType)
|
|
434
|
+
toolParams.type = trenchesType;
|
|
435
|
+
const result = await gmgnTool("", toolParams);
|
|
436
|
+
notify(result.content[0].text);
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
// ── Cross tools ────────────────────────────────────────────────────
|
|
440
|
+
if (cmd === "watch") {
|
|
441
|
+
const result = await watchTokenTool("", { address: args || "AVPJS61gZmWKtaEpb7qYPKo8Fk2xQUsayYQxPiPMpump" });
|
|
442
|
+
setWatchList(_getWatchList());
|
|
443
|
+
notify(result.content[0].text);
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
if (cmd === "unwatch") {
|
|
447
|
+
const result = await removeWatchTool("", { address: args });
|
|
448
|
+
setWatchList(_getWatchList());
|
|
449
|
+
notify(result.content[0].text);
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
if (cmd === "watchlist") {
|
|
453
|
+
const result = await listWatchTool();
|
|
454
|
+
notify(result.content[0].text);
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
if (cmd === "alerts") {
|
|
458
|
+
const result = await checkAlertsTool();
|
|
459
|
+
notify(result.content[0].text);
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
if (cmd === "alert") {
|
|
463
|
+
// /alert above 0.001 AIAIAI
|
|
464
|
+
const parts = args.split(" ");
|
|
465
|
+
const type = parts[0];
|
|
466
|
+
const price = parseFloat(parts[1]);
|
|
467
|
+
if (!type || isNaN(price)) {
|
|
468
|
+
notify(T.error("Usage: /alert above|below <price>"));
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
const result = await addAlertTool("", { token: "AIAIAI", type, price });
|
|
472
|
+
notify(result.content[0].text);
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
if (cmd === "compare") {
|
|
476
|
+
// /compare <addr1> <addr2>
|
|
477
|
+
const parts = args.split(" ");
|
|
478
|
+
if (parts.length < 2) {
|
|
479
|
+
notify(T.error("Usage: /compare <address1> <address2>"));
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
const result = await compareTokensTool("", { address1: parts[0], address2: parts[1] });
|
|
483
|
+
notify(result.content[0].text);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
if (cmd === "portfolio") {
|
|
487
|
+
const addr = args || "A11iZoqEt6hU7HyggqC67ee4AtYmaJjwKCvJLerJRV2J";
|
|
488
|
+
const result = await portfolioTool("", { address: addr });
|
|
489
|
+
notify(result.content[0].text);
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
// ── Session management ─────────────────────────────────────────────
|
|
493
|
+
if (cmd === "sessions") {
|
|
494
|
+
const sessions = sessionStore.list();
|
|
495
|
+
if (sessions.length === 0) {
|
|
496
|
+
notify("No saved sessions.");
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
const lines = sessions.slice(0, 10).map((s, i) => ` ${i + 1}. ${s.title} (${s.messageCount} msgs, ${new Date(s.updatedAt).toLocaleDateString()})`);
|
|
500
|
+
notify(`Recent Sessions:\n${lines.join("\n")}\n\nUse /resume <number> to resume.`);
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
if (cmd === "resume") {
|
|
504
|
+
const sessions = sessionStore.list();
|
|
505
|
+
const idx = parseInt(args) - 1;
|
|
506
|
+
if (isNaN(idx) || idx < 0 || idx >= sessions.length) {
|
|
507
|
+
notify(T.error("Invalid session number."));
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
const session = sessionStore.load(sessions[idx].id);
|
|
511
|
+
if (!session) {
|
|
512
|
+
notify(T.error("Session not found."));
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
setMessages(session.messages);
|
|
516
|
+
sessionStore.setCurrentId(session.id);
|
|
517
|
+
notify(`Resumed: ${session.title} (${session.messages.length} messages)`);
|
|
332
518
|
return;
|
|
333
519
|
}
|
|
520
|
+
// ── Standard commands ───────────────────────────────────────────────
|
|
334
521
|
if (cmd === "price") {
|
|
335
522
|
const result = await priceFeed.getAiaiaiPriceTool();
|
|
336
523
|
notify(result.content[0].text);
|
|
@@ -379,20 +566,7 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
379
566
|
return;
|
|
380
567
|
}
|
|
381
568
|
if (cmd === "burn") {
|
|
382
|
-
notify([
|
|
383
|
-
"🔥 Burn $AIAIAI",
|
|
384
|
-
"",
|
|
385
|
-
`Deposit wallet: ${DEPOSIT_WALLET}`,
|
|
386
|
-
`Send SOL or USDC here to fuel agent burns.`,
|
|
387
|
-
"",
|
|
388
|
-
`The agent detects deposits and automatically`,
|
|
389
|
-
`sends funds to the action wallet to burn tokens.`,
|
|
390
|
-
"",
|
|
391
|
-
`Action wallet: ${ACTION_WALLET}`,
|
|
392
|
-
`Signer: ${SIGNER}`,
|
|
393
|
-
"",
|
|
394
|
-
`Burn destination: tokens are removed from circulation.`,
|
|
395
|
-
].join("\n"));
|
|
569
|
+
notify(["🔥 Burn $AIAIAI", "", `Deposit: ${DEPOSIT_WALLET}`, "Send SOL or USDC here.", "", `Action: ${ACTION_WALLET}`, `Signer: ${SIGNER}`].join("\n"));
|
|
396
570
|
return;
|
|
397
571
|
}
|
|
398
572
|
if (cmd === "actions" || cmd === "activity") {
|
|
@@ -405,6 +579,10 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
405
579
|
notify(result.content[0].text);
|
|
406
580
|
return;
|
|
407
581
|
}
|
|
582
|
+
if (cmd === "keys" || cmd === "providers") {
|
|
583
|
+
notify("Switch to CLI for key management:\n aiaiai keys");
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
408
586
|
if (cmd === "goals" || cmd === "goal") {
|
|
409
587
|
const subCmd = args.trim().split(" ")[0];
|
|
410
588
|
if (subCmd === "add") {
|
|
@@ -436,34 +614,41 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
436
614
|
notify(result.content[0].text);
|
|
437
615
|
return;
|
|
438
616
|
}
|
|
439
|
-
if (cmd === "
|
|
440
|
-
|
|
441
|
-
if (pending) {
|
|
442
|
-
pending(true);
|
|
443
|
-
push({ role: "system", content: T.success("✅ Tool approved.") });
|
|
444
|
-
}
|
|
445
|
-
else
|
|
446
|
-
notify(T.error("No pending approval."));
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
449
|
-
if (cmd === "deny" || cmd === "n") {
|
|
450
|
-
const pending = runnerRef.current?._pendingApproval;
|
|
451
|
-
if (pending) {
|
|
452
|
-
pending(false);
|
|
453
|
-
push({ role: "system", content: T.error("❌ Tool denied.") });
|
|
454
|
-
}
|
|
455
|
-
else
|
|
456
|
-
notify(T.error("No pending approval."));
|
|
617
|
+
if (cmd === "update" || cmd === "upgrade") {
|
|
618
|
+
notify("Switch to CLI:\n aiaiai update");
|
|
457
619
|
return;
|
|
458
620
|
}
|
|
459
621
|
if (cmd === "memory" && args.trim()) {
|
|
460
622
|
const results = memoryStore.search(args.trim(), 8);
|
|
461
623
|
if (results.length === 0) {
|
|
462
|
-
notify(T.muted("No memories found
|
|
624
|
+
notify(T.muted("No memories found."));
|
|
463
625
|
return;
|
|
464
626
|
}
|
|
465
627
|
const lines = results.map(r => `[${new Date(r.ts).toLocaleDateString()} ${r.role}] ${r.content.slice(0, 120)}`);
|
|
466
|
-
notify(`Memory
|
|
628
|
+
notify(`Memory: "${args.trim()}"\n${lines.join("\n")}`);
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
if (cmd === "gmgn" || cmd === "gmgnhelp") {
|
|
632
|
+
const gmgnArgs = rest.join(" ");
|
|
633
|
+
if (!gmgnArgs || gmgnArgs === "help") {
|
|
634
|
+
notify(gmgnHelp());
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
if (gmgnArgs === "status") {
|
|
638
|
+
notify(gmgnStatus());
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
if (gmgnArgs.startsWith("setup")) {
|
|
642
|
+
notify("Run in CLI: aiaiai gmgn setup");
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
const result = await gmgnTool("", { subcommand: gmgnArgs.split(" ")[0], chain: "sol" });
|
|
646
|
+
notify(result.content[0].text);
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
if (cmd === "gmgnmarket" || cmd === "market") {
|
|
650
|
+
const result = await gmgnMarketTool("", { query: args || "trending", chain: "sol" });
|
|
651
|
+
notify(result.content[0].text);
|
|
467
652
|
return;
|
|
468
653
|
}
|
|
469
654
|
const def = registry.getCommand(cmd);
|
|
@@ -495,6 +680,7 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
495
680
|
notify(T.error(`Error: ${e.message}`));
|
|
496
681
|
}
|
|
497
682
|
}, [registry, exit, push, notify, uiCtx, modelReg, costTracker]);
|
|
683
|
+
// ── Model selector ─────────────────────────────────────────────────────
|
|
498
684
|
const handleModelSelect = useCallback((modelId) => {
|
|
499
685
|
setShowModelSelector(false);
|
|
500
686
|
try {
|
|
@@ -527,17 +713,20 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
527
713
|
}, [modelReg]);
|
|
528
714
|
const ctx = getContextBar(sessionRef.current);
|
|
529
715
|
const statusLine = [costBadge, newsBadge, ...Object.values(statusBadges)].filter(Boolean).join(" ") || null;
|
|
530
|
-
const changeColor = (pct) => pct > 0 ? AIAIAI_COLORS.success : pct < 0 ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
|
|
531
716
|
const SIDEBAR_W = 44;
|
|
532
|
-
const
|
|
717
|
+
const SIDEBAR_MIN = 38;
|
|
718
|
+
const changeColor = (pct) => pct > 0 ? AIAIAI_COLORS.success : pct < 0 ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
|
|
719
|
+
const actionIcon = (type) => type === "buy" ? "↗" : type === "burn" ? "🔥" : "→";
|
|
533
720
|
if (showModelSelector) {
|
|
534
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusBar, { model: modelName, chain: chain, price: priceBadge, toolRunning: toolRunning, connected: true, statusLine: statusLine }), _jsx(ModelSelector, { models: modelList, currentModelId: process.env.DEFAULT_MODEL ?? "", onSelect: handleModelSelect, onCancel: handleModelCancel, initialQuery: modelSelectorInitialQuery })] }));
|
|
721
|
+
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 }), _jsx(ModelSelector, { models: modelList, currentModelId: process.env.DEFAULT_MODEL ?? "", onSelect: handleModelSelect, onCancel: handleModelCancel, initialQuery: modelSelectorInitialQuery })] }));
|
|
535
722
|
}
|
|
536
|
-
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, flexShrink: 0, 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) => {
|
|
723
|
+
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 }), _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) => {
|
|
537
724
|
const col = a.type === "buy" ? AIAIAI_COLORS.success : a.type === "burn" ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
|
|
538
725
|
const time = new Date(a.timestamp).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
539
726
|
return _jsxs(Text, { color: col, children: [actionIcon(a.type), " ", time, " ", a.type, " ", a.amount.toLocaleString()] }, i);
|
|
540
|
-
}) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Waiting
|
|
727
|
+
}) : _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) => {
|
|
728
|
+
return _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [addr.slice(0, 8), "\u2026", addr.slice(-6)] }, i);
|
|
729
|
+
}) : _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) => {
|
|
541
730
|
const sentColor = n.sentiment > 0.3 ? AIAIAI_COLORS.success : n.sentiment < -0.3 ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
|
|
542
731
|
const title = n.title.length > 30 ? n.title.slice(0, 28) + "…" : n.title;
|
|
543
732
|
return _jsx(Text, { color: sentColor, children: title }, i);
|
package/dist/tui/REPL.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* REPL — scrolling message history +
|
|
2
|
+
* REPL — scrolling message history + feature-rich input box.
|
|
3
|
+
* Supports: multi-line (Shift+Enter), command history (↑/↓), tab completion.
|
|
3
4
|
*/
|
|
4
5
|
import React from "react";
|
|
5
6
|
export interface ChatMessage {
|