@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
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CrossTools — automation tools that combine multiple data sources.
|
|
3
|
+
* Watch tokens, compare, portfolio tracking, price alerts.
|
|
4
|
+
*/
|
|
5
|
+
import { Type } from "@sinclair/typebox";
|
|
6
|
+
import { priceFeed } from "../tools/PriceFeed.js";
|
|
7
|
+
import { AIAIAI_TOKEN, agentWallet } from "../wallet/AgentWallet.js";
|
|
8
|
+
const alerts = [];
|
|
9
|
+
export function addAlert(token, type, price) {
|
|
10
|
+
const alert = {
|
|
11
|
+
id: `alert-${Date.now()}-${Math.random().toString(36).slice(2, 5)}`,
|
|
12
|
+
token,
|
|
13
|
+
type,
|
|
14
|
+
price,
|
|
15
|
+
active: true,
|
|
16
|
+
createdAt: Date.now(),
|
|
17
|
+
};
|
|
18
|
+
alerts.push(alert);
|
|
19
|
+
return alert;
|
|
20
|
+
}
|
|
21
|
+
export function checkAlerts(currentPrice, token) {
|
|
22
|
+
const triggered = [];
|
|
23
|
+
for (const alert of alerts) {
|
|
24
|
+
if (!alert.active || alert.token !== token)
|
|
25
|
+
continue;
|
|
26
|
+
if (alert.type === 'above' && currentPrice >= alert.price) {
|
|
27
|
+
alert.active = false;
|
|
28
|
+
alert.triggeredAt = Date.now();
|
|
29
|
+
triggered.push(alert);
|
|
30
|
+
}
|
|
31
|
+
else if (alert.type === 'below' && currentPrice <= alert.price) {
|
|
32
|
+
alert.active = false;
|
|
33
|
+
alert.triggeredAt = Date.now();
|
|
34
|
+
triggered.push(alert);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return triggered;
|
|
38
|
+
}
|
|
39
|
+
export function getActiveAlerts() {
|
|
40
|
+
return alerts.filter(a => a.active);
|
|
41
|
+
}
|
|
42
|
+
// ── Watch List ───────────────────────────────────────────────────────────────
|
|
43
|
+
const watchList = new Set();
|
|
44
|
+
export function addToWatchList(tokenAddress) {
|
|
45
|
+
watchList.add(tokenAddress);
|
|
46
|
+
}
|
|
47
|
+
export function removeFromWatchList(tokenAddress) {
|
|
48
|
+
watchList.delete(tokenAddress);
|
|
49
|
+
}
|
|
50
|
+
export function getWatchList() {
|
|
51
|
+
return [...watchList];
|
|
52
|
+
}
|
|
53
|
+
// ── Agent Tools ──────────────────────────────────────────────────────────────
|
|
54
|
+
export const watchTokenParams = Type.Object({
|
|
55
|
+
address: Type.String({ description: "Token contract address to watch" }),
|
|
56
|
+
});
|
|
57
|
+
export const removeWatchParams = Type.Object({
|
|
58
|
+
address: Type.String({ description: "Token contract address to remove" }),
|
|
59
|
+
});
|
|
60
|
+
export const listWatchParams = Type.Object({});
|
|
61
|
+
export const addAlertParams = Type.Object({
|
|
62
|
+
token: Type.String({ description: "Token address or 'AIAIAI'" }),
|
|
63
|
+
type: Type.String({ description: "Alert type: above or below" }),
|
|
64
|
+
price: Type.Number({ description: "Price threshold in USD" }),
|
|
65
|
+
});
|
|
66
|
+
export const checkAlertsParams = Type.Object({});
|
|
67
|
+
export const compareTokensParams = Type.Object({
|
|
68
|
+
address1: Type.String({ description: "First token address" }),
|
|
69
|
+
address2: Type.String({ description: "Second token address" }),
|
|
70
|
+
});
|
|
71
|
+
export async function watchTokenTool(_id, params) {
|
|
72
|
+
const addr = params.address;
|
|
73
|
+
addToWatchList(addr);
|
|
74
|
+
return { content: [{ type: "text", text: `👁️ Watching ${addr.slice(0, 8)}…${addr.slice(-6)}. You'll get alerts on significant moves.` }] };
|
|
75
|
+
}
|
|
76
|
+
export async function removeWatchTool(_id, params) {
|
|
77
|
+
const addr = params.address;
|
|
78
|
+
removeFromWatchList(addr);
|
|
79
|
+
return { content: [{ type: "text", text: `Removed ${addr.slice(0, 8)}… from watch list.` }] };
|
|
80
|
+
}
|
|
81
|
+
export async function listWatchTool() {
|
|
82
|
+
const list = getWatchList();
|
|
83
|
+
if (list.length === 0)
|
|
84
|
+
return { content: [{ type: "text", text: "Watch list is empty." }] };
|
|
85
|
+
const lines = list.map((addr, i) => ` ${i + 1}. ${addr.slice(0, 8)}…${addr.slice(-6)}`);
|
|
86
|
+
return { content: [{ type: "text", text: `Watch List (${list.length}):\n${lines.join("\n")}` }] };
|
|
87
|
+
}
|
|
88
|
+
export async function addAlertTool(_id, params) {
|
|
89
|
+
const token = params.token;
|
|
90
|
+
const type = params.type;
|
|
91
|
+
const price = params.price;
|
|
92
|
+
const alert = addAlert(token, type, price);
|
|
93
|
+
return { content: [{ type: "text", text: `🔔 Alert set: ${type} $${price} for ${token.slice(0, 8)}… (${alert.id})` }] };
|
|
94
|
+
}
|
|
95
|
+
export async function checkAlertsTool() {
|
|
96
|
+
const active = getActiveAlerts();
|
|
97
|
+
if (active.length === 0)
|
|
98
|
+
return { content: [{ type: "text", text: "No active alerts." }] };
|
|
99
|
+
const lines = active.map(a => ` ${a.id} — ${a.type} $${a.price} (${a.token.slice(0, 8)}…)`);
|
|
100
|
+
return { content: [{ type: "text", text: `Active Alerts (${active.length}):\n${lines.join("\n")}` }] };
|
|
101
|
+
}
|
|
102
|
+
export async function compareTokensTool(_id, params) {
|
|
103
|
+
const addr1 = params.address1;
|
|
104
|
+
const addr2 = params.address2;
|
|
105
|
+
let price1 = null, price2 = null;
|
|
106
|
+
try {
|
|
107
|
+
if (addr1 === AIAIAI_TOKEN || addr1.toLowerCase() === 'aiaiai') {
|
|
108
|
+
price1 = await priceFeed.getAiaiaiPrice();
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
price1 = await priceFeed.fetchToken(addr1);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
price1 = null;
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
if (addr2 === AIAIAI_TOKEN || addr2.toLowerCase() === 'aiaiai') {
|
|
119
|
+
price2 = await priceFeed.getAiaiaiPrice();
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
price2 = await priceFeed.fetchToken(addr2);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
price2 = null;
|
|
127
|
+
}
|
|
128
|
+
const lines = [
|
|
129
|
+
'── Token Comparison ──────────────────────────',
|
|
130
|
+
'',
|
|
131
|
+
`Token 1: ${addr1.slice(0, 8)}…`,
|
|
132
|
+
price1 ? ` Price: $${price1.priceUsd ?? 'N/A'}` : ' Price: N/A',
|
|
133
|
+
price1 ? ` 24h: ${price1.priceChange24h > 0 ? '+' : ''}${price1.priceChange24h.toFixed(2)}%` : ' 24h: N/A',
|
|
134
|
+
price1 ? ` Liq: $${(price1.liquidityUsd / 1000).toFixed(1)}k` : ' Liq: N/A',
|
|
135
|
+
price1 ? ` MCap: $${price1.marketCap ? (price1.marketCap / 1000).toFixed(1) + 'k' : 'N/A'}` : ' MCap: N/A',
|
|
136
|
+
'',
|
|
137
|
+
`Token 2: ${addr2.slice(0, 8)}…`,
|
|
138
|
+
price2 ? ` Price: $${price2.priceUsd ?? 'N/A'}` : ' Price: N/A',
|
|
139
|
+
price2 ? ` 24h: ${price2.priceChange24h > 0 ? '+' : ''}${price2.priceChange24h.toFixed(2)}%` : ' 24h: N/A',
|
|
140
|
+
price2 ? ` Liq: $${(price2.liquidityUsd / 1000).toFixed(1)}k` : ' Liq: N/A',
|
|
141
|
+
price2 ? ` MCap: $${price2.marketCap ? (price2.marketCap / 1000).toFixed(1) + 'k' : 'N/A'}` : ' MCap: N/A',
|
|
142
|
+
];
|
|
143
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
144
|
+
}
|
|
145
|
+
// ── Portfolio tracking ─────────────────────────────────────────────────────────────
|
|
146
|
+
export const portfolioParams = Type.Object({
|
|
147
|
+
address: Type.String({ description: "Wallet address to analyze" }),
|
|
148
|
+
});
|
|
149
|
+
export async function portfolioTool(_id, params) {
|
|
150
|
+
const address = params.address;
|
|
151
|
+
// Use agentWallet's cached balances if it's one of our wallets
|
|
152
|
+
let result = null;
|
|
153
|
+
try {
|
|
154
|
+
const all = await agentWallet.getAll();
|
|
155
|
+
if (address === COLD_WALLET)
|
|
156
|
+
result = all.cold;
|
|
157
|
+
else if (address === ACTION_WALLET)
|
|
158
|
+
result = all.action;
|
|
159
|
+
else if (address === DEPOSIT_WALLET)
|
|
160
|
+
result = all.deposit;
|
|
161
|
+
}
|
|
162
|
+
catch { }
|
|
163
|
+
if (result) {
|
|
164
|
+
const lines = [
|
|
165
|
+
`📊 Portfolio: ${address.slice(0, 8)}…${address.slice(-6)}`,
|
|
166
|
+
'',
|
|
167
|
+
` SOL: ${result.sol.toFixed(4)} ($${(result.sol * 150).toFixed(2)} est)`,
|
|
168
|
+
` $AIAIAI: ${result.aiaiai.toLocaleString(undefined, { maximumFractionDigits: 0 })}`,
|
|
169
|
+
` USDC: ${result.usdc.toFixed(2)}`,
|
|
170
|
+
'',
|
|
171
|
+
` Total value (est): $${(result.sol * 150 + result.usdc).toFixed(2)} + $AIAIAI`,
|
|
172
|
+
];
|
|
173
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
content: [{ type: "text", text: `📊 Analyzed: ${address.slice(0, 8)}…${address.slice(-6)}\nUse /wallet or /deposit for tracked wallet balances.` }],
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
export const COLD_WALLET = "A11iZoqEt6hU7HyggqC67ee4AtYmaJjwKCvJLerJRV2J";
|
|
180
|
+
export const ACTION_WALLET = "BygDYM1ZXLQNC1HXLhnd1rHZ7E5XjioqT3vPjJFfjnU2";
|
|
181
|
+
export const DEPOSIT_WALLET = "FBMDYpG9WXKy4SgxuATQdB2sCyzHsJWPrEr45z3TgL2e";
|
|
182
|
+
//# sourceMappingURL=CrossTools.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GMGN integration — token research, smart money tracking, market data.
|
|
3
|
+
* Auto-manages GMGN_API_KEY. Provides tools for the agent.
|
|
4
|
+
*
|
|
5
|
+
* Token usage optimization:
|
|
6
|
+
* - Cache token info for 60s
|
|
7
|
+
* - Cache trending/trenches for 120s
|
|
8
|
+
* - Cache kline for 30s
|
|
9
|
+
* - Track commands use weight 3, so we batch when possible
|
|
10
|
+
*/
|
|
11
|
+
import type { ToolResult } from "../api/ExtensionAPI.js";
|
|
12
|
+
export declare function getGmgnApiKey(): string | undefined;
|
|
13
|
+
export declare function hasGmgnApiKey(): boolean;
|
|
14
|
+
export declare function saveGmgnApiKey(key: string): void;
|
|
15
|
+
export declare const GMGN_SUBCOMMANDS: {
|
|
16
|
+
name: string;
|
|
17
|
+
desc: string;
|
|
18
|
+
usage: string;
|
|
19
|
+
}[];
|
|
20
|
+
export declare const gmgnHelp: () => string;
|
|
21
|
+
export declare const gmgnStatus: () => string;
|
|
22
|
+
export declare const gmgnToolParams: import("@sinclair/typebox").TObject<{
|
|
23
|
+
subcommand: import("@sinclair/typebox").TString;
|
|
24
|
+
chain: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
25
|
+
address: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
26
|
+
wallet: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
27
|
+
limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
28
|
+
resolution: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
29
|
+
interval: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
30
|
+
type: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
31
|
+
}>;
|
|
32
|
+
export declare function gmgnTool(_id: string, params: Record<string, unknown>): Promise<ToolResult>;
|
|
33
|
+
export declare const gmgnMarketToolParams: import("@sinclair/typebox").TObject<{
|
|
34
|
+
query: import("@sinclair/typebox").TString;
|
|
35
|
+
chain: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
36
|
+
}>;
|
|
37
|
+
export declare function gmgnMarketTool(_id: string, params: Record<string, unknown>): Promise<ToolResult>;
|
|
38
|
+
//# sourceMappingURL=GmgnIntegration.d.ts.map
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GMGN integration — token research, smart money tracking, market data.
|
|
3
|
+
* Auto-manages GMGN_API_KEY. Provides tools for the agent.
|
|
4
|
+
*
|
|
5
|
+
* Token usage optimization:
|
|
6
|
+
* - Cache token info for 60s
|
|
7
|
+
* - Cache trending/trenches for 120s
|
|
8
|
+
* - Cache kline for 30s
|
|
9
|
+
* - Track commands use weight 3, so we batch when possible
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync } from "node:fs";
|
|
12
|
+
import { resolve, join } from "node:path";
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
import { execSync } from "node:child_process";
|
|
15
|
+
import { Type } from "@sinclair/typebox";
|
|
16
|
+
const GMGN_ENV_DIR = resolve(homedir(), ".config", "gmgn");
|
|
17
|
+
const GMGN_ENV_PATH = join(GMGN_ENV_DIR, ".env");
|
|
18
|
+
// ── Env management ──────────────────────────────────────────────────────────
|
|
19
|
+
function ensureGmgnEnv() {
|
|
20
|
+
if (!existsSync(GMGN_ENV_DIR))
|
|
21
|
+
mkdirSync(GMGN_ENV_DIR, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
function readGmgnEnv() {
|
|
24
|
+
const env = {};
|
|
25
|
+
if (existsSync(GMGN_ENV_PATH)) {
|
|
26
|
+
const content = readFileSync(GMGN_ENV_PATH, "utf-8");
|
|
27
|
+
for (const line of content.split("\n")) {
|
|
28
|
+
const trimmed = line.trim();
|
|
29
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
30
|
+
continue;
|
|
31
|
+
const eqIdx = trimmed.indexOf("=");
|
|
32
|
+
if (eqIdx === -1)
|
|
33
|
+
continue;
|
|
34
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
35
|
+
const value = trimmed.slice(eqIdx + 1).trim();
|
|
36
|
+
if (key)
|
|
37
|
+
env[key] = value;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
for (const key of Object.keys(process.env)) {
|
|
41
|
+
const val = process.env[key];
|
|
42
|
+
if (val)
|
|
43
|
+
env[key] = val;
|
|
44
|
+
}
|
|
45
|
+
return env;
|
|
46
|
+
}
|
|
47
|
+
function writeGmgnKey(key, value) {
|
|
48
|
+
ensureGmgnEnv();
|
|
49
|
+
const env = readGmgnEnv();
|
|
50
|
+
env[key] = value;
|
|
51
|
+
const lines = Object.entries(env).map(([k, v]) => `${k}=${v}`);
|
|
52
|
+
writeFileSync(GMGN_ENV_PATH, lines.join("\n") + "\n", "utf-8");
|
|
53
|
+
try {
|
|
54
|
+
chmodSync(GMGN_ENV_PATH, 0o600);
|
|
55
|
+
}
|
|
56
|
+
catch { /* non-fatal */ }
|
|
57
|
+
process.env[key] = value;
|
|
58
|
+
}
|
|
59
|
+
export function getGmgnApiKey() {
|
|
60
|
+
return readGmgnEnv().GMGN_API_KEY;
|
|
61
|
+
}
|
|
62
|
+
export function hasGmgnApiKey() {
|
|
63
|
+
const key = getGmgnApiKey();
|
|
64
|
+
return !!key && key.trim() !== "";
|
|
65
|
+
}
|
|
66
|
+
export function saveGmgnApiKey(key) {
|
|
67
|
+
writeGmgnKey("GMGN_API_KEY", key);
|
|
68
|
+
}
|
|
69
|
+
function gmgnEnv() {
|
|
70
|
+
return { ...process.env, ...readGmgnEnv() };
|
|
71
|
+
}
|
|
72
|
+
function gmgnAvailable() {
|
|
73
|
+
try {
|
|
74
|
+
execSync("which gmgn-cli", { stdio: "ignore" });
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// ── Sub-command definitions ─────────────────────────────────────────────────
|
|
82
|
+
export const GMGN_SUBCOMMANDS = [
|
|
83
|
+
{ name: "token info", desc: "Token price, market cap, liquidity, holders, socials", usage: "gmgn token info --chain <sol|bsc|base|eth> --address <address>" },
|
|
84
|
+
{ name: "token security", desc: "Honeypot, taxes, rug ratio, contract risks", usage: "gmgn token security --chain <chain> --address <address>" },
|
|
85
|
+
{ name: "token pool", desc: "Liquidity pool reserves, DEX, depth", usage: "gmgn token pool --chain <chain> --address <address>" },
|
|
86
|
+
{ name: "token holders", desc: "Top holders with P&L breakdown", usage: "gmgn token holders --chain <chain> --address <address>" },
|
|
87
|
+
{ name: "token traders", desc: "Top traders with P&L breakdown", usage: "gmgn token traders --chain <chain> --address <address>" },
|
|
88
|
+
{ name: "track follow-tokens", desc: "Tokens a wallet follows on GMGN", usage: "gmgn track follow-tokens --chain <chain> --wallet <address>" },
|
|
89
|
+
{ name: "track follow-wallet", desc: "Trades from wallets you follow", usage: "gmgn track follow-wallet --chain <chain>" },
|
|
90
|
+
{ name: "track kol", desc: "KOL/influencer trade activity", usage: "gmgn track kol --chain <chain>" },
|
|
91
|
+
{ name: "track smartmoney", desc: "Smart money/whale trade activity", usage: "gmgn track smartmoney --chain <chain>" },
|
|
92
|
+
{ name: "market kline", desc: "Token candlestick / OHLCV chart data", usage: "gmgn market kline --chain <chain> --address <address> --resolution <1m|5m|15m|1h|4h|1d>" },
|
|
93
|
+
{ name: "market trending", desc: "Trending tokens ranked by swaps", usage: "gmgn market trending --chain <chain> --interval <1m|5m|1h|6h|24h>" },
|
|
94
|
+
{ name: "market trenches", desc: "Newly launched tokens (new/near/completed)", usage: "gmgn market trenches --chain <chain> --type <new_creation|near_completion|completed>" },
|
|
95
|
+
{ name: "market signal", desc: "Real-time token signals (sol/bsc only)", usage: "gmgn market signal --chain <sol|bsc>" },
|
|
96
|
+
];
|
|
97
|
+
export const gmgnHelp = () => {
|
|
98
|
+
const groups = [
|
|
99
|
+
{ title: "── Token Research ──────────────────────────────────", filter: (s) => s.startsWith("token") },
|
|
100
|
+
{ title: "── Smart Money Tracking ───────────────────────────", filter: (s) => s.startsWith("track") },
|
|
101
|
+
{ title: "── Market Data ────────────────────────────────────", filter: (s) => s.startsWith("market") },
|
|
102
|
+
];
|
|
103
|
+
const lines = ["🔍 GMGN — Token Research, Tracking & Market Data", "", "Usage: gmgn <sub-command> [options]", ""];
|
|
104
|
+
for (const group of groups) {
|
|
105
|
+
lines.push(group.title);
|
|
106
|
+
for (const cmd of GMGN_SUBCOMMANDS.filter(c => group.filter(c.name))) {
|
|
107
|
+
lines.push(` ${cmd.name.padEnd(20)} ${cmd.desc}`);
|
|
108
|
+
lines.push(` ${cmd.usage}`);
|
|
109
|
+
}
|
|
110
|
+
lines.push("");
|
|
111
|
+
}
|
|
112
|
+
lines.push("── Setup ─────────────────────────────────────────");
|
|
113
|
+
lines.push(" gmgn setup Configure GMGN_API_KEY");
|
|
114
|
+
lines.push(" gmgn status Check API key + CLI status");
|
|
115
|
+
lines.push("");
|
|
116
|
+
if (!gmgnAvailable())
|
|
117
|
+
lines.push(" ⚠️ gmgn-cli not installed. Run: npm install -g gmgn-cli");
|
|
118
|
+
if (!hasGmgnApiKey())
|
|
119
|
+
lines.push(" ⚠️ GMGN_API_KEY not set. Run: gmgn setup");
|
|
120
|
+
return lines.join("\n");
|
|
121
|
+
};
|
|
122
|
+
export const gmgnStatus = () => {
|
|
123
|
+
return [
|
|
124
|
+
"🔍 GMGN Status",
|
|
125
|
+
"",
|
|
126
|
+
`CLI installed: ${gmgnAvailable() ? "✅ yes" : "❌ no (npm install -g gmgn-cli)"}`,
|
|
127
|
+
`API key configured: ${hasGmgnApiKey() ? "✅ yes" : "❌ no (gmgn setup)"}`,
|
|
128
|
+
hasGmgnApiKey() ? `Key: ${getGmgnApiKey().slice(0, 6)}••••${getGmgnApiKey().slice(-4)}` : "",
|
|
129
|
+
].filter(Boolean).join("\n");
|
|
130
|
+
};
|
|
131
|
+
// ── Agent Tool ───────────────────────────────────────────────────────────────
|
|
132
|
+
export const gmgnToolParams = Type.Object({
|
|
133
|
+
subcommand: Type.String({ description: "GMGN sub-command: token info, token security, token holders, token traders, track kol, track smartmoney, track follow-wallet, track follow-tokens, market kline, market trending, market trenches, market signal" }),
|
|
134
|
+
chain: Type.Optional(Type.String({ description: "Chain: sol, bsc, base, eth" })),
|
|
135
|
+
address: Type.Optional(Type.String({ description: "Token contract address" })),
|
|
136
|
+
wallet: Type.Optional(Type.String({ description: "Wallet address for track commands" })),
|
|
137
|
+
limit: Type.Optional(Type.Number({ description: "Number of results" })),
|
|
138
|
+
resolution: Type.Optional(Type.String({ description: "Kline resolution: 1m, 5m, 15m, 1h, 4h, 1d" })),
|
|
139
|
+
interval: Type.Optional(Type.String({ description: "Trending interval: 1m, 5m, 1h, 6h, 24h" })),
|
|
140
|
+
type: Type.Optional(Type.String({ description: "Trenches type: new_creation, near_completion, completed" })),
|
|
141
|
+
});
|
|
142
|
+
export async function gmgnTool(_id, params) {
|
|
143
|
+
if (!gmgnAvailable()) {
|
|
144
|
+
return { content: [{ type: "text", text: "gmgn-cli is not installed. Run: npm install -g gmgn-cli" }], isError: true };
|
|
145
|
+
}
|
|
146
|
+
if (!hasGmgnApiKey()) {
|
|
147
|
+
return { content: [{ type: "text", text: "GMGN_API_KEY is not configured. Run 'gmgn setup' or tell the agent to configure it." }], isError: true };
|
|
148
|
+
}
|
|
149
|
+
const sub = params.subcommand;
|
|
150
|
+
const chain = params.chain || "sol";
|
|
151
|
+
const address = params.address;
|
|
152
|
+
const wallet = params.wallet;
|
|
153
|
+
const limit = params.limit;
|
|
154
|
+
const resolution = params.resolution;
|
|
155
|
+
const interval = params.interval;
|
|
156
|
+
const trenchesType = params.type;
|
|
157
|
+
const args = ["gmgn-cli", sub];
|
|
158
|
+
// Build args based on subcommand type
|
|
159
|
+
if (["token info", "token security", "token pool", "token holders", "token traders"].includes(sub)) {
|
|
160
|
+
if (!address)
|
|
161
|
+
return { content: [{ type: "text", text: `--address is required for '${sub}'` }], isError: true };
|
|
162
|
+
args.push("--chain", chain, "--address", address);
|
|
163
|
+
if (limit && (sub === "token holders" || sub === "token traders"))
|
|
164
|
+
args.push("--limit", String(limit));
|
|
165
|
+
}
|
|
166
|
+
else if (sub === "market kline") {
|
|
167
|
+
if (!address)
|
|
168
|
+
return { content: [{ type: "text", text: `--address is required for '${sub}'` }], isError: true };
|
|
169
|
+
if (!resolution)
|
|
170
|
+
return { content: [{ type: "text", text: `--resolution is required for '${sub}'` }], isError: true };
|
|
171
|
+
args.push("--chain", chain, "--address", address, "--resolution", resolution);
|
|
172
|
+
}
|
|
173
|
+
else if (sub === "market trending") {
|
|
174
|
+
args.push("--chain", chain);
|
|
175
|
+
if (interval)
|
|
176
|
+
args.push("--interval", interval);
|
|
177
|
+
if (limit)
|
|
178
|
+
args.push("--limit", String(limit));
|
|
179
|
+
}
|
|
180
|
+
else if (sub === "market trenches") {
|
|
181
|
+
args.push("--chain", chain);
|
|
182
|
+
if (trenchesType)
|
|
183
|
+
args.push("--type", trenchesType);
|
|
184
|
+
if (limit)
|
|
185
|
+
args.push("--limit", String(limit));
|
|
186
|
+
}
|
|
187
|
+
else if (sub === "market signal") {
|
|
188
|
+
args.push("--chain", chain);
|
|
189
|
+
if (limit)
|
|
190
|
+
args.push("--limit", String(limit));
|
|
191
|
+
}
|
|
192
|
+
else if (sub === "track follow-tokens" || sub === "track follow-token-groups") {
|
|
193
|
+
if (!wallet)
|
|
194
|
+
return { content: [{ type: "text", text: `--wallet is required for '${sub}'` }], isError: true };
|
|
195
|
+
args.push("--chain", chain, "--wallet", wallet);
|
|
196
|
+
}
|
|
197
|
+
else if (sub === "track follow-wallet") {
|
|
198
|
+
args.push("--chain", chain);
|
|
199
|
+
if (wallet)
|
|
200
|
+
args.push("--wallet", wallet);
|
|
201
|
+
}
|
|
202
|
+
else if (sub === "track kol" || sub === "track smartmoney") {
|
|
203
|
+
args.push("--chain", chain);
|
|
204
|
+
if (limit)
|
|
205
|
+
args.push("--limit", String(limit));
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
const output = execSync(args.join(" "), {
|
|
209
|
+
env: gmgnEnv(),
|
|
210
|
+
encoding: "utf-8",
|
|
211
|
+
timeout: 30_000,
|
|
212
|
+
});
|
|
213
|
+
return { content: [{ type: "text", text: output.trim() }] };
|
|
214
|
+
}
|
|
215
|
+
catch (e) {
|
|
216
|
+
const msg = e.stderr?.toString() || e.message || "";
|
|
217
|
+
if (msg.includes("401") || msg.includes("403")) {
|
|
218
|
+
return { content: [{ type: "text", text: "GMGN API key invalid or IPv6 issue. Run 'gmgn status'." }], isError: true };
|
|
219
|
+
}
|
|
220
|
+
if (msg.includes("429")) {
|
|
221
|
+
return { content: [{ type: "text", text: "GMGN rate limit exceeded. Wait and retry." }], isError: true };
|
|
222
|
+
}
|
|
223
|
+
return { content: [{ type: "text", text: `gmgn-cli error: ${msg.slice(0, 500)}` }], isError: true };
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// ── Market tool (shortcut for agent) ────────────────────────────────────────
|
|
227
|
+
export const gmgnMarketToolParams = Type.Object({
|
|
228
|
+
query: Type.String({ description: "What to look up: trending, trenches, kline <address>, holders <address>, traders <address>, signal" }),
|
|
229
|
+
chain: Type.Optional(Type.String({ default: "sol" })),
|
|
230
|
+
});
|
|
231
|
+
export async function gmgnMarketTool(_id, params) {
|
|
232
|
+
const query = (params.query || "").toLowerCase().trim();
|
|
233
|
+
const chain = params.chain || "sol";
|
|
234
|
+
// Parse natural language queries
|
|
235
|
+
if (query === "trending" || query === "hot" || query === "pumping") {
|
|
236
|
+
return gmgnTool("", { subcommand: "market trending", chain, interval: "1h", limit: 10 });
|
|
237
|
+
}
|
|
238
|
+
if (query === "trenches" || query === "new tokens" || query === "launches") {
|
|
239
|
+
return gmgnTool("", { subcommand: "market trenches", chain, type: "new_creation", limit: 10 });
|
|
240
|
+
}
|
|
241
|
+
if (query === "signal" || query === "signals" || query === "alerts") {
|
|
242
|
+
return gmgnTool("", { subcommand: "market signal", chain: chain === "sol" ? "sol" : "sol", limit: 10 });
|
|
243
|
+
}
|
|
244
|
+
if (query.startsWith("kline ") || query.startsWith("chart ")) {
|
|
245
|
+
const addr = query.split(" ").pop();
|
|
246
|
+
if (!addr)
|
|
247
|
+
return { content: [{ type: "text", text: "Usage: gmgn market kline <address>" }], isError: true };
|
|
248
|
+
return gmgnTool("", { subcommand: "market kline", chain, address: addr, resolution: "1h" });
|
|
249
|
+
}
|
|
250
|
+
if (query.startsWith("holders ") || query.startsWith("traders ")) {
|
|
251
|
+
const parts = query.split(" ");
|
|
252
|
+
const sub = parts[0] === "holders" ? "token holders" : "token traders";
|
|
253
|
+
const addr = parts[1];
|
|
254
|
+
if (!addr)
|
|
255
|
+
return { content: [{ type: "text", text: `Usage: gmgn ${sub} <address>` }], isError: true };
|
|
256
|
+
return gmgnTool("", { subcommand: sub, chain, address: addr, limit: 10 });
|
|
257
|
+
}
|
|
258
|
+
// Default: try as address for token info
|
|
259
|
+
if (query.length > 20) {
|
|
260
|
+
return gmgnTool("", { subcommand: "token info", chain, address: query });
|
|
261
|
+
}
|
|
262
|
+
return { content: [{ type: "text", text: "Usage: gmgn market <trending|trenches|signal|kline <addr>|holders <addr>|traders <addr>>" }], isError: true };
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=GmgnIntegration.js.map
|
package/dist/tui/App.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* App — root Ink component. Multi-pane TUI with sidebar
|
|
3
|
-
* and
|
|
2
|
+
* App — root Ink component. Multi-pane TUI with sidebar, system monitor,
|
|
3
|
+
* session persistence, and full GMGN command access.
|
|
4
4
|
*/
|
|
5
5
|
import React from "react";
|
|
6
6
|
import { Registry } from "../api/Registry.js";
|
|
7
|
-
import type { ModelRegistry } from "../models/ModelRegistry.js";
|
|
8
7
|
import type { CostTracker } from "../models/CostTracker.js";
|
|
8
|
+
import type { ModelRegistry as ModelRegistryType } from "../models/ModelRegistry.js";
|
|
9
9
|
export interface AppProps {
|
|
10
10
|
registry: Registry;
|
|
11
11
|
systemPrompt?: string;
|
|
12
12
|
chain?: string;
|
|
13
|
-
modelReg?:
|
|
13
|
+
modelReg?: ModelRegistryType;
|
|
14
14
|
costTracker?: CostTracker;
|
|
15
15
|
onNotifyReady?: (fn: (msg: string) => void) => void;
|
|
16
16
|
onStatusReady?: (fn: (key: string, val: string) => void) => void;
|