@aiaiaichain/agent 0.1.4 → 0.1.6
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/dist/cli.js +224 -9
- package/dist/core/ChainConfig.js +1 -1
- package/dist/core/SystemMonitor.d.ts +1 -4
- package/dist/core/SystemMonitor.js +20 -46
- package/dist/index.d.ts +10 -0
- package/dist/index.js +11 -0
- 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.js +8 -3
- package/dist/tools/CrossTools.js +13 -5
- 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.js +145 -23
- package/dist/tui/REPL.js +2 -2
- package/dist/tui/Sparkline.d.ts +21 -0
- package/dist/tui/Sparkline.js +44 -0
- 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.d.ts +3 -2
- package/dist/wallet/ActionFeed.js +97 -76
- package/dist/wallet/AgentWallet.d.ts +6 -2
- package/dist/wallet/AgentWallet.js +59 -27
- package/dist/wallet/ProfitTracker.d.ts +30 -0
- package/dist/wallet/ProfitTracker.js +93 -0
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -15,6 +15,7 @@ import { loadExtension } from "./loader.js";
|
|
|
15
15
|
import { App } from "./tui/App.js";
|
|
16
16
|
import { T } from "./tui/theme.js";
|
|
17
17
|
import { wireNotify, safeLog } from "./util/safeLog.js";
|
|
18
|
+
import { logger } from "./util/logger.js";
|
|
18
19
|
import { modelRegistry } from "./models/ModelRegistry.js";
|
|
19
20
|
import { CostTracker } from "./models/CostTracker.js";
|
|
20
21
|
import { AgentRunner } from "./runner/AgentRunner.js";
|
|
@@ -22,10 +23,11 @@ import { SessionManager } from "./session/SessionManager.js";
|
|
|
22
23
|
import { makeTheme } from "./tui/theme.js";
|
|
23
24
|
import { AgentDir } from "./core/AgentDir.js";
|
|
24
25
|
import { priceFeed } from "./tools/PriceFeed.js";
|
|
25
|
-
import { agentWallet, DEPOSIT_WALLET, ACTION_WALLET,
|
|
26
|
+
import { agentWallet, DEPOSIT_WALLET, ACTION_WALLET, HOT_WALLET } from "./wallet/AgentWallet.js";
|
|
26
27
|
import { PROVIDERS, getConfigured, getUnconfigured } from "./providers/ProviderRegistry.js";
|
|
27
28
|
import { env } from "./core/EnvLoader.js";
|
|
28
29
|
import { gmgnHelp, gmgnStatus, saveGmgnApiKey } from "./tools/GmgnIntegration.js";
|
|
30
|
+
import { scanTokenSecurityTool } from "./tools/TokenSecurityScanner.js";
|
|
29
31
|
import { sessionStore } from "./session/SessionStore.js";
|
|
30
32
|
const AIAIAI_HOME = process.env.AIAIAI_HOME ?? join(homedir(), ".aiaiai");
|
|
31
33
|
const envPath = join(AIAIAI_HOME, ".env");
|
|
@@ -79,6 +81,10 @@ if (subcmd === "--help" || subcmd === "-h" || subcmd === "help") {
|
|
|
79
81
|
console.log(" aiaiaichain deposit Show deposit instructions");
|
|
80
82
|
console.log(" aiaiaichain burn Show burn instructions");
|
|
81
83
|
console.log(" aiaiaichain actions Recent agent actions");
|
|
84
|
+
console.log(" aiaiaichain pnl Profit & Loss report");
|
|
85
|
+
console.log(" aiaiaichain health System health & diagnostics");
|
|
86
|
+
console.log(" aiaiaichain doctor Comprehensive provider check");
|
|
87
|
+
console.log(" aiaiaichain scan <address> Token security scan");
|
|
82
88
|
console.log(" aiaiaichain --help This help");
|
|
83
89
|
console.log(" aiaiaichain --version Show version");
|
|
84
90
|
console.log("");
|
|
@@ -106,7 +112,22 @@ if (subcmd === "setup") {
|
|
|
106
112
|
console.log(T.muted(` Home: ${AIAIAI_HOME}`));
|
|
107
113
|
console.log(T.accent("\n Run `aiaiaichain` to start the agent.\n"));
|
|
108
114
|
process.exit(0);
|
|
109
|
-
})().catch(e => {
|
|
115
|
+
})().catch(e => {
|
|
116
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
117
|
+
if (msg.includes('ENOENT')) {
|
|
118
|
+
console.error(T.error(`Setup error: Cannot write to ~/.aiaiai/.env — check permissions.`));
|
|
119
|
+
}
|
|
120
|
+
else if (msg.includes('EACCES')) {
|
|
121
|
+
console.error(T.error(`Setup error: Permission denied — try running with correct user.`));
|
|
122
|
+
}
|
|
123
|
+
else if (msg.includes('API') || msg.includes('401')) {
|
|
124
|
+
console.error(T.error(`Setup error: API key invalid — check your provider key.`));
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
console.error(T.error(`Setup error: ${msg}`));
|
|
128
|
+
}
|
|
129
|
+
process.exit(1);
|
|
130
|
+
});
|
|
110
131
|
await new Promise(() => { });
|
|
111
132
|
}
|
|
112
133
|
if (subcmd === "config") {
|
|
@@ -179,17 +200,18 @@ if (subcmd === "burn") {
|
|
|
179
200
|
console.log(T.muted(" The agent detects deposits and automatically"));
|
|
180
201
|
console.log(T.muted(" sends funds to the action wallet to burn tokens."));
|
|
181
202
|
console.log("");
|
|
182
|
-
console.log(T.muted(" Action wallet:"));
|
|
203
|
+
console.log(T.muted(" Action wallet (executes trades):"));
|
|
183
204
|
console.log(` ${ACTION_WALLET}`);
|
|
184
205
|
console.log("");
|
|
185
|
-
console.log(T.muted("
|
|
186
|
-
console.log(` ${
|
|
206
|
+
console.log(T.muted(" Hot wallet (temporary swap):"));
|
|
207
|
+
console.log(` ${HOT_WALLET}`);
|
|
187
208
|
console.log("");
|
|
188
209
|
process.exit(0);
|
|
189
210
|
}
|
|
190
211
|
if (subcmd === "actions" || subcmd === "activity") {
|
|
191
212
|
(async () => {
|
|
192
213
|
const { actionFeed } = await import("./wallet/ActionFeed.js");
|
|
214
|
+
await actionFeed.refresh();
|
|
193
215
|
const result = await actionFeed.getActionsTool("", { limit: 20 });
|
|
194
216
|
console.log(result.content[0].text);
|
|
195
217
|
process.exit(0);
|
|
@@ -199,12 +221,46 @@ if (subcmd === "actions" || subcmd === "activity") {
|
|
|
199
221
|
if (subcmd === "fees") {
|
|
200
222
|
(async () => {
|
|
201
223
|
const { actionFeed } = await import("./wallet/ActionFeed.js");
|
|
224
|
+
await actionFeed.refresh();
|
|
202
225
|
const result = await actionFeed.getFeesTool();
|
|
203
226
|
console.log(result.content[0].text);
|
|
204
227
|
process.exit(0);
|
|
205
228
|
})();
|
|
206
229
|
await new Promise(() => { });
|
|
207
230
|
}
|
|
231
|
+
if (subcmd === "pnl" || subcmd === "pl" || subcmd === "profit") {
|
|
232
|
+
(async () => {
|
|
233
|
+
const { getPLSummary, getTradeHistory, exportCSV } = await import("./wallet/ProfitTracker.js");
|
|
234
|
+
const { priceFeed } = await import("./tools/PriceFeed.js");
|
|
235
|
+
const price = await priceFeed.getAiaiaiPrice();
|
|
236
|
+
const summary = getPLSummary(parseFloat(price.priceUsd ?? "0"));
|
|
237
|
+
const history = getTradeHistory();
|
|
238
|
+
console.log(T.accent("\n Profit & Loss — $AIAIAI\n"));
|
|
239
|
+
console.log(` Avg Cost: $${summary.avgCostBasis.toFixed(8)}`);
|
|
240
|
+
console.log(` Total Spent: ${summary.totalSpent.toFixed(4)} SOL`);
|
|
241
|
+
console.log(` Total Received: ${summary.totalReceived.toFixed(4)} SOL`);
|
|
242
|
+
console.log(` Realized P&L: ${summary.realizedPL >= 0 ? "+" : ""}${summary.realizedPL.toFixed(4)} SOL`);
|
|
243
|
+
console.log(` Unrealized P&L: ${summary.unrealizedPL >= 0 ? "+" : ""}${summary.unrealizedPL.toFixed(4)} SOL`);
|
|
244
|
+
console.log(` ROI: ${summary.roi >= 0 ? "+" : ""}${summary.roi.toFixed(2)}%`);
|
|
245
|
+
console.log(` Trades: ${history.length}`);
|
|
246
|
+
if (history.length > 0) {
|
|
247
|
+
console.log(T.muted("\n Recent trades:"));
|
|
248
|
+
history.slice(-5).forEach(t => {
|
|
249
|
+
const date = new Date(t.timestamp).toLocaleDateString();
|
|
250
|
+
console.log(` ${date} ${t.type} ${t.tokenAmount.toFixed(0)} tokens @ ${t.pricePerToken.toFixed(8)} SOL`);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
try {
|
|
254
|
+
const csvPath = exportCSV();
|
|
255
|
+
console.log(T.success(`\n CSV exported: ${csvPath}`));
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
logger.warn('CLI', 'CSV export failed', { error: error.message });
|
|
259
|
+
}
|
|
260
|
+
process.exit(0);
|
|
261
|
+
})();
|
|
262
|
+
await new Promise(() => { });
|
|
263
|
+
}
|
|
208
264
|
if (subcmd === "keys" || subcmd === "providers") {
|
|
209
265
|
(async () => {
|
|
210
266
|
const readline = await import("node:readline");
|
|
@@ -323,7 +379,9 @@ if (subcmd === "gmgn" || subcmd === "gmgnhelp") {
|
|
|
323
379
|
gmgnEnv[m[1]] = m[2];
|
|
324
380
|
}
|
|
325
381
|
}
|
|
326
|
-
catch {
|
|
382
|
+
catch {
|
|
383
|
+
logger.debug('CLI', 'No GMGN env file found (optional)');
|
|
384
|
+
}
|
|
327
385
|
const output = execSync(fullArgs.join(" "), {
|
|
328
386
|
env: { ...process.env, ...gmgnEnv },
|
|
329
387
|
encoding: "utf-8",
|
|
@@ -338,6 +396,25 @@ if (subcmd === "gmgn" || subcmd === "gmgnhelp") {
|
|
|
338
396
|
}
|
|
339
397
|
process.exit(0);
|
|
340
398
|
}
|
|
399
|
+
if (subcmd === "scan" || subcmd === "security") {
|
|
400
|
+
(async () => {
|
|
401
|
+
const tokenAddress = args[1];
|
|
402
|
+
if (!tokenAddress) {
|
|
403
|
+
console.log(T.error(" Usage: aiaiai scan <token-address>"));
|
|
404
|
+
process.exit(1);
|
|
405
|
+
}
|
|
406
|
+
console.log(T.accent("\n \uD83D\uDD0D Scanning token...\n"));
|
|
407
|
+
const result = await scanTokenSecurityTool("", { address: tokenAddress });
|
|
408
|
+
if (result.isError) {
|
|
409
|
+
console.log(result.content[0].text);
|
|
410
|
+
process.exit(1);
|
|
411
|
+
}
|
|
412
|
+
console.log(result.content[0].text);
|
|
413
|
+
console.log("");
|
|
414
|
+
process.exit(0);
|
|
415
|
+
})().catch(e => { console.error(T.error("Scan error: " + e.message)); process.exit(1); });
|
|
416
|
+
await new Promise(() => { });
|
|
417
|
+
}
|
|
341
418
|
if (subcmd === "update" || subcmd === "upgrade") {
|
|
342
419
|
(async () => {
|
|
343
420
|
const pkgName = "@aiaiaichain/agent";
|
|
@@ -368,9 +445,12 @@ if (subcmd === "update" || subcmd === "upgrade") {
|
|
|
368
445
|
changelog = `Git: ${pkgData.gitHead.slice(0, 8)}`;
|
|
369
446
|
}
|
|
370
447
|
}
|
|
371
|
-
catch {
|
|
448
|
+
catch {
|
|
449
|
+
logger.debug('CLI', 'No changelog available (optional)');
|
|
450
|
+
}
|
|
372
451
|
}
|
|
373
|
-
catch {
|
|
452
|
+
catch (error) {
|
|
453
|
+
logger.warn('CLI', 'Could not reach npm registry', { error: error.message });
|
|
374
454
|
console.log(T.error(" Could not reach npm registry. Check your connection."));
|
|
375
455
|
process.exit(1);
|
|
376
456
|
}
|
|
@@ -414,6 +494,119 @@ if (subcmd === "update" || subcmd === "upgrade") {
|
|
|
414
494
|
})().catch(e => { console.error(T.error(`Error: ${e.message}`)); process.exit(1); });
|
|
415
495
|
await new Promise(() => { });
|
|
416
496
|
}
|
|
497
|
+
if (subcmd === "health" || subcmd === "doctor") {
|
|
498
|
+
(async () => {
|
|
499
|
+
console.log(T.accent("\n \u2705 AIAIAI Health Check\n"));
|
|
500
|
+
let healthy = true;
|
|
501
|
+
// Check env file
|
|
502
|
+
console.log(T.muted(" Config:"));
|
|
503
|
+
if (existsSync(envPath)) {
|
|
504
|
+
console.log(T.success(" .env found"));
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
console.log(T.warn(" .env not found \u2014 run: aiaiai setup"));
|
|
508
|
+
healthy = false;
|
|
509
|
+
}
|
|
510
|
+
// Check API key
|
|
511
|
+
console.log(T.muted(" API Key:"));
|
|
512
|
+
if (process.env.OPENROUTER_API_KEY || env.get("OPENROUTER_API_KEY")) {
|
|
513
|
+
console.log(T.success(" OPENROUTER_API_KEY set"));
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
console.log(T.warn(" OPENROUTER_API_KEY not set"));
|
|
517
|
+
healthy = false;
|
|
518
|
+
}
|
|
519
|
+
// Check RPC
|
|
520
|
+
console.log(T.muted(" Solana RPC:"));
|
|
521
|
+
try {
|
|
522
|
+
const { resilientFetch } = await import("./util/resilientFetch.js");
|
|
523
|
+
const rpcUrl = process.env.SOLANA_RPC_URL || "https://api.mainnet-beta.solana.com";
|
|
524
|
+
const res = await resilientFetch(rpcUrl, {
|
|
525
|
+
timeout: 10_000,
|
|
526
|
+
retries: 0,
|
|
527
|
+
method: "POST",
|
|
528
|
+
headers: { "Content-Type": "application/json" },
|
|
529
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "getHealth", params: [] }),
|
|
530
|
+
});
|
|
531
|
+
const json = await res.json();
|
|
532
|
+
if (json.result === "ok") {
|
|
533
|
+
console.log(T.success(" RPC healthy: " + rpcUrl));
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
console.log(T.warn(" RPC responded but unhealthy"));
|
|
537
|
+
healthy = false;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
catch (e) {
|
|
541
|
+
console.log(T.error(" RPC unreachable: " + e.message));
|
|
542
|
+
healthy = false;
|
|
543
|
+
}
|
|
544
|
+
// Check disk writable
|
|
545
|
+
console.log(T.muted(" Disk:"));
|
|
546
|
+
try {
|
|
547
|
+
const { writeFileSync, unlinkSync } = await import("node:fs");
|
|
548
|
+
const testFile = join(AIAIAI_HOME, ".health-test");
|
|
549
|
+
writeFileSync(testFile, "test", "utf-8");
|
|
550
|
+
unlinkSync(testFile);
|
|
551
|
+
console.log(T.success(" writable"));
|
|
552
|
+
}
|
|
553
|
+
catch (error) {
|
|
554
|
+
logger.warn('CLI', 'Disk not writable', { error: error.message });
|
|
555
|
+
console.log(T.error(" not writable"));
|
|
556
|
+
healthy = false;
|
|
557
|
+
}
|
|
558
|
+
// ── Extras (doctor mode) ──────────────────────────────────────────
|
|
559
|
+
console.log(T.muted("\n Extras:"));
|
|
560
|
+
// Theme
|
|
561
|
+
try {
|
|
562
|
+
const { getCurrentTheme } = await import("./tui/ThemePresets.js");
|
|
563
|
+
console.log(T.success(` Theme: ${getCurrentTheme()} (run aiaiai to change)`));
|
|
564
|
+
}
|
|
565
|
+
catch {
|
|
566
|
+
console.log(T.muted(" Theme: default"));
|
|
567
|
+
}
|
|
568
|
+
// Clipboard
|
|
569
|
+
try {
|
|
570
|
+
const { clipboardSupported } = await import("./util/clipboard.js");
|
|
571
|
+
console.log(T.muted(` Clipboard: ${clipboardSupported()}`));
|
|
572
|
+
}
|
|
573
|
+
catch {
|
|
574
|
+
console.log(T.muted(" Clipboard: not available"));
|
|
575
|
+
}
|
|
576
|
+
// Webhooks
|
|
577
|
+
try {
|
|
578
|
+
const { hasWebhooks } = await import("./util/webhooks.js");
|
|
579
|
+
console.log(hasWebhooks() ? T.success(" Webhooks: configured") : T.muted(" Webhooks: not configured (set WEBHOOK_DISCORD or WEBHOOK_TELEGRAM in .env)"));
|
|
580
|
+
}
|
|
581
|
+
catch {
|
|
582
|
+
console.log(T.muted(" Webhooks: not available"));
|
|
583
|
+
}
|
|
584
|
+
// Provider key check
|
|
585
|
+
console.log(T.muted(" Provider Keys:"));
|
|
586
|
+
const providers = [
|
|
587
|
+
{ name: "OpenRouter", key: "OPENROUTER_API_KEY" },
|
|
588
|
+
{ name: "Anthropic", key: "ANTHROPIC_API_KEY" },
|
|
589
|
+
{ name: "OpenAI", key: "OPENAI_API_KEY" },
|
|
590
|
+
{ name: "Google", key: "GOOGLE_API_KEY" },
|
|
591
|
+
{ name: "DeepSeek", key: "DEEPSEEK_API_KEY" },
|
|
592
|
+
{ name: "Groq", key: "GROQ_API_KEY" },
|
|
593
|
+
{ name: "GMGN", key: "GMGN_API_KEY" },
|
|
594
|
+
];
|
|
595
|
+
for (const p of providers) {
|
|
596
|
+
const has = process.env[p.key] || env.get(p.key);
|
|
597
|
+
console.log(has ? T.success(` ${p.name}: ✓`) : T.dim(` ${p.name}: ✗ (optional)`));
|
|
598
|
+
}
|
|
599
|
+
console.log("");
|
|
600
|
+
if (healthy) {
|
|
601
|
+
console.log(T.success(" All systems operational.\n"));
|
|
602
|
+
}
|
|
603
|
+
else {
|
|
604
|
+
console.log(T.warn(" Some checks failed. Review above for details.\n"));
|
|
605
|
+
}
|
|
606
|
+
process.exit(healthy ? 0 : 1);
|
|
607
|
+
})().catch(e => { console.error(T.error("Health check failed: " + e.message)); process.exit(2); });
|
|
608
|
+
await new Promise(() => { });
|
|
609
|
+
}
|
|
417
610
|
if (subcmd === "sessions") {
|
|
418
611
|
const sessions = sessionStore.list();
|
|
419
612
|
if (sessions.length === 0) {
|
|
@@ -461,7 +654,29 @@ for (let i = 0; i < args.length; i++) {
|
|
|
461
654
|
}
|
|
462
655
|
let systemPrompt = SYSTEM_PROMPT;
|
|
463
656
|
if (promptPath && existsSync(promptPath)) {
|
|
464
|
-
|
|
657
|
+
let rawPrompt = readFileSync(promptPath, "utf-8");
|
|
658
|
+
// Validate prompt file size
|
|
659
|
+
if (rawPrompt.length > 10_000) {
|
|
660
|
+
console.error(T.error("Prompt file too large (max 10KB)."));
|
|
661
|
+
process.exit(1);
|
|
662
|
+
}
|
|
663
|
+
// Strip control characters
|
|
664
|
+
rawPrompt = rawPrompt.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
|
|
665
|
+
// Basic prompt injection detection
|
|
666
|
+
const dangerousPatterns = [
|
|
667
|
+
/ignore (all |previous |above|prior) instructions/i,
|
|
668
|
+
/you are now /i,
|
|
669
|
+
/new instructions/i,
|
|
670
|
+
/override (system|safety)/i,
|
|
671
|
+
];
|
|
672
|
+
for (const pattern of dangerousPatterns) {
|
|
673
|
+
if (pattern.test(rawPrompt)) {
|
|
674
|
+
console.error(T.error("Prompt file contains suspicious patterns. Use a simpler prompt."));
|
|
675
|
+
process.exit(1);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
systemPrompt = rawPrompt;
|
|
679
|
+
console.log(T.muted(" Custom prompt loaded: " + promptPath));
|
|
465
680
|
}
|
|
466
681
|
// ── Headless mode ─────────────────────────────────────────────────────────
|
|
467
682
|
const headlessIdx = args.indexOf("--headless");
|
package/dist/core/ChainConfig.js
CHANGED
|
@@ -7,7 +7,7 @@ export const CHAINS = {
|
|
|
7
7
|
name: 'solana',
|
|
8
8
|
symbol: 'SOL',
|
|
9
9
|
chainId: 101,
|
|
10
|
-
rpcUrl: 'https://api.mainnet-beta.solana.com',
|
|
10
|
+
rpcUrl: process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com',
|
|
11
11
|
explorerUrl: 'https://solscan.io',
|
|
12
12
|
enabled: true,
|
|
13
13
|
primary: true,
|
|
@@ -15,9 +15,7 @@ export interface SystemStats {
|
|
|
15
15
|
}
|
|
16
16
|
export declare class SystemMonitor {
|
|
17
17
|
private apiCallCount;
|
|
18
|
-
private
|
|
19
|
-
constructor();
|
|
20
|
-
private initNetStats;
|
|
18
|
+
private prevCpuTimes;
|
|
21
19
|
/** Track an API call */
|
|
22
20
|
trackApiCall(): void;
|
|
23
21
|
/** Get current system stats */
|
|
@@ -25,7 +23,6 @@ export declare class SystemMonitor {
|
|
|
25
23
|
private getCpuPercent;
|
|
26
24
|
private getRamPercent;
|
|
27
25
|
private getNetStats;
|
|
28
|
-
private getRawNetStats;
|
|
29
26
|
private formatBytes;
|
|
30
27
|
private formatUptime;
|
|
31
28
|
/** Reset API call counter (call every minute) */
|
|
@@ -2,20 +2,11 @@
|
|
|
2
2
|
* SystemMonitor — tracks CPU, RAM, network, and agent stats.
|
|
3
3
|
* Uses Node.js built-in modules only (no external deps).
|
|
4
4
|
*/
|
|
5
|
-
import { cpus, totalmem, freemem, uptime
|
|
5
|
+
import { cpus, totalmem, freemem, uptime } from 'node:os';
|
|
6
|
+
import { logger } from '../util/logger.js';
|
|
6
7
|
export class SystemMonitor {
|
|
7
8
|
apiCallCount = 0;
|
|
8
|
-
|
|
9
|
-
constructor() {
|
|
10
|
-
this.initNetStats();
|
|
11
|
-
}
|
|
12
|
-
initNetStats() {
|
|
13
|
-
try {
|
|
14
|
-
const stats = this.getRawNetStats();
|
|
15
|
-
this.lastNetStats = { rx: stats.rx, tx: stats.tx, time: Date.now() };
|
|
16
|
-
}
|
|
17
|
-
catch { /* non-fatal */ }
|
|
18
|
-
}
|
|
9
|
+
prevCpuTimes = { idle: 0, total: 0 };
|
|
19
10
|
/** Track an API call */
|
|
20
11
|
trackApiCall() {
|
|
21
12
|
this.apiCallCount++;
|
|
@@ -45,9 +36,21 @@ export class SystemMonitor {
|
|
|
45
36
|
}
|
|
46
37
|
const idle = totalIdle / cpuInfo.length;
|
|
47
38
|
const total = totalTick / cpuInfo.length;
|
|
48
|
-
|
|
39
|
+
// Delta-based: compare against previous sample
|
|
40
|
+
const nowIdle = idle, nowTotal = total;
|
|
41
|
+
if (this.prevCpuTimes.total > 0) {
|
|
42
|
+
const idleDelta = nowIdle - this.prevCpuTimes.idle;
|
|
43
|
+
const totalDelta = nowTotal - this.prevCpuTimes.total;
|
|
44
|
+
this.prevCpuTimes = { idle: nowIdle, total: nowTotal };
|
|
45
|
+
if (totalDelta <= 0)
|
|
46
|
+
return 0;
|
|
47
|
+
return Math.round((1 - idleDelta / totalDelta) * 100);
|
|
48
|
+
}
|
|
49
|
+
this.prevCpuTimes = { idle: nowIdle, total: nowTotal };
|
|
50
|
+
return 0;
|
|
49
51
|
}
|
|
50
|
-
catch {
|
|
52
|
+
catch (error) {
|
|
53
|
+
logger.debug('SystemMonitor', 'CPU measurement failed', { error: error.message });
|
|
51
54
|
return 0;
|
|
52
55
|
}
|
|
53
56
|
}
|
|
@@ -56,38 +59,9 @@ export class SystemMonitor {
|
|
|
56
59
|
return Math.round((used / totalmem()) * 100);
|
|
57
60
|
}
|
|
58
61
|
getNetStats() {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (elapsed < 1)
|
|
63
|
-
return { netUp: '0 B/s', netDown: '0 B/s' };
|
|
64
|
-
const rxPerSec = Math.max(0, (current.rx - this.lastNetStats.rx) / elapsed);
|
|
65
|
-
const txPerSec = Math.max(0, (current.tx - this.lastNetStats.tx) / elapsed);
|
|
66
|
-
this.lastNetStats = { rx: current.rx, tx: current.tx, time: Date.now() };
|
|
67
|
-
return {
|
|
68
|
-
netUp: this.formatBytes(txPerSec) + '/s',
|
|
69
|
-
netDown: this.formatBytes(rxPerSec) + '/s',
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
catch {
|
|
73
|
-
return { netUp: 'N/A', netDown: 'N/A' };
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
getRawNetStats() {
|
|
77
|
-
let rx = 0, tx = 0;
|
|
78
|
-
const interfaces = networkInterfaces();
|
|
79
|
-
for (const name of Object.keys(interfaces)) {
|
|
80
|
-
const iface = interfaces[name];
|
|
81
|
-
if (!iface)
|
|
82
|
-
continue;
|
|
83
|
-
for (const info of iface) {
|
|
84
|
-
if (info.internal)
|
|
85
|
-
continue;
|
|
86
|
-
// Node.js doesn't expose per-interface bytes by default
|
|
87
|
-
// This is a best-effort approximation
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return { rx, tx };
|
|
62
|
+
// Node.js doesn't expose per-interface byte counters without native addons.
|
|
63
|
+
// Return N/A rather than pretending zeros are real data.
|
|
64
|
+
return { netUp: 'N/A', netDown: 'N/A' };
|
|
91
65
|
}
|
|
92
66
|
formatBytes(bytes) {
|
|
93
67
|
if (bytes === 0)
|
package/dist/index.d.ts
CHANGED
|
@@ -48,4 +48,14 @@ export { AgentDir } from "./core/AgentDir.js";
|
|
|
48
48
|
export { EnvLoader, env } from "./core/EnvLoader.js";
|
|
49
49
|
export { CHAINS, getPrimaryChain, getEnabledChains, getChain, getSupportedChains } from "./core/ChainConfig.js";
|
|
50
50
|
export type { ChainInfo } from "./core/ChainConfig.js";
|
|
51
|
+
export { resilientFetch, isReachable } from "./util/resilientFetch.js";
|
|
52
|
+
export { logger } from "./util/logger.js";
|
|
53
|
+
export { getCached, setCache, getOrCompute, clearCache, cacheStats } from "./util/responseCache.js";
|
|
54
|
+
export { addTask, removeTask, pauseTask, resumeTask, pauseAll, resumeAll, startScheduler } from "./util/scheduler.js";
|
|
55
|
+
export { requireConfirmation, isNonInteractive } from "./util/confirmation.js";
|
|
56
|
+
export { ProcessManager } from "./util/processManager.js";
|
|
57
|
+
export { recordTrade, getPLSummary, getTradeHistory, exportCSV } from "./wallet/ProfitTracker.js";
|
|
58
|
+
export type { Trade, ProfitState, PLSummary } from "./wallet/ProfitTracker.js";
|
|
59
|
+
export { scanTokenSecurityTool } from "./tools/TokenSecurityScanner.js";
|
|
60
|
+
export type { TokenSecurityResult } from "./tools/TokenSecurityScanner.js";
|
|
51
61
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -50,4 +50,15 @@ export { MCPServer } from "./mcp/server.js";
|
|
|
50
50
|
export { AgentDir } from "./core/AgentDir.js";
|
|
51
51
|
export { EnvLoader, env } from "./core/EnvLoader.js";
|
|
52
52
|
export { CHAINS, getPrimaryChain, getEnabledChains, getChain, getSupportedChains } from "./core/ChainConfig.js";
|
|
53
|
+
// Utilities
|
|
54
|
+
export { resilientFetch, isReachable } from "./util/resilientFetch.js";
|
|
55
|
+
export { logger } from "./util/logger.js";
|
|
56
|
+
export { getCached, setCache, getOrCompute, clearCache, cacheStats } from "./util/responseCache.js";
|
|
57
|
+
export { addTask, removeTask, pauseTask, resumeTask, pauseAll, resumeAll, startScheduler } from "./util/scheduler.js";
|
|
58
|
+
export { requireConfirmation, isNonInteractive } from "./util/confirmation.js";
|
|
59
|
+
export { ProcessManager } from "./util/processManager.js";
|
|
60
|
+
// Profit Tracker
|
|
61
|
+
export { recordTrade, getPLSummary, getTradeHistory, exportCSV } from "./wallet/ProfitTracker.js";
|
|
62
|
+
// Token Security
|
|
63
|
+
export { scanTokenSecurityTool } from "./tools/TokenSecurityScanner.js";
|
|
53
64
|
//# sourceMappingURL=index.js.map
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { env } from "../core/EnvLoader.js";
|
|
6
6
|
import { Type } from "@sinclair/typebox";
|
|
7
|
+
import { logger } from "../util/logger.js";
|
|
8
|
+
import { resilientFetch } from "../util/resilientFetch.js";
|
|
7
9
|
const PROVIDERS_WITH_MODELS = [
|
|
8
10
|
{ id: "openrouter", envVar: "OPENROUTER_API_KEY", baseUrl: "https://openrouter.ai/api/v1/models", mapper: (m) => ({ id: m.id, name: m.name, context: m.context_length ?? 131072, max: m.top_provider?.max_completion_tokens ?? 4096 }) },
|
|
9
11
|
{ id: "anthropic", envVar: "ANTHROPIC_API_KEY", baseUrl: "https://api.anthropic.com/v1/models", mapper: (m) => ({ id: m.id, name: m.name, context: m.context_length ?? 200000, max: m.top_provider?.max_completion_tokens ?? 8192 }) },
|
|
@@ -56,9 +58,11 @@ export class ModelRegistry {
|
|
|
56
58
|
if (provider.id === "google") {
|
|
57
59
|
url = `${provider.baseUrl}?key=${apiKey}`;
|
|
58
60
|
}
|
|
59
|
-
const res = await
|
|
60
|
-
if (!res.ok)
|
|
61
|
+
const res = await resilientFetch(url, { timeout: 15_000, retries: 1, headers });
|
|
62
|
+
if (!res.ok) {
|
|
63
|
+
logger.warn('ModelRegistry', `Provider ${provider.id} returned ${res.status}`);
|
|
61
64
|
continue;
|
|
65
|
+
}
|
|
62
66
|
const data = await res.json();
|
|
63
67
|
const items = data.data ?? data.models ?? data;
|
|
64
68
|
if (!Array.isArray(items))
|
|
@@ -80,10 +84,14 @@ export class ModelRegistry {
|
|
|
80
84
|
this.providerModels.set(provider.id, []);
|
|
81
85
|
this.providerModels.get(provider.id).push(model);
|
|
82
86
|
}
|
|
83
|
-
catch {
|
|
87
|
+
catch (error) {
|
|
88
|
+
logger.debug('ModelRegistry', 'Skipped bad model entry', { error: error.message });
|
|
89
|
+
}
|
|
84
90
|
}
|
|
85
91
|
}
|
|
86
|
-
catch {
|
|
92
|
+
catch (error) {
|
|
93
|
+
logger.warn('ModelRegistry', 'Skipped provider', { error: error.message });
|
|
94
|
+
}
|
|
87
95
|
}
|
|
88
96
|
this.buildTiers();
|
|
89
97
|
this.initialized = true;
|
|
@@ -44,6 +44,8 @@ export declare class AgentRunner {
|
|
|
44
44
|
private contextStore?;
|
|
45
45
|
private abortController;
|
|
46
46
|
private _pendingApproval?;
|
|
47
|
+
private _toolRetries;
|
|
48
|
+
private _maxToolRetries;
|
|
47
49
|
constructor(registry: Registry, session: SessionManager, onEvent: (event: RunnerEvent) => void, sessionCtx: SessionContext, effectLevel: string, modelReg: ModelRegistry, costTracker?: CostTracker, goalManager?: GoalManager, contextStore?: ContextStore);
|
|
48
50
|
setEffectLevel(level: string): void;
|
|
49
51
|
run(input: string): Promise<void>;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* AgentRunner — main agent loop. Handles tool calling, streaming, and message processing.
|
|
3
3
|
* The AI responds with text or tool calls; we parse, dispatch, and feed results back.
|
|
4
4
|
*/
|
|
5
|
+
import { logger } from "../util/logger.js";
|
|
5
6
|
import { ToolDispatcher } from "./ToolDispatcher.js";
|
|
6
7
|
import { ModelClient, resolveModelConfig } from "./ModelClient.js";
|
|
7
8
|
export class AgentRunner {
|
|
@@ -17,6 +18,8 @@ export class AgentRunner {
|
|
|
17
18
|
contextStore;
|
|
18
19
|
abortController = null;
|
|
19
20
|
_pendingApproval;
|
|
21
|
+
_toolRetries = new Map();
|
|
22
|
+
_maxToolRetries = 2;
|
|
20
23
|
constructor(registry, session, onEvent, sessionCtx, effectLevel, modelReg, costTracker, goalManager, contextStore) {
|
|
21
24
|
this.registry = registry;
|
|
22
25
|
this.session = session;
|
|
@@ -53,7 +56,9 @@ export class AgentRunner {
|
|
|
53
56
|
try {
|
|
54
57
|
args = JSON.parse(toolCallMatch[3]);
|
|
55
58
|
}
|
|
56
|
-
catch {
|
|
59
|
+
catch {
|
|
60
|
+
logger.warn('AgentRunner', 'Failed to parse tool call args from stream', { toolCall: toolCallMatch[0].slice(0, 100) });
|
|
61
|
+
}
|
|
57
62
|
fullResponse = fullResponse.replace(toolCallMatch[0], "").trim();
|
|
58
63
|
toolCallBuffer = "";
|
|
59
64
|
if (toolName !== "noop") {
|
|
@@ -130,6 +135,18 @@ export class AgentRunner {
|
|
|
130
135
|
result: resultText,
|
|
131
136
|
isError: result.isError ?? false,
|
|
132
137
|
});
|
|
138
|
+
// Break retry loop: don't feed back if tool has failed too many times
|
|
139
|
+
if (result.isError) {
|
|
140
|
+
const retries = (this._toolRetries.get(toolName) ?? 0) + 1;
|
|
141
|
+
this._toolRetries.set(toolName, retries);
|
|
142
|
+
if (retries > this._maxToolRetries) {
|
|
143
|
+
this._toolRetries.delete(toolName);
|
|
144
|
+
return; // Stop feeding errors — prevent loop
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
this._toolRetries.delete(toolName);
|
|
149
|
+
}
|
|
133
150
|
// Feed the result back to the model for a follow-up
|
|
134
151
|
const toolMessages = [
|
|
135
152
|
...messages.slice(-5),
|