@aiaiaichain/agent 0.1.1
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 +157 -0
- package/bin/aiai-mcp +31 -0
- package/bin/aiaiaicli +60 -0
- package/dist/api/ExtensionAPI.d.ts +68 -0
- package/dist/api/ExtensionAPI.js +9 -0
- package/dist/api/Registry.d.ts +24 -0
- package/dist/api/Registry.js +58 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +252 -0
- package/dist/core/AgentDir.d.ts +10 -0
- package/dist/core/AgentDir.js +74 -0
- package/dist/core/ChainConfig.d.ts +19 -0
- package/dist/core/ChainConfig.js +65 -0
- package/dist/core/EnvLoader.d.ts +15 -0
- package/dist/core/EnvLoader.js +58 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +42 -0
- package/dist/loader.d.ts +11 -0
- package/dist/loader.js +73 -0
- package/dist/mcp/entry.d.ts +5 -0
- package/dist/mcp/entry.js +10 -0
- package/dist/mcp/server.d.ts +14 -0
- package/dist/mcp/server.js +137 -0
- package/dist/models/CostTracker.d.ts +38 -0
- package/dist/models/CostTracker.js +75 -0
- package/dist/models/ModelRegistry.d.ts +70 -0
- package/dist/models/ModelRegistry.js +163 -0
- package/dist/runner/AgentRunner.d.ts +54 -0
- package/dist/runner/AgentRunner.js +171 -0
- package/dist/runner/ModelClient.d.ts +30 -0
- package/dist/runner/ModelClient.js +84 -0
- package/dist/runner/SwarmRouter.d.ts +23 -0
- package/dist/runner/SwarmRouter.js +24 -0
- package/dist/runner/ToolDispatcher.d.ts +13 -0
- package/dist/runner/ToolDispatcher.js +34 -0
- package/dist/scheduler/AgentScheduler.d.ts +48 -0
- package/dist/scheduler/AgentScheduler.js +111 -0
- package/dist/session/ContextStore.d.ts +28 -0
- package/dist/session/ContextStore.js +85 -0
- package/dist/session/GoalManager.d.ts +43 -0
- package/dist/session/GoalManager.js +108 -0
- package/dist/session/MemoryStore.d.ts +27 -0
- package/dist/session/MemoryStore.js +92 -0
- package/dist/session/SessionManager.d.ts +24 -0
- package/dist/session/SessionManager.js +57 -0
- package/dist/setup/SetupWizard.d.ts +13 -0
- package/dist/setup/SetupWizard.js +71 -0
- package/dist/tools/MarketSentiment.d.ts +20 -0
- package/dist/tools/MarketSentiment.js +211 -0
- package/dist/tools/NewsSentiment.d.ts +36 -0
- package/dist/tools/NewsSentiment.js +141 -0
- package/dist/tools/PriceFeed.d.ts +85 -0
- package/dist/tools/PriceFeed.js +134 -0
- package/dist/tools/TechnicalAnalysis.d.ts +50 -0
- package/dist/tools/TechnicalAnalysis.js +234 -0
- package/dist/tui/App.d.ts +20 -0
- package/dist/tui/App.js +484 -0
- package/dist/tui/ModelSelector.d.ts +18 -0
- package/dist/tui/ModelSelector.js +59 -0
- package/dist/tui/REPL.d.ts +22 -0
- package/dist/tui/REPL.js +48 -0
- package/dist/tui/StatusBar.d.ts +14 -0
- package/dist/tui/StatusBar.js +13 -0
- package/dist/tui/theme.d.ts +27 -0
- package/dist/tui/theme.js +38 -0
- package/dist/util/safeLog.d.ts +8 -0
- package/dist/util/safeLog.js +38 -0
- package/package.json +64 -0
package/dist/tui/App.js
ADDED
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* App β root Ink component. Multi-pane TUI with sidebar (price, news, context)
|
|
4
|
+
* and main REPL area.
|
|
5
|
+
*/
|
|
6
|
+
import { useState, useCallback, useEffect, useRef, useMemo } from "react";
|
|
7
|
+
import { Box, Text, useApp, useInput } from "ink";
|
|
8
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
import { Type } from "@sinclair/typebox";
|
|
12
|
+
import { StatusBar } from "./StatusBar.js";
|
|
13
|
+
import { REPL } from "./REPL.js";
|
|
14
|
+
import { ModelSelector } from "./ModelSelector.js";
|
|
15
|
+
import { makeTheme, T, AIAIAI_COLORS } from "./theme.js";
|
|
16
|
+
import { AgentRunner } from "../runner/AgentRunner.js";
|
|
17
|
+
import { SessionManager } from "../session/SessionManager.js";
|
|
18
|
+
import { resolveModelConfig } from "../runner/ModelClient.js";
|
|
19
|
+
import { priceFeed } from "../tools/PriceFeed.js";
|
|
20
|
+
import { getNewsTool, getNewsParams, newsFeed } from "../tools/NewsSentiment.js";
|
|
21
|
+
import { analyzeTAParams, getCandlesParams, getCandlesTool, fullAnalysis } from "../tools/TechnicalAnalysis.js";
|
|
22
|
+
import { getFearGreedTool, fearGreedParams, getFundingRatesTool, fundingRatesParams, getBtcMempoolTool, btcMempoolParams, getDefiTvlTool, defiTvlParams, getSolanaStatsTool, solanaStatsParams, } from "../tools/MarketSentiment.js";
|
|
23
|
+
import { contextStore } from "../session/ContextStore.js";
|
|
24
|
+
import { goalManager } from "../session/GoalManager.js";
|
|
25
|
+
import { memoryStore } from "../session/MemoryStore.js";
|
|
26
|
+
import { agentScheduler } from "../scheduler/AgentScheduler.js";
|
|
27
|
+
function getContextBar(session) {
|
|
28
|
+
if (!session)
|
|
29
|
+
return { pct: 0, bar: "β".repeat(20), color: AIAIAI_COLORS.dim };
|
|
30
|
+
const pressure = session.getContextPressure();
|
|
31
|
+
const filled = Math.round(pressure.pct / 5);
|
|
32
|
+
const color = pressure.level === "critical" ? AIAIAI_COLORS.error
|
|
33
|
+
: pressure.level === "red" ? AIAIAI_COLORS.warn
|
|
34
|
+
: pressure.level === "yellow" ? AIAIAI_COLORS.header
|
|
35
|
+
: AIAIAI_COLORS.success;
|
|
36
|
+
return {
|
|
37
|
+
pct: pressure.pct,
|
|
38
|
+
bar: "β".repeat(Math.min(filled, 20)) + "β".repeat(Math.max(0, 20 - filled)),
|
|
39
|
+
color,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function highlightJson(obj, indent = 0) {
|
|
43
|
+
const pad = " ".repeat(indent);
|
|
44
|
+
if (obj === null)
|
|
45
|
+
return T.dim("null");
|
|
46
|
+
if (typeof obj === "boolean")
|
|
47
|
+
return T.warn(String(obj));
|
|
48
|
+
if (typeof obj === "number")
|
|
49
|
+
return T.success(String(obj));
|
|
50
|
+
if (typeof obj === "string")
|
|
51
|
+
return T.accent(`"${obj}"`);
|
|
52
|
+
if (Array.isArray(obj)) {
|
|
53
|
+
if (obj.length === 0)
|
|
54
|
+
return "[]";
|
|
55
|
+
if (obj.length <= 3)
|
|
56
|
+
return `[${obj.map(v => highlightJson(v, 0)).join(", ")}]`;
|
|
57
|
+
return `[\n${obj.slice(0, 5).map(v => `${pad} ${highlightJson(v, indent + 1)}`).join(",\n")}${obj.length > 5 ? `\n${pad} β¦${obj.length - 5} more` : ""}\n${pad}]`;
|
|
58
|
+
}
|
|
59
|
+
if (typeof obj === "object") {
|
|
60
|
+
const entries = Object.entries(obj);
|
|
61
|
+
if (entries.length === 0)
|
|
62
|
+
return "{}";
|
|
63
|
+
return `{\n${entries.slice(0, 8).map(([k, v]) => `${pad} ${T.accent(k)}: ${highlightJson(v, indent + 1)}`).join(",\n")}${entries.length > 8 ? `\n${pad} β¦${entries.length - 8} more` : ""}\n${pad}}`;
|
|
64
|
+
}
|
|
65
|
+
return String(obj);
|
|
66
|
+
}
|
|
67
|
+
function formatToolContent(text) {
|
|
68
|
+
try {
|
|
69
|
+
return highlightJson(JSON.parse(text));
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return text.length > 300 ? text.slice(0, 300) + T.dim("\nβ¦[truncated]") : text;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
let _msgIdCounter = 0;
|
|
76
|
+
function nextId() { return String(++_msgIdCounter); }
|
|
77
|
+
const sessionId = `session-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
|
|
78
|
+
export function App({ registry, systemPrompt, chain: initialChain = "solana", modelReg, costTracker, onNotifyReady, onStatusReady, onModelSelectorReady, }) {
|
|
79
|
+
const { exit } = useApp();
|
|
80
|
+
const [messages, setMessages] = useState([]);
|
|
81
|
+
const [streaming, setStreaming] = useState("");
|
|
82
|
+
const [toolRunning, setToolRunning] = useState(null);
|
|
83
|
+
const [disabled, setDisabled] = useState(false);
|
|
84
|
+
const [chain] = useState(initialChain);
|
|
85
|
+
const [statusBadges, setStatusBadges] = useState({});
|
|
86
|
+
const [priceBadge, setPriceBadge] = useState("");
|
|
87
|
+
const [costBadge, setCostBadge] = useState("");
|
|
88
|
+
const [newsBadge, setNewsBadge] = useState("");
|
|
89
|
+
const [sidebarNews, setSidebarNews] = useState([]);
|
|
90
|
+
const [aiaiPrice, setAiaiPrice] = useState(null);
|
|
91
|
+
const [showModelSelector, setShowModelSelector] = useState(false);
|
|
92
|
+
const [modelSelectorInitialQuery, setModelSelectorInitialQuery] = useState("");
|
|
93
|
+
const runnerRef = useRef(null);
|
|
94
|
+
const sessionRef = useRef(null);
|
|
95
|
+
const sessionCtxRef = useRef(null);
|
|
96
|
+
const theme = makeTheme();
|
|
97
|
+
let modelName = "no-model";
|
|
98
|
+
try {
|
|
99
|
+
modelName = resolveModelConfig(modelReg).model;
|
|
100
|
+
}
|
|
101
|
+
catch { /* shown via banner */ }
|
|
102
|
+
const push = useCallback((msg) => {
|
|
103
|
+
setMessages(prev => [...prev, { ...msg, id: nextId(), ts: Date.now() }]);
|
|
104
|
+
}, []);
|
|
105
|
+
const notify = useCallback((content) => { push({ role: "notify", content }); }, [push]);
|
|
106
|
+
const setStatus = useCallback((key, value) => {
|
|
107
|
+
setStatusBadges(prev => ({ ...prev, [key]: value }));
|
|
108
|
+
}, []);
|
|
109
|
+
const uiCtx = {
|
|
110
|
+
notify, setStatus,
|
|
111
|
+
setTheme() { },
|
|
112
|
+
setHeader() { },
|
|
113
|
+
showModelSelector: (query) => {
|
|
114
|
+
setModelSelectorInitialQuery(query ?? "");
|
|
115
|
+
setShowModelSelector(true);
|
|
116
|
+
},
|
|
117
|
+
theme,
|
|
118
|
+
};
|
|
119
|
+
function registerBuiltinTools() {
|
|
120
|
+
if (!modelReg)
|
|
121
|
+
return;
|
|
122
|
+
const mk = modelReg;
|
|
123
|
+
registry.addTool({ name: "list_models", label: "List Models", description: "Search and filter available AI models.", parameters: mk.listModelsParams, execute: (id, p) => mk.listModelsTool(id, p) });
|
|
124
|
+
registry.addTool({ name: "pick_model", label: "Pick Model", description: "Find a model in a given tier.", parameters: mk.pickModelParams, execute: (id, p) => mk.pickModelTool(id, p) });
|
|
125
|
+
registry.addTool({ name: "model_summary", label: "Model Summary", description: "Summary of available model tiers.", parameters: mk.summaryParams, execute: () => mk.summaryTool() });
|
|
126
|
+
if (costTracker) {
|
|
127
|
+
registry.addTool({ name: "cost_report", label: "Cost Report", description: "Show session and lifetime token usage.", parameters: costTracker.costReportParams, execute: () => costTracker.costReportTool() });
|
|
128
|
+
}
|
|
129
|
+
// Price tools
|
|
130
|
+
registry.addTool({ name: "get_aiaiai_price", label: "AIAIAI Price", description: "Get the current $AIAIAI token price, liquidity, market cap, and volume from DexScreener.", parameters: priceFeed.getAiaiaiPriceParams, execute: () => priceFeed.getAiaiaiPriceTool() });
|
|
131
|
+
registry.addTool({ name: "get_token_price", label: "Token Price", description: "Get price data for any Solana token by address via DexScreener.", parameters: priceFeed.getTokenPriceParams, execute: (id, p) => priceFeed.getTokenPriceTool(id, p) });
|
|
132
|
+
// News
|
|
133
|
+
registry.addTool({ name: "get_news", label: "Get News", description: "Crypto news headlines with sentiment scoring.", parameters: getNewsParams, execute: (id, p) => getNewsTool(id, p) });
|
|
134
|
+
// TA
|
|
135
|
+
registry.addTool({ name: "get_candles", label: "Get Candles", description: "Fetch OHLCV candlestick data from Binance and run technical analysis (RSI, MACD, Bollinger, EMA).", parameters: getCandlesParams, execute: (id, p) => getCandlesTool(id, p) });
|
|
136
|
+
registry.addTool({ name: "analyze_ta", label: "Technical Analysis", description: "Run RSI, MACD, Bollinger, ATR on a price array.", parameters: analyzeTAParams, execute: async (_id, p) => {
|
|
137
|
+
const closes = p.prices;
|
|
138
|
+
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 }));
|
|
139
|
+
const results = fullAnalysis(candles);
|
|
140
|
+
const summary = results.find((r) => r.indicator === "SUMMARY");
|
|
141
|
+
const text = results.map((r) => {
|
|
142
|
+
const s = r.signal === "bullish" ? "π’" : r.signal === "bearish" ? "π΄" : "βͺ";
|
|
143
|
+
const v = Array.isArray(r.value) ? `[${r.value.length}]` : r.value;
|
|
144
|
+
return `${s} ${r.indicator}: ${v}`;
|
|
145
|
+
}).join("\n");
|
|
146
|
+
return { content: [{ type: "text", text: `Technical Analysis:\n${text}` }], details: { results, overall_signal: summary?.signal } };
|
|
147
|
+
} });
|
|
148
|
+
// Market sentiment
|
|
149
|
+
registry.addTool({ name: "get_fear_greed", label: "Fear & Greed", description: "Crypto Fear & Greed Index with 7-day history.", parameters: fearGreedParams, execute: () => getFearGreedTool() });
|
|
150
|
+
registry.addTool({ name: "get_funding_rates", label: "Funding Rates", description: "Binance perpetual funding rates.", parameters: fundingRatesParams, execute: (id, p) => getFundingRatesTool(id, p) });
|
|
151
|
+
registry.addTool({ name: "get_btc_mempool", label: "BTC Mempool", description: "Bitcoin mempool stats and fee rates.", parameters: btcMempoolParams, execute: () => getBtcMempoolTool() });
|
|
152
|
+
registry.addTool({ name: "get_defi_tvl", label: "DeFi TVL", description: "DeFiLlama TVL by chain or all chains.", parameters: defiTvlParams, execute: (id, p) => getDefiTvlTool(id, p) });
|
|
153
|
+
registry.addTool({ name: "get_solana_stats", label: "Solana Stats", description: "Solana network epoch, slot, and SOL price.", parameters: solanaStatsParams, execute: () => getSolanaStatsTool() });
|
|
154
|
+
// Context + goals
|
|
155
|
+
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) });
|
|
156
|
+
registry.addTool({ name: "list_tasks", label: "List Tasks", description: "List saved task contexts.", parameters: Type.Object({}), execute: () => contextStore.listTasksTool() });
|
|
157
|
+
registry.addTool({ name: "set_goal", label: "Set Goal", description: "Set a persistent cross-session goal.", parameters: goalManager.setGoalParams, execute: (id, p) => goalManager.setGoalTool(id, p) });
|
|
158
|
+
registry.addTool({ name: "complete_goal", label: "Complete Goal", description: "Mark a goal complete.", parameters: goalManager.completeGoalParams, execute: (id, p) => goalManager.completeGoalTool(id, p) });
|
|
159
|
+
registry.addTool({ name: "list_goals", label: "List Goals", description: "List goals.", parameters: goalManager.listGoalsParams, execute: (id, p) => goalManager.listGoalsTool(id, p) });
|
|
160
|
+
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) });
|
|
161
|
+
// Scheduler
|
|
162
|
+
registry.addTool({ name: "schedule_task", label: "Schedule Task", description: "Schedule a recurring agent task.", parameters: agentScheduler.addTaskParams, execute: (id, p) => agentScheduler.addTaskTool(id, p) });
|
|
163
|
+
registry.addTool({ name: "list_schedule", label: "List Schedule", description: "List scheduled tasks.", parameters: agentScheduler.listTasksParams, execute: (id, p) => agentScheduler.listTasksTool(id, p) });
|
|
164
|
+
registry.addTool({ name: "remove_schedule", label: "Remove Schedule", description: "Remove a scheduled task.", parameters: agentScheduler.removeTaskParams, execute: (id, p) => agentScheduler.removeTaskTool(id, p) });
|
|
165
|
+
}
|
|
166
|
+
useEffect(() => { onNotifyReady?.(notify); onStatusReady?.(setStatus); }, []);
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
onModelSelectorReady?.((initialQuery) => {
|
|
169
|
+
setModelSelectorInitialQuery(initialQuery ?? "");
|
|
170
|
+
setShowModelSelector(true);
|
|
171
|
+
});
|
|
172
|
+
}, [onModelSelectorReady]);
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
registerBuiltinTools();
|
|
175
|
+
// Initial price fetch
|
|
176
|
+
priceFeed.getAiaiaiPrice().then(p => {
|
|
177
|
+
setAiaiPrice({ priceUsd: p.priceUsd, change: p.priceChange24h, mcap: p.marketCap, liq: p.liquidityUsd });
|
|
178
|
+
if (p.priceUsd)
|
|
179
|
+
setPriceBadge(`$${parseFloat(p.priceUsd).toFixed(6)}`);
|
|
180
|
+
}).catch(() => { });
|
|
181
|
+
newsFeed.getLatest().then(report => {
|
|
182
|
+
setSidebarNews(report.items.slice(0, 6).map(i => ({ title: i.title, sentiment: i.sentiment ?? 0, source: i.source })));
|
|
183
|
+
setNewsBadge(newsFeed.statusBadge());
|
|
184
|
+
}).catch(() => { });
|
|
185
|
+
agentScheduler.start((task) => {
|
|
186
|
+
push({ role: "system", content: T.muted(`β° Scheduler: running "${task.name}"`) });
|
|
187
|
+
if (!disabled && runnerRef.current) {
|
|
188
|
+
setDisabled(true);
|
|
189
|
+
setStreaming("");
|
|
190
|
+
runnerRef.current.run(`[SCHEDULED TASK: ${task.name}] ${task.prompt}`)
|
|
191
|
+
.catch((e) => notify(T.error(`Scheduler error: ${e.message}`)));
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
const priceInterval = setInterval(() => {
|
|
195
|
+
priceFeed.getAiaiaiPrice().then(p => {
|
|
196
|
+
setAiaiPrice({ priceUsd: p.priceUsd, change: p.priceChange24h, mcap: p.marketCap, liq: p.liquidityUsd });
|
|
197
|
+
if (p.priceUsd)
|
|
198
|
+
setPriceBadge(`$${parseFloat(p.priceUsd).toFixed(6)}`);
|
|
199
|
+
}).catch(() => { });
|
|
200
|
+
if (costTracker)
|
|
201
|
+
setCostBadge(costTracker.statusLine());
|
|
202
|
+
}, 30_000);
|
|
203
|
+
const newsInterval = setInterval(() => {
|
|
204
|
+
newsFeed.getLatest().then(report => {
|
|
205
|
+
setSidebarNews(report.items.slice(0, 6).map(i => ({ title: i.title, sentiment: i.sentiment ?? 0, source: i.source })));
|
|
206
|
+
setNewsBadge(newsFeed.statusBadge());
|
|
207
|
+
}).catch(() => { });
|
|
208
|
+
}, 300_000);
|
|
209
|
+
const saveInterval = setInterval(() => { costTracker?.saveLifetime(); }, 60_000);
|
|
210
|
+
const session = new SessionManager();
|
|
211
|
+
session.setSystemPrompt(registry.getSystemPrompt() || systemPrompt || "You are AIAIAI Chain Agent.");
|
|
212
|
+
sessionRef.current = session;
|
|
213
|
+
const sessionCtx = { ui: uiCtx, hasUI: true, config: { OPENROUTER_API_KEY: process.env.OPENROUTER_API_KEY, DEFAULT_MODEL: process.env.DEFAULT_MODEL } };
|
|
214
|
+
sessionCtxRef.current = sessionCtx;
|
|
215
|
+
registry.fireHook("session_start", sessionCtx).then(() => {
|
|
216
|
+
if (memoryStore.isAvailable) {
|
|
217
|
+
const memCtx = memoryStore.buildContextBlock(sessionId);
|
|
218
|
+
if (memCtx)
|
|
219
|
+
session.setSystemPrompt((session.getSystemPrompt() || "") + memCtx);
|
|
220
|
+
}
|
|
221
|
+
const goalCtx = goalManager.buildContextBlock();
|
|
222
|
+
if (goalCtx)
|
|
223
|
+
session.setSystemPrompt((session.getSystemPrompt() || "") + goalCtx);
|
|
224
|
+
push({ role: "system", content: T.muted("Session started. Type /help for commands.") });
|
|
225
|
+
});
|
|
226
|
+
const runner = new AgentRunner(registry, session, (event) => {
|
|
227
|
+
if (event.type === "text_delta")
|
|
228
|
+
setStreaming(prev => prev + event.text);
|
|
229
|
+
else if (event.type === "tool_start")
|
|
230
|
+
setToolRunning(event.name);
|
|
231
|
+
else if (event.type === "tool_done") {
|
|
232
|
+
setToolRunning(null);
|
|
233
|
+
push({ role: "tool", content: formatToolContent(event.result), toolName: event.name, isError: event.isError });
|
|
234
|
+
}
|
|
235
|
+
else if (event.type === "turn_done") {
|
|
236
|
+
setDisabled(false);
|
|
237
|
+
setToolRunning(null);
|
|
238
|
+
setStreaming(prev => {
|
|
239
|
+
if (prev.trim()) {
|
|
240
|
+
push({ role: "assistant", content: prev });
|
|
241
|
+
memoryStore.save(sessionId, "assistant", prev);
|
|
242
|
+
}
|
|
243
|
+
return "";
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
else if (event.type === "error") {
|
|
247
|
+
setDisabled(false);
|
|
248
|
+
setToolRunning(null);
|
|
249
|
+
setStreaming("");
|
|
250
|
+
notify(T.error(`Error: ${event.message}`));
|
|
251
|
+
}
|
|
252
|
+
else if (event.type === "approval_request") {
|
|
253
|
+
setToolRunning(null);
|
|
254
|
+
const { toolName, args, approve } = event;
|
|
255
|
+
let parsed = "";
|
|
256
|
+
try {
|
|
257
|
+
parsed = JSON.stringify(JSON.parse(args), null, 2).slice(0, 200);
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
parsed = args.slice(0, 200);
|
|
261
|
+
}
|
|
262
|
+
push({ role: "notify", content: `β οΈ APPROVAL REQUIRED\n\nTool: ${toolName}\nArgs: ${parsed}\n\nType /approve or /deny to continue.` });
|
|
263
|
+
runnerRef.current._pendingApproval = approve;
|
|
264
|
+
}
|
|
265
|
+
}, sessionCtx, "normal", modelReg, costTracker, goalManager, contextStore);
|
|
266
|
+
runnerRef.current = runner;
|
|
267
|
+
if (costTracker)
|
|
268
|
+
setCostBadge(costTracker.statusLine());
|
|
269
|
+
return () => {
|
|
270
|
+
if (sessionCtxRef.current)
|
|
271
|
+
registry.fireHook("session_end", sessionCtxRef.current).catch(() => { });
|
|
272
|
+
agentScheduler.stop();
|
|
273
|
+
clearInterval(priceInterval);
|
|
274
|
+
clearInterval(newsInterval);
|
|
275
|
+
clearInterval(saveInterval);
|
|
276
|
+
costTracker?.saveLifetime();
|
|
277
|
+
};
|
|
278
|
+
}, []);
|
|
279
|
+
useInput((_input, key) => {
|
|
280
|
+
if (key.ctrl && _input === "c") {
|
|
281
|
+
push({ role: "system", content: T.muted("Goodbye π€") });
|
|
282
|
+
costTracker?.saveLifetime();
|
|
283
|
+
setTimeout(exit, 200);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
const handleSubmit = useCallback(async (raw) => {
|
|
287
|
+
const input = raw.trim();
|
|
288
|
+
if (!input)
|
|
289
|
+
return;
|
|
290
|
+
if (input.startsWith("/")) {
|
|
291
|
+
const [cmd, ...rest] = input.slice(1).split(" ");
|
|
292
|
+
const args = rest.join(" ");
|
|
293
|
+
if (cmd === "exit" || cmd === "quit") {
|
|
294
|
+
push({ role: "system", content: T.muted("Goodbye π€") });
|
|
295
|
+
costTracker?.saveLifetime();
|
|
296
|
+
setTimeout(exit, 200);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (cmd === "help") {
|
|
300
|
+
const lines = registry.listCommands().map(([n, d]) => T.accent(`/${n}`.padEnd(16)) + " " + d.description);
|
|
301
|
+
notify("Commands:\n/help /price /news /models /cost /goals /schedule /model /clear /exit\n" + (lines.length ? lines.join("\n") + "\n" : "") + "\nTools: get_aiaiai_price, get_token_price, get_news, get_candles, analyze_ta, get_fear_greed, get_solana_stats, and more");
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
if (cmd === "price") {
|
|
305
|
+
const result = await priceFeed.getAiaiaiPriceTool();
|
|
306
|
+
notify(result.content[0].text);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (cmd === "news") {
|
|
310
|
+
const result = await getNewsTool("", { limit: 15 });
|
|
311
|
+
notify(result.content[0].text);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
if (cmd === "models") {
|
|
315
|
+
if (!modelReg) {
|
|
316
|
+
notify(T.error("Model registry not available"));
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const result = await modelReg.listModelsTool("", { query: rest.join(" ") || undefined, limit: 20 });
|
|
320
|
+
notify(result.content[0].text);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (cmd === "cost") {
|
|
324
|
+
if (!costTracker) {
|
|
325
|
+
notify(T.error("Cost tracker not available"));
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const result = await costTracker.costReportTool();
|
|
329
|
+
notify(result.content[0].text);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (cmd === "model") {
|
|
333
|
+
setModelSelectorInitialQuery(args);
|
|
334
|
+
setShowModelSelector(true);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
if (cmd === "clear") {
|
|
338
|
+
setMessages([]);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if (cmd === "goals" || cmd === "goal") {
|
|
342
|
+
const subCmd = args.trim().split(" ")[0];
|
|
343
|
+
if (subCmd === "add") {
|
|
344
|
+
const text = args.trim().slice(4).trim();
|
|
345
|
+
if (!text) {
|
|
346
|
+
notify(T.error("Usage: /goal add <text>"));
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
const result = await goalManager.setGoalTool("", { text });
|
|
350
|
+
notify(result.content[0].text);
|
|
351
|
+
}
|
|
352
|
+
else if (subCmd === "done" || subCmd === "complete") {
|
|
353
|
+
const id = args.trim().split(" ")[1];
|
|
354
|
+
if (!id) {
|
|
355
|
+
notify(T.error("Usage: /goal done <id>"));
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
const result = await goalManager.completeGoalTool("", { id });
|
|
359
|
+
notify(result.content[0].text);
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
const result = await goalManager.listGoalsTool("", { status: "all" });
|
|
363
|
+
notify(result.content[0].text);
|
|
364
|
+
}
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
if (cmd === "schedule" || cmd === "sched") {
|
|
368
|
+
const result = await agentScheduler.listTasksTool("", { enabled_only: false });
|
|
369
|
+
notify(result.content[0].text);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (cmd === "approve" || cmd === "y") {
|
|
373
|
+
const pending = runnerRef.current?._pendingApproval;
|
|
374
|
+
if (pending) {
|
|
375
|
+
pending(true);
|
|
376
|
+
push({ role: "system", content: T.success("β
Tool approved.") });
|
|
377
|
+
}
|
|
378
|
+
else
|
|
379
|
+
notify(T.error("No pending approval."));
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
if (cmd === "deny" || cmd === "n") {
|
|
383
|
+
const pending = runnerRef.current?._pendingApproval;
|
|
384
|
+
if (pending) {
|
|
385
|
+
pending(false);
|
|
386
|
+
push({ role: "system", content: T.error("β Tool denied.") });
|
|
387
|
+
}
|
|
388
|
+
else
|
|
389
|
+
notify(T.error("No pending approval."));
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
if (cmd === "memory" && args.trim()) {
|
|
393
|
+
const results = memoryStore.search(args.trim(), 8);
|
|
394
|
+
if (results.length === 0) {
|
|
395
|
+
notify(T.muted("No memories found for: " + args.trim()));
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
const lines = results.map(r => `[${new Date(r.ts).toLocaleDateString()} ${r.role}] ${r.content.slice(0, 120)}`);
|
|
399
|
+
notify(`Memory search: "${args.trim()}"\n${lines.join("\n")}`);
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const def = registry.getCommand(cmd);
|
|
403
|
+
if (!def) {
|
|
404
|
+
notify(T.error(`Unknown: /${cmd} β try /help`));
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
try {
|
|
408
|
+
await def.handler(args, { ui: uiCtx });
|
|
409
|
+
}
|
|
410
|
+
catch (e) {
|
|
411
|
+
notify(T.error(`Error: ${e.message}`));
|
|
412
|
+
}
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
if (!runnerRef.current) {
|
|
416
|
+
notify(T.error("Agent not ready"));
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
push({ role: "user", content: input });
|
|
420
|
+
memoryStore.save(sessionId, "user", input);
|
|
421
|
+
setDisabled(true);
|
|
422
|
+
setStreaming("");
|
|
423
|
+
try {
|
|
424
|
+
await runnerRef.current.run(input);
|
|
425
|
+
}
|
|
426
|
+
catch (e) {
|
|
427
|
+
setDisabled(false);
|
|
428
|
+
notify(T.error(`Error: ${e.message}`));
|
|
429
|
+
}
|
|
430
|
+
}, [registry, exit, push, notify, uiCtx, modelReg, costTracker]);
|
|
431
|
+
const handleModelSelect = useCallback((modelId) => {
|
|
432
|
+
setShowModelSelector(false);
|
|
433
|
+
try {
|
|
434
|
+
const AIAIAI_HOME = process.env.AIAIAI_HOME ?? join(homedir(), ".aiaiai");
|
|
435
|
+
const envFile = join(AIAIAI_HOME, ".env");
|
|
436
|
+
mkdirSync(AIAIAI_HOME, { recursive: true });
|
|
437
|
+
const content = existsSync(envFile) ? readFileSync(envFile, "utf-8") : "";
|
|
438
|
+
const re = /^DEFAULT_MODEL=.*$/m;
|
|
439
|
+
const line = `DEFAULT_MODEL=${modelId}`;
|
|
440
|
+
writeFileSync(envFile, re.test(content) ? content.replace(re, line) : content + "\n" + line + "\n", "utf-8");
|
|
441
|
+
process.env.DEFAULT_MODEL = modelId;
|
|
442
|
+
}
|
|
443
|
+
catch { /* non-fatal */ }
|
|
444
|
+
notify(T.accent(`Model set to: ${modelId}\nRestart to apply.`));
|
|
445
|
+
}, [notify]);
|
|
446
|
+
const handleModelCancel = useCallback(() => { setShowModelSelector(false); }, []);
|
|
447
|
+
const modelList = useMemo(() => {
|
|
448
|
+
if (!modelReg)
|
|
449
|
+
return [];
|
|
450
|
+
const tiers = ["orchestrator", "analyst", "worker", "free"];
|
|
451
|
+
const items = [];
|
|
452
|
+
for (const tier of tiers) {
|
|
453
|
+
const pool = modelReg.getPool(tier);
|
|
454
|
+
for (const tm of pool) {
|
|
455
|
+
if (tm.available && tm.failures < 3)
|
|
456
|
+
items.push({ id: tm.model.id, tier });
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return items;
|
|
460
|
+
}, [modelReg]);
|
|
461
|
+
const ctx = getContextBar(sessionRef.current);
|
|
462
|
+
const statusLine = [costBadge, newsBadge, ...Object.values(statusBadges)].filter(Boolean).join(" ") || null;
|
|
463
|
+
const changeColor = (pct) => pct > 0 ? AIAIAI_COLORS.success : pct < 0 ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
|
|
464
|
+
const SIDEBAR_W = 42;
|
|
465
|
+
if (showModelSelector) {
|
|
466
|
+
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 })] }));
|
|
467
|
+
}
|
|
468
|
+
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: "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: "News" }), sidebarNews.length > 0 ? sidebarNews.map((n, i) => {
|
|
469
|
+
const sentColor = n.sentiment > 0.3 ? AIAIAI_COLORS.success : n.sentiment < -0.3 ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
|
|
470
|
+
const title = n.title.length > 30 ? n.title.slice(0, 28) + "β¦" : n.title;
|
|
471
|
+
return _jsx(Text, { color: sentColor, children: title }, i);
|
|
472
|
+
}) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Loading news\u2026" })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "Chain" }), _jsx(Text, { color: AIAIAI_COLORS.header, children: "\u26D3 solana (primary)" })] })] }), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: _jsx(REPL, { messages: messages, streamingText: streaming, toolRunning: toolRunning, onSubmit: handleSubmit, disabled: disabled, onAbort: () => {
|
|
473
|
+
runnerRef.current?.abort();
|
|
474
|
+
setDisabled(false);
|
|
475
|
+
setToolRunning(null);
|
|
476
|
+
setStreaming(prev => {
|
|
477
|
+
if (prev.trim())
|
|
478
|
+
push({ role: "assistant", content: prev + " β [interrupted]" });
|
|
479
|
+
return "";
|
|
480
|
+
});
|
|
481
|
+
push({ role: "system", content: T.dim("β stream interrupted β") });
|
|
482
|
+
} }) })] })] }));
|
|
483
|
+
}
|
|
484
|
+
//# sourceMappingURL=App.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ModelSelector β interactive model picker overlay.
|
|
3
|
+
*/
|
|
4
|
+
import React from "react";
|
|
5
|
+
interface ModelItem {
|
|
6
|
+
id: string;
|
|
7
|
+
tier: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ModelSelectorProps {
|
|
10
|
+
models: ModelItem[];
|
|
11
|
+
currentModelId: string;
|
|
12
|
+
onSelect: (id: string) => void;
|
|
13
|
+
onCancel: () => void;
|
|
14
|
+
initialQuery?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function ModelSelector({ models, currentModelId, onSelect, onCancel, initialQuery }: ModelSelectorProps): React.JSX.Element;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=ModelSelector.d.ts.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* ModelSelector β interactive model picker overlay.
|
|
4
|
+
*/
|
|
5
|
+
import { useState, useCallback, useEffect, useMemo } from "react";
|
|
6
|
+
import { Box, Text, useInput } from "ink";
|
|
7
|
+
import TextInput from "ink-text-input";
|
|
8
|
+
import { AIAIAI_COLORS } from "./theme.js";
|
|
9
|
+
const VISIBLE_COUNT = 10;
|
|
10
|
+
function fuzzyFilter(items, query) {
|
|
11
|
+
const q = query.toLowerCase().trim();
|
|
12
|
+
if (!q)
|
|
13
|
+
return items;
|
|
14
|
+
return items.filter((item) => {
|
|
15
|
+
const hay = `${item.id} ${item.tier}`.toLowerCase();
|
|
16
|
+
return hay.includes(q);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
export function ModelSelector({ models, currentModelId, onSelect, onCancel, initialQuery = "" }) {
|
|
20
|
+
const [query, setQuery] = useState(initialQuery);
|
|
21
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
22
|
+
const filtered = useMemo(() => fuzzyFilter(models, query), [models, query]);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
setSelectedIndex((prev) => Math.min(prev, Math.max(0, filtered.length - 1)));
|
|
25
|
+
}, [filtered.length]);
|
|
26
|
+
const handleSubmit = useCallback(() => {
|
|
27
|
+
const selected = filtered[selectedIndex];
|
|
28
|
+
if (selected)
|
|
29
|
+
onSelect(selected.id);
|
|
30
|
+
}, [filtered, selectedIndex, onSelect]);
|
|
31
|
+
useInput((input, key) => {
|
|
32
|
+
if (key.escape) {
|
|
33
|
+
onCancel();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (key.upArrow) {
|
|
37
|
+
setSelectedIndex(i => Math.max(0, i - 1));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (key.downArrow) {
|
|
41
|
+
setSelectedIndex(i => Math.min(filtered.length - 1, i + 1));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (key.return) {
|
|
45
|
+
handleSubmit();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
const start = Math.max(0, Math.min(selectedIndex - Math.floor(VISIBLE_COUNT / 2), filtered.length - VISIBLE_COUNT));
|
|
50
|
+
const visible = filtered.slice(start, start + VISIBLE_COUNT);
|
|
51
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.accent, paddingX: 1, children: [_jsxs(Text, { color: AIAIAI_COLORS.accent, bold: true, children: ["Select Model (", filtered.length, ")"] }), _jsxs(Box, { children: [_jsx(Text, { color: AIAIAI_COLORS.muted, children: "filter: " }), _jsx(TextInput, { value: query, onChange: setQuery, placeholder: "type to filter\u2026" })] }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [visible.map((m, i) => {
|
|
52
|
+
const realIdx = start + i;
|
|
53
|
+
const isSelected = realIdx === selectedIndex;
|
|
54
|
+
const isCurrent = m.id === currentModelId;
|
|
55
|
+
const tierIcon = { orchestrator: "β", analyst: "π¬", worker: "β‘", free: "π" }[m.tier] ?? "β’";
|
|
56
|
+
return (_jsxs(Text, { color: isSelected ? AIAIAI_COLORS.accent : AIAIAI_COLORS.muted, bold: isSelected, children: [isSelected ? "βΊ " : " ", tierIcon, " ", m.id, isCurrent ? " (current)" : ""] }, m.id));
|
|
57
|
+
}), filtered.length === 0 && _jsx(Text, { color: AIAIAI_COLORS.muted, children: "No models match." })] }), _jsx(Text, { color: AIAIAI_COLORS.dim, children: "\u2191\u2193 navigate \u00B7 Enter select \u00B7 Esc cancel" })] }));
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=ModelSelector.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REPL β scrolling message history + bottom input box.
|
|
3
|
+
*/
|
|
4
|
+
import React from "react";
|
|
5
|
+
export interface ChatMessage {
|
|
6
|
+
id: string;
|
|
7
|
+
role: "user" | "assistant" | "tool" | "system" | "notify";
|
|
8
|
+
content: string;
|
|
9
|
+
ts: number;
|
|
10
|
+
toolName?: string;
|
|
11
|
+
isError?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface REPLProps {
|
|
14
|
+
messages: ChatMessage[];
|
|
15
|
+
streamingText: string;
|
|
16
|
+
toolRunning: string | null;
|
|
17
|
+
onSubmit: (text: string) => void;
|
|
18
|
+
onAbort?: () => void;
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function REPL({ messages, streamingText, toolRunning, onSubmit, onAbort, disabled }: REPLProps): React.JSX.Element;
|
|
22
|
+
//# sourceMappingURL=REPL.d.ts.map
|
package/dist/tui/REPL.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* REPL β scrolling message history + bottom input box.
|
|
4
|
+
*/
|
|
5
|
+
import { useState, useCallback } from "react";
|
|
6
|
+
import { Box, Text, useStdout, useInput } from "ink";
|
|
7
|
+
import TextInput from "ink-text-input";
|
|
8
|
+
import { AIAIAI_COLORS } from "./theme.js";
|
|
9
|
+
const MAX_VISIBLE = 40;
|
|
10
|
+
function MessageLine({ msg }) {
|
|
11
|
+
const time = new Date(msg.ts).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
12
|
+
if (msg.role === "user") {
|
|
13
|
+
return (_jsxs(Box, { flexDirection: "row", gap: 1, marginTop: 1, children: [_jsx(Text, { color: AIAIAI_COLORS.muted, children: time }), _jsx(Text, { color: AIAIAI_COLORS.accent, bold: true, children: "you " }), _jsx(Text, { wrap: "wrap", children: msg.content })] }));
|
|
14
|
+
}
|
|
15
|
+
if (msg.role === "assistant") {
|
|
16
|
+
return (_jsxs(Box, { flexDirection: "row", gap: 1, marginTop: 1, children: [_jsx(Text, { color: AIAIAI_COLORS.muted, children: time }), _jsx(Text, { color: AIAIAI_COLORS.header, bold: true, children: "\uD83E\uDD16 " }), _jsx(Text, { wrap: "wrap", children: msg.content })] }));
|
|
17
|
+
}
|
|
18
|
+
if (msg.role === "tool") {
|
|
19
|
+
const icon = msg.isError ? "β" : "β";
|
|
20
|
+
const col = msg.isError ? AIAIAI_COLORS.error : AIAIAI_COLORS.success;
|
|
21
|
+
const isLong = msg.content.length > 120;
|
|
22
|
+
return (_jsxs(Box, { flexDirection: "column", marginY: 0, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: AIAIAI_COLORS.muted, children: time }), _jsxs(Text, { color: col, children: [icon, " ", msg.toolName ?? "tool"] }), isLong && _jsxs(Text, { color: AIAIAI_COLORS.dim, children: [" (", msg.content.length, " chars)"] })] }), !isLong && (_jsx(Box, { marginLeft: 6, children: _jsx(Text, { color: AIAIAI_COLORS.muted, wrap: "wrap", children: msg.content }) }))] }));
|
|
23
|
+
}
|
|
24
|
+
if (msg.role === "notify") {
|
|
25
|
+
return (_jsx(Box, { borderStyle: "round", borderColor: AIAIAI_COLORS.accent, marginY: 1, paddingX: 1, children: _jsx(Text, { wrap: "wrap", children: msg.content }) }));
|
|
26
|
+
}
|
|
27
|
+
// system messages β dimmed
|
|
28
|
+
return (_jsx(Box, { flexDirection: "row", gap: 1, children: _jsx(Text, { color: AIAIAI_COLORS.dim, wrap: "wrap", children: msg.content }) }));
|
|
29
|
+
}
|
|
30
|
+
export function REPL({ messages, streamingText, toolRunning, onSubmit, onAbort, disabled }) {
|
|
31
|
+
const [input, setInput] = useState("");
|
|
32
|
+
const { stdout } = useStdout();
|
|
33
|
+
const termWidth = stdout?.columns ?? 80;
|
|
34
|
+
const handleSubmit = useCallback((val) => {
|
|
35
|
+
const trimmed = val.trim();
|
|
36
|
+
if (!trimmed || disabled)
|
|
37
|
+
return;
|
|
38
|
+
setInput("");
|
|
39
|
+
onSubmit(trimmed);
|
|
40
|
+
}, [onSubmit, disabled]);
|
|
41
|
+
useInput((_input, key) => {
|
|
42
|
+
if (key.escape && disabled && onAbort)
|
|
43
|
+
onAbort();
|
|
44
|
+
});
|
|
45
|
+
const visible = messages.slice(-MAX_VISIBLE);
|
|
46
|
+
return (_jsxs(Box, { flexDirection: "column", width: termWidth, children: [_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [visible.map(m => _jsx(MessageLine, { msg: m }, m.id)), streamingText && (_jsxs(Box, { flexDirection: "row", gap: 1, marginTop: 1, children: [_jsx(Text, { color: AIAIAI_COLORS.muted, children: " " }), _jsx(Text, { color: AIAIAI_COLORS.header, bold: true, children: "\uD83E\uDD16 " }), _jsx(Text, { wrap: "wrap", children: streamingText })] })), toolRunning && (_jsx(Box, { flexDirection: "row", gap: 1, marginTop: 1, children: _jsxs(Text, { color: AIAIAI_COLORS.warn, children: ["\u2699 running ", toolRunning, "\u2026"] }) }))] }), _jsxs(Box, { borderStyle: "round", borderColor: disabled ? AIAIAI_COLORS.dim : AIAIAI_COLORS.accent, paddingX: 1, marginTop: 1, children: [_jsx(Text, { color: AIAIAI_COLORS.accent, children: "\u203A " }), _jsx(TextInput, { value: input, onChange: setInput, onSubmit: handleSubmit, placeholder: disabled ? "thinkingβ¦" : "message or /command" })] })] }));
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=REPL.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StatusBar β top line of the TUI showing agent state + live $AIAIAI price.
|
|
3
|
+
*/
|
|
4
|
+
import React from "react";
|
|
5
|
+
export interface StatusBarProps {
|
|
6
|
+
model: string;
|
|
7
|
+
chain: string;
|
|
8
|
+
price: string;
|
|
9
|
+
toolRunning: string | null;
|
|
10
|
+
connected: boolean;
|
|
11
|
+
statusLine?: string | null;
|
|
12
|
+
}
|
|
13
|
+
export declare function StatusBar({ model, chain, price, toolRunning, connected, statusLine }: StatusBarProps): React.JSX.Element;
|
|
14
|
+
//# sourceMappingURL=StatusBar.d.ts.map
|