@moonpay/cli 0.1.0
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 +41 -0
- package/dist/index.js +2030 -0
- package/dist/index.js.map +1 -0
- package/package.json +31 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2030 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { createRequire } from "module";
|
|
5
|
+
import { Command, Option } from "commander";
|
|
6
|
+
|
|
7
|
+
// src/auth.ts
|
|
8
|
+
import { spawn } from "child_process";
|
|
9
|
+
import * as crypto from "crypto";
|
|
10
|
+
import * as fs from "fs";
|
|
11
|
+
import * as http from "http";
|
|
12
|
+
import * as os from "os";
|
|
13
|
+
import * as path from "path";
|
|
14
|
+
function escapeHtml(str) {
|
|
15
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
16
|
+
}
|
|
17
|
+
function generateCodeVerifier() {
|
|
18
|
+
return crypto.randomBytes(32).toString("base64url");
|
|
19
|
+
}
|
|
20
|
+
function generateCodeChallenge(verifier) {
|
|
21
|
+
return crypto.createHash("sha256").update(verifier).digest("base64url");
|
|
22
|
+
}
|
|
23
|
+
var CONFIG_DIR = path.join(os.homedir(), ".config", "moon-iq");
|
|
24
|
+
var CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
|
|
25
|
+
var CREDENTIALS_PATH = path.join(CONFIG_DIR, "credentials.json");
|
|
26
|
+
var LOCK_PATH = path.join(CONFIG_DIR, ".credentials.lock");
|
|
27
|
+
var CALLBACK_PORT = 3847;
|
|
28
|
+
var CALLBACK_URL = `http://localhost:${CALLBACK_PORT}/callback`;
|
|
29
|
+
var DEFAULT_CONFIG = {
|
|
30
|
+
baseUrl: "https://moon-iq.com",
|
|
31
|
+
clientId: "mooniq_zin3s5jz3olzkdfxpmbeaogv"
|
|
32
|
+
};
|
|
33
|
+
function ensureConfigDir() {
|
|
34
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
35
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function atomicWriteFileSync(filePath, data, mode) {
|
|
39
|
+
const tmp = filePath + `.tmp.${process.pid}`;
|
|
40
|
+
fs.writeFileSync(tmp, data, { encoding: "utf-8", mode });
|
|
41
|
+
fs.renameSync(tmp, filePath);
|
|
42
|
+
}
|
|
43
|
+
var LOCK_STALE_MS = 3e4;
|
|
44
|
+
function acquireLock() {
|
|
45
|
+
ensureConfigDir();
|
|
46
|
+
try {
|
|
47
|
+
const fd = fs.openSync(LOCK_PATH, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY, 384);
|
|
48
|
+
fs.writeSync(fd, JSON.stringify({ pid: process.pid, ts: Date.now() }));
|
|
49
|
+
fs.closeSync(fd);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
if (err.code !== "EEXIST") throw err;
|
|
52
|
+
try {
|
|
53
|
+
const lockData = JSON.parse(fs.readFileSync(LOCK_PATH, "utf-8"));
|
|
54
|
+
if (Date.now() - lockData.ts < LOCK_STALE_MS) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
fs.unlinkSync(LOCK_PATH);
|
|
61
|
+
} catch {
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const fd = fs.openSync(LOCK_PATH, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY, 384);
|
|
65
|
+
fs.writeSync(fd, JSON.stringify({ pid: process.pid, ts: Date.now() }));
|
|
66
|
+
fs.closeSync(fd);
|
|
67
|
+
} catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return () => {
|
|
72
|
+
try {
|
|
73
|
+
fs.unlinkSync(LOCK_PATH);
|
|
74
|
+
} catch {
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function getConfig() {
|
|
79
|
+
if (!fs.existsSync(CONFIG_PATH)) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const data = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
|
|
84
|
+
if (!data.baseUrl || !data.clientId) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
return data;
|
|
88
|
+
} catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function getConfigOrDefault() {
|
|
93
|
+
const fromFile = getConfig();
|
|
94
|
+
if (fromFile) return fromFile;
|
|
95
|
+
saveConfig(DEFAULT_CONFIG);
|
|
96
|
+
return DEFAULT_CONFIG;
|
|
97
|
+
}
|
|
98
|
+
function getCredentials() {
|
|
99
|
+
if (!fs.existsSync(CREDENTIALS_PATH)) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const data = JSON.parse(fs.readFileSync(CREDENTIALS_PATH, "utf-8"));
|
|
104
|
+
if (!data.accessToken || !data.baseUrl) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
return data;
|
|
108
|
+
} catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function saveCredentials(creds) {
|
|
113
|
+
ensureConfigDir();
|
|
114
|
+
atomicWriteFileSync(CREDENTIALS_PATH, JSON.stringify(creds, null, 2), 384);
|
|
115
|
+
}
|
|
116
|
+
function saveConfig(config) {
|
|
117
|
+
ensureConfigDir();
|
|
118
|
+
atomicWriteFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), 384);
|
|
119
|
+
}
|
|
120
|
+
function clearCredentials() {
|
|
121
|
+
if (fs.existsSync(CREDENTIALS_PATH)) {
|
|
122
|
+
fs.unlinkSync(CREDENTIALS_PATH);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function openBrowser(url) {
|
|
126
|
+
const platform = process.platform;
|
|
127
|
+
const cmd = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
|
|
128
|
+
const args = platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
129
|
+
spawn(cmd, args, { stdio: "ignore", detached: true }).unref();
|
|
130
|
+
}
|
|
131
|
+
async function login(config) {
|
|
132
|
+
const state = crypto.randomUUID();
|
|
133
|
+
const usePkce = !config.clientSecret;
|
|
134
|
+
let codeVerifier;
|
|
135
|
+
if (usePkce) {
|
|
136
|
+
codeVerifier = generateCodeVerifier();
|
|
137
|
+
}
|
|
138
|
+
let resolveCallback;
|
|
139
|
+
const codePromise = new Promise((resolve) => {
|
|
140
|
+
resolveCallback = resolve;
|
|
141
|
+
});
|
|
142
|
+
const sockets = /* @__PURE__ */ new Set();
|
|
143
|
+
const server = http.createServer((req, res) => {
|
|
144
|
+
const url = new URL(req.url ?? "/", `http://localhost:${CALLBACK_PORT}`);
|
|
145
|
+
if (url.pathname === "/callback") {
|
|
146
|
+
const code2 = url.searchParams.get("code");
|
|
147
|
+
const returnedState = url.searchParams.get("state");
|
|
148
|
+
const error = url.searchParams.get("error");
|
|
149
|
+
const errorDesc = url.searchParams.get("error_description");
|
|
150
|
+
if (returnedState !== state) {
|
|
151
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
152
|
+
res.end(
|
|
153
|
+
`<!DOCTYPE html><html><head><meta charset="utf-8"><title>OAuth Error</title></head><body style="font-family:system-ui,sans-serif;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;margin:0;padding:2rem;text-align:center;background:#fef2f2"><h1 style="color:#b91c1c;margin:0 0 1rem">OAuth Error</h1><p style="color:#991b1b;margin:0">State mismatch \u2014 possible CSRF attack. Please try logging in again.</p></body></html>`
|
|
154
|
+
);
|
|
155
|
+
resolveCallback("");
|
|
156
|
+
setTimeout(() => server.close(), 2e3);
|
|
157
|
+
} else if (error) {
|
|
158
|
+
const safeError = escapeHtml(error);
|
|
159
|
+
const safeDesc = escapeHtml(errorDesc ?? "Unknown error");
|
|
160
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
161
|
+
res.end(
|
|
162
|
+
`<!DOCTYPE html><html><head><meta charset="utf-8"><title>OAuth Error</title></head><body style="font-family:system-ui,sans-serif;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;margin:0;padding:2rem;text-align:center;background:#fef2f2"><h1 style="color:#b91c1c;margin:0 0 1rem">OAuth Error</h1><p style="color:#991b1b;margin:0">${safeError}: ${safeDesc}</p></body></html>`
|
|
163
|
+
);
|
|
164
|
+
resolveCallback("");
|
|
165
|
+
setTimeout(() => server.close(), 2e3);
|
|
166
|
+
} else if (code2) {
|
|
167
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
168
|
+
res.end(
|
|
169
|
+
`<!DOCTYPE html><html><head><meta charset="utf-8"><title>Success</title></head><body style="font-family:system-ui,sans-serif;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;margin:0;padding:2rem;text-align:center;background:#f0fdf4"><h1 style="color:#166534;margin:0 0 1rem;font-size:1.5rem">\u2713 Success!</h1><p style="color:#15803d;margin:0;font-size:1.1rem">You can close this page and return to the terminal.</p></body></html>`
|
|
170
|
+
);
|
|
171
|
+
resolveCallback(code2);
|
|
172
|
+
} else {
|
|
173
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
174
|
+
res.end(
|
|
175
|
+
`<!DOCTYPE html><html><head><meta charset="utf-8"><title>Error</title></head><body style="font-family:system-ui,sans-serif;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;margin:0;padding:2rem;text-align:center;background:#fef2f2"><h1 style="color:#b91c1c;margin:0 0 1rem">Error</h1><p style="color:#991b1b;margin:0">No authorization code received.</p></body></html>`
|
|
176
|
+
);
|
|
177
|
+
resolveCallback("");
|
|
178
|
+
setTimeout(() => server.close(), 2e3);
|
|
179
|
+
}
|
|
180
|
+
} else {
|
|
181
|
+
res.writeHead(204);
|
|
182
|
+
res.end();
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
await new Promise((resolve, reject) => {
|
|
186
|
+
server.on("error", (err) => {
|
|
187
|
+
if (err.code === "EADDRINUSE") {
|
|
188
|
+
reject(
|
|
189
|
+
new Error(
|
|
190
|
+
`Port ${CALLBACK_PORT} is in use. Close the other process or run: lsof -i :${CALLBACK_PORT}`
|
|
191
|
+
)
|
|
192
|
+
);
|
|
193
|
+
} else {
|
|
194
|
+
reject(err);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
server.on("connection", (socket) => {
|
|
198
|
+
sockets.add(socket);
|
|
199
|
+
socket.on("close", () => sockets.delete(socket));
|
|
200
|
+
});
|
|
201
|
+
server.listen(CALLBACK_PORT, "127.0.0.1", () => resolve());
|
|
202
|
+
});
|
|
203
|
+
console.log(
|
|
204
|
+
`Waiting for callback at http://localhost:${CALLBACK_PORT}/callback`
|
|
205
|
+
);
|
|
206
|
+
const authorizeParams = new URLSearchParams({
|
|
207
|
+
client_id: config.clientId,
|
|
208
|
+
redirect_uri: CALLBACK_URL,
|
|
209
|
+
response_type: "code",
|
|
210
|
+
scope: "profile email",
|
|
211
|
+
state
|
|
212
|
+
});
|
|
213
|
+
if (usePkce && codeVerifier) {
|
|
214
|
+
authorizeParams.set("code_challenge", generateCodeChallenge(codeVerifier));
|
|
215
|
+
authorizeParams.set("code_challenge_method", "S256");
|
|
216
|
+
}
|
|
217
|
+
const authorizeUrl = `${config.baseUrl}/api/oauth/authorize?${authorizeParams.toString()}`;
|
|
218
|
+
console.log("Opening browser for authorization...");
|
|
219
|
+
console.log("(Make sure you're logged in to Moon IQ in your browser)\n");
|
|
220
|
+
openBrowser(authorizeUrl);
|
|
221
|
+
const code = await codePromise;
|
|
222
|
+
if (!code) {
|
|
223
|
+
throw new Error("No authorization code received");
|
|
224
|
+
}
|
|
225
|
+
const tokenParams = {
|
|
226
|
+
grant_type: "authorization_code",
|
|
227
|
+
code,
|
|
228
|
+
client_id: config.clientId,
|
|
229
|
+
redirect_uri: CALLBACK_URL
|
|
230
|
+
};
|
|
231
|
+
if (config.clientSecret) {
|
|
232
|
+
tokenParams.client_secret = config.clientSecret;
|
|
233
|
+
}
|
|
234
|
+
if (usePkce && codeVerifier) {
|
|
235
|
+
tokenParams.code_verifier = codeVerifier;
|
|
236
|
+
}
|
|
237
|
+
const tokenResponse = await fetch(`${config.baseUrl}/api/oauth/token`, {
|
|
238
|
+
method: "POST",
|
|
239
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
240
|
+
body: new URLSearchParams(tokenParams)
|
|
241
|
+
});
|
|
242
|
+
if (!tokenResponse.ok) {
|
|
243
|
+
const err = await tokenResponse.json().catch(() => ({}));
|
|
244
|
+
const msg = err.error_description ?? err.error ?? tokenResponse.statusText;
|
|
245
|
+
throw new Error(`Token exchange failed: ${msg}`);
|
|
246
|
+
}
|
|
247
|
+
const tokens = await tokenResponse.json();
|
|
248
|
+
const expiresAt = Date.now() + (tokens.expires_in ?? 3600) * 1e3;
|
|
249
|
+
const creds = {
|
|
250
|
+
accessToken: tokens.access_token,
|
|
251
|
+
refreshToken: tokens.refresh_token ?? null,
|
|
252
|
+
expiresAt,
|
|
253
|
+
baseUrl: config.baseUrl
|
|
254
|
+
};
|
|
255
|
+
saveCredentials(creds);
|
|
256
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
257
|
+
server.close();
|
|
258
|
+
for (const socket of sockets) {
|
|
259
|
+
if ("destroy" in socket && typeof socket.destroy === "function") {
|
|
260
|
+
socket.destroy();
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
sockets.clear();
|
|
264
|
+
return creds;
|
|
265
|
+
}
|
|
266
|
+
async function refreshCredentials(creds, config) {
|
|
267
|
+
if (!creds.refreshToken) {
|
|
268
|
+
throw new Error("No refresh token available");
|
|
269
|
+
}
|
|
270
|
+
const unlock = acquireLock();
|
|
271
|
+
if (!unlock) {
|
|
272
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
273
|
+
const fresh = getCredentials();
|
|
274
|
+
if (fresh && fresh.expiresAt > Date.now()) {
|
|
275
|
+
return fresh;
|
|
276
|
+
}
|
|
277
|
+
throw new Error("Token refresh failed (concurrent refresh in progress)");
|
|
278
|
+
}
|
|
279
|
+
try {
|
|
280
|
+
const latest = getCredentials();
|
|
281
|
+
if (latest && latest.expiresAt > Date.now() + 6e4) {
|
|
282
|
+
return latest;
|
|
283
|
+
}
|
|
284
|
+
const refreshParams = {
|
|
285
|
+
grant_type: "refresh_token",
|
|
286
|
+
refresh_token: creds.refreshToken,
|
|
287
|
+
client_id: config.clientId
|
|
288
|
+
};
|
|
289
|
+
if (config.clientSecret) {
|
|
290
|
+
refreshParams.client_secret = config.clientSecret;
|
|
291
|
+
}
|
|
292
|
+
const tokenResponse = await fetch(`${config.baseUrl}/api/oauth/token`, {
|
|
293
|
+
method: "POST",
|
|
294
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
295
|
+
body: new URLSearchParams(refreshParams)
|
|
296
|
+
});
|
|
297
|
+
if (!tokenResponse.ok) {
|
|
298
|
+
throw new Error("Token refresh failed");
|
|
299
|
+
}
|
|
300
|
+
const tokens = await tokenResponse.json();
|
|
301
|
+
const expiresAt = Date.now() + (tokens.expires_in ?? 3600) * 1e3;
|
|
302
|
+
const newCreds = {
|
|
303
|
+
accessToken: tokens.access_token,
|
|
304
|
+
refreshToken: tokens.refresh_token ?? creds.refreshToken,
|
|
305
|
+
expiresAt,
|
|
306
|
+
baseUrl: config.baseUrl
|
|
307
|
+
};
|
|
308
|
+
saveCredentials(newCreds);
|
|
309
|
+
return newCreds;
|
|
310
|
+
} finally {
|
|
311
|
+
unlock();
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
var TOKEN_EXPIRY_BUFFER_MS = 5 * 60 * 1e3;
|
|
315
|
+
async function getValidToken() {
|
|
316
|
+
const creds = getCredentials();
|
|
317
|
+
if (!creds) return null;
|
|
318
|
+
const config = getConfig();
|
|
319
|
+
if (!config || config.baseUrl !== creds.baseUrl) return null;
|
|
320
|
+
if (Date.now() >= creds.expiresAt - TOKEN_EXPIRY_BUFFER_MS) {
|
|
321
|
+
if (creds.refreshToken) {
|
|
322
|
+
try {
|
|
323
|
+
const newCreds = await refreshCredentials(creds, config);
|
|
324
|
+
return newCreds.accessToken;
|
|
325
|
+
} catch {
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
return creds.accessToken;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// src/generated/client.ts
|
|
335
|
+
var TOOL_NAMES = [
|
|
336
|
+
"iron_account_create",
|
|
337
|
+
"iron_account_retrieve",
|
|
338
|
+
"iron_offramp_create",
|
|
339
|
+
"iron_offramp_delete",
|
|
340
|
+
"iron_offramp_initiate",
|
|
341
|
+
"iron_offramp_list",
|
|
342
|
+
"iron_offramp_retrieve",
|
|
343
|
+
"iron_onramp_create",
|
|
344
|
+
"iron_onramp_delete",
|
|
345
|
+
"iron_onramp_list",
|
|
346
|
+
"iron_onramp_payment_create",
|
|
347
|
+
"iron_onramp_payment_retrieve",
|
|
348
|
+
"iron_onramp_retrieve",
|
|
349
|
+
"lending_account_retrieve",
|
|
350
|
+
"lending_deposit",
|
|
351
|
+
"lending_rate_retrieve",
|
|
352
|
+
"lending_recommend",
|
|
353
|
+
"lending_withdraw",
|
|
354
|
+
"market_digest_retrieve",
|
|
355
|
+
"moonpay_buy",
|
|
356
|
+
"solana_buy",
|
|
357
|
+
"token_balance_check",
|
|
358
|
+
"token_balance_list",
|
|
359
|
+
"token_balance_retrieve",
|
|
360
|
+
"token_bridge",
|
|
361
|
+
"token_buy",
|
|
362
|
+
"token_digest_retrieve",
|
|
363
|
+
"token_holder_list",
|
|
364
|
+
"token_limit_buy",
|
|
365
|
+
"token_limit_buy_recommend",
|
|
366
|
+
"token_limit_order_list",
|
|
367
|
+
"token_limit_recommend",
|
|
368
|
+
"token_limit_sell",
|
|
369
|
+
"token_limit_sell_recommend",
|
|
370
|
+
"token_liquidity_lock_list",
|
|
371
|
+
"token_liquidity_retrieve",
|
|
372
|
+
"token_pair_list",
|
|
373
|
+
"token_recurring_buy",
|
|
374
|
+
"token_recurring_order_list",
|
|
375
|
+
"token_recurring_recommend",
|
|
376
|
+
"token_recurring_sell",
|
|
377
|
+
"token_retrieve",
|
|
378
|
+
"token_search",
|
|
379
|
+
"token_sell",
|
|
380
|
+
"token_swap",
|
|
381
|
+
"token_swap_recommend",
|
|
382
|
+
"token_top_trader_list",
|
|
383
|
+
"token_transfer",
|
|
384
|
+
"token_trending_list",
|
|
385
|
+
"user_retrieve",
|
|
386
|
+
"user_update",
|
|
387
|
+
"user_wallet_list",
|
|
388
|
+
"user_wallet_retrieve",
|
|
389
|
+
"wallet_digest_retrieve",
|
|
390
|
+
"x402_request"
|
|
391
|
+
];
|
|
392
|
+
var TOOL_DEFINITIONS = [
|
|
393
|
+
{
|
|
394
|
+
"name": "iron_account_create",
|
|
395
|
+
"description": "Create a new Iron account for the user.",
|
|
396
|
+
"options": []
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
"name": "iron_account_retrieve",
|
|
400
|
+
"description": "Get the user's Iron account information.",
|
|
401
|
+
"options": []
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
"name": "iron_offramp_create",
|
|
405
|
+
"description": "Create a new offramp for the user to convert crypto to fiat",
|
|
406
|
+
"options": [
|
|
407
|
+
{
|
|
408
|
+
"name": "name",
|
|
409
|
+
"type": "string",
|
|
410
|
+
"description": "The name of the offramp",
|
|
411
|
+
"required": true
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
"name": "fiat",
|
|
415
|
+
"type": "string",
|
|
416
|
+
"description": "The fiat currency to convert to",
|
|
417
|
+
"required": true,
|
|
418
|
+
"choices": [
|
|
419
|
+
"USD",
|
|
420
|
+
"EUR"
|
|
421
|
+
]
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
"name": "stablecoin",
|
|
425
|
+
"type": "string",
|
|
426
|
+
"description": "The stablecoin token to convert from",
|
|
427
|
+
"required": true,
|
|
428
|
+
"choices": [
|
|
429
|
+
"USDC",
|
|
430
|
+
"USDT",
|
|
431
|
+
"EURC"
|
|
432
|
+
]
|
|
433
|
+
}
|
|
434
|
+
]
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
"name": "iron_offramp_delete",
|
|
438
|
+
"description": "Cancel an offramp",
|
|
439
|
+
"options": [
|
|
440
|
+
{
|
|
441
|
+
"name": "offrampId",
|
|
442
|
+
"type": "string",
|
|
443
|
+
"description": "The ID of the offramp to cancel",
|
|
444
|
+
"required": true
|
|
445
|
+
}
|
|
446
|
+
]
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
"name": "iron_offramp_initiate",
|
|
450
|
+
"description": "Initiate an offramp by sending stablecoin to the offramp's deposit account. The stablecoin will be converted to fiat and deposited into your registered bank account. This process is asynchronous and may take some time for funds to appear in your bank account.",
|
|
451
|
+
"options": [
|
|
452
|
+
{
|
|
453
|
+
"name": "simulation",
|
|
454
|
+
"type": "boolean",
|
|
455
|
+
"description": "If true, only simulate the transaction without executing it",
|
|
456
|
+
"required": true
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
"name": "offrampId",
|
|
460
|
+
"type": "string",
|
|
461
|
+
"description": "The ID of the offramp to initiate",
|
|
462
|
+
"required": true
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
"name": "amount",
|
|
466
|
+
"type": "number",
|
|
467
|
+
"description": "The amount of stablecoin to send (in human-readable units, e.g., 100.5)",
|
|
468
|
+
"required": true
|
|
469
|
+
}
|
|
470
|
+
]
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
"name": "iron_offramp_list",
|
|
474
|
+
"description": "Get all offramps for the user",
|
|
475
|
+
"options": []
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
"name": "iron_offramp_retrieve",
|
|
479
|
+
"description": "Get an offramp for the user",
|
|
480
|
+
"options": [
|
|
481
|
+
{
|
|
482
|
+
"name": "offrampId",
|
|
483
|
+
"type": "string",
|
|
484
|
+
"description": "The ID of the offramp to retrieve",
|
|
485
|
+
"required": true
|
|
486
|
+
}
|
|
487
|
+
]
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
"name": "iron_onramp_create",
|
|
491
|
+
"description": "Create an onramp to convert fiat currency to a stablecoin",
|
|
492
|
+
"options": [
|
|
493
|
+
{
|
|
494
|
+
"name": "name",
|
|
495
|
+
"type": "string",
|
|
496
|
+
"description": "The name of the onramp",
|
|
497
|
+
"required": true
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
"name": "fiat",
|
|
501
|
+
"type": "string",
|
|
502
|
+
"description": "The fiat currency to convert from",
|
|
503
|
+
"required": true,
|
|
504
|
+
"choices": [
|
|
505
|
+
"USD",
|
|
506
|
+
"EUR"
|
|
507
|
+
]
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
"name": "stablecoin",
|
|
511
|
+
"type": "string",
|
|
512
|
+
"description": "The stablecoin token to convert to",
|
|
513
|
+
"required": true,
|
|
514
|
+
"choices": [
|
|
515
|
+
"USDC",
|
|
516
|
+
"USDT",
|
|
517
|
+
"EURC"
|
|
518
|
+
]
|
|
519
|
+
}
|
|
520
|
+
]
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
"name": "iron_onramp_delete",
|
|
524
|
+
"description": "Cancel an onramp",
|
|
525
|
+
"options": [
|
|
526
|
+
{
|
|
527
|
+
"name": "onrampId",
|
|
528
|
+
"type": "string",
|
|
529
|
+
"description": "The ID of the onramp to cancel",
|
|
530
|
+
"required": true
|
|
531
|
+
}
|
|
532
|
+
]
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
"name": "iron_onramp_list",
|
|
536
|
+
"description": "Get all onramps for the user",
|
|
537
|
+
"options": [
|
|
538
|
+
{
|
|
539
|
+
"name": "status",
|
|
540
|
+
"type": "string",
|
|
541
|
+
"description": "Status of the onramps to get",
|
|
542
|
+
"required": true,
|
|
543
|
+
"choices": [
|
|
544
|
+
"Created",
|
|
545
|
+
"Authorized",
|
|
546
|
+
"DepositAccountAdded",
|
|
547
|
+
"Approved",
|
|
548
|
+
"Rejected",
|
|
549
|
+
"Cancelled"
|
|
550
|
+
]
|
|
551
|
+
}
|
|
552
|
+
]
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
"name": "iron_onramp_payment_create",
|
|
556
|
+
"description": "Create an open banking payment link for an onramp",
|
|
557
|
+
"options": [
|
|
558
|
+
{
|
|
559
|
+
"name": "onrampId",
|
|
560
|
+
"type": "string",
|
|
561
|
+
"description": "The ID of the onramp",
|
|
562
|
+
"required": true
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
"name": "amount",
|
|
566
|
+
"type": "string",
|
|
567
|
+
"description": "The amount to pay",
|
|
568
|
+
"required": true
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
"name": "fiat",
|
|
572
|
+
"type": "string",
|
|
573
|
+
"description": "The fiat currency (USD or EUR)",
|
|
574
|
+
"required": true,
|
|
575
|
+
"choices": [
|
|
576
|
+
"USD",
|
|
577
|
+
"EUR"
|
|
578
|
+
]
|
|
579
|
+
}
|
|
580
|
+
]
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
"name": "iron_onramp_payment_retrieve",
|
|
584
|
+
"description": "Get the status of an open banking payment",
|
|
585
|
+
"options": [
|
|
586
|
+
{
|
|
587
|
+
"name": "onrampId",
|
|
588
|
+
"type": "string",
|
|
589
|
+
"description": "The ID of the onramp this payment belongs to",
|
|
590
|
+
"required": true
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
"name": "paymentId",
|
|
594
|
+
"type": "string",
|
|
595
|
+
"description": "The ID of the payment to retrieve",
|
|
596
|
+
"required": true
|
|
597
|
+
}
|
|
598
|
+
]
|
|
599
|
+
},
|
|
600
|
+
{
|
|
601
|
+
"name": "iron_onramp_retrieve",
|
|
602
|
+
"description": "Get an onramp for the user",
|
|
603
|
+
"options": [
|
|
604
|
+
{
|
|
605
|
+
"name": "onrampId",
|
|
606
|
+
"type": "string",
|
|
607
|
+
"description": "The ID of the onramp to retrieve",
|
|
608
|
+
"required": true
|
|
609
|
+
}
|
|
610
|
+
]
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
"name": "lending_account_retrieve",
|
|
614
|
+
"description": "Get account details for a lending account by wallet address, including balances, interest earned, and withdrawal limits",
|
|
615
|
+
"options": []
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
"name": "lending_deposit",
|
|
619
|
+
"description": "Deposit USDC into your lending account to earn interest.",
|
|
620
|
+
"options": [
|
|
621
|
+
{
|
|
622
|
+
"name": "simulation",
|
|
623
|
+
"type": "boolean",
|
|
624
|
+
"description": "If true, only simulate the transaction without executing it",
|
|
625
|
+
"required": true
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
"name": "amount",
|
|
629
|
+
"type": "number",
|
|
630
|
+
"description": "Amount of USDC to deposit",
|
|
631
|
+
"required": true
|
|
632
|
+
}
|
|
633
|
+
]
|
|
634
|
+
},
|
|
635
|
+
{
|
|
636
|
+
"name": "lending_rate_retrieve",
|
|
637
|
+
"description": "Get current and historical interest rates for lending USDC",
|
|
638
|
+
"options": []
|
|
639
|
+
},
|
|
640
|
+
{
|
|
641
|
+
"name": "lending_recommend",
|
|
642
|
+
"description": "Analyze user's lending account and wallet balances to recommend either a deposit or withdraw action with appropriate parameters. Understands queries like 'should I deposit more?', 'recommend a withdrawal', or 'what should I do with my lending account?'.",
|
|
643
|
+
"options": [
|
|
644
|
+
{
|
|
645
|
+
"name": "query",
|
|
646
|
+
"type": "string",
|
|
647
|
+
"description": "Natural language query about lending recommendations. Examples: 'should I deposit more?', 'recommend a withdrawal', 'what should I do with my lending account?'.",
|
|
648
|
+
"required": true
|
|
649
|
+
}
|
|
650
|
+
]
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
"name": "lending_withdraw",
|
|
654
|
+
"description": "Withdraw USDC from your lending account.",
|
|
655
|
+
"options": [
|
|
656
|
+
{
|
|
657
|
+
"name": "simulation",
|
|
658
|
+
"type": "boolean",
|
|
659
|
+
"description": "If true, only simulate the transaction without executing it",
|
|
660
|
+
"required": true
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
"name": "amount",
|
|
664
|
+
"type": "number",
|
|
665
|
+
"description": "Amount of USDC to withdraw",
|
|
666
|
+
"required": true
|
|
667
|
+
}
|
|
668
|
+
]
|
|
669
|
+
},
|
|
670
|
+
{
|
|
671
|
+
"name": "market_digest_retrieve",
|
|
672
|
+
"description": "Retrieve a market digest by chain.",
|
|
673
|
+
"options": [
|
|
674
|
+
{
|
|
675
|
+
"name": "chain",
|
|
676
|
+
"type": "string",
|
|
677
|
+
"description": "Blockchain network",
|
|
678
|
+
"required": true,
|
|
679
|
+
"choices": [
|
|
680
|
+
"solana",
|
|
681
|
+
"ethereum",
|
|
682
|
+
"base",
|
|
683
|
+
"polygon",
|
|
684
|
+
"arbitrum",
|
|
685
|
+
"optimism"
|
|
686
|
+
]
|
|
687
|
+
}
|
|
688
|
+
]
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
"name": "moonpay_buy",
|
|
692
|
+
"description": "Buy a token using Moonpay. Specify either from.amount (USD to spend) or to.amount (tokens to buy), not both.",
|
|
693
|
+
"options": [
|
|
694
|
+
{
|
|
695
|
+
"name": "from",
|
|
696
|
+
"type": "string",
|
|
697
|
+
"description": "Payment currency and amount to spend",
|
|
698
|
+
"required": true
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
"name": "to",
|
|
702
|
+
"type": "string",
|
|
703
|
+
"description": "Destination chain, currency, and token amount",
|
|
704
|
+
"required": true
|
|
705
|
+
}
|
|
706
|
+
]
|
|
707
|
+
},
|
|
708
|
+
{
|
|
709
|
+
"name": "solana_buy",
|
|
710
|
+
"description": "Buy SOL with USDC. Use this when the user needs SOL for gas or wants to convert USDC to SOL.",
|
|
711
|
+
"options": [
|
|
712
|
+
{
|
|
713
|
+
"name": "simulation",
|
|
714
|
+
"type": "boolean",
|
|
715
|
+
"description": "If true, only simulate the transaction without executing it",
|
|
716
|
+
"required": true
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
"name": "amount",
|
|
720
|
+
"type": "number",
|
|
721
|
+
"description": "Amount of USDC to spend to buy SOL",
|
|
722
|
+
"required": true
|
|
723
|
+
}
|
|
724
|
+
]
|
|
725
|
+
},
|
|
726
|
+
{
|
|
727
|
+
"name": "token_balance_check",
|
|
728
|
+
"description": "Check if a wallet has enough of a specific token. Returns success: true if the wallet has sufficient balance, false otherwise.",
|
|
729
|
+
"options": [
|
|
730
|
+
{
|
|
731
|
+
"name": "wallet",
|
|
732
|
+
"type": "string",
|
|
733
|
+
"description": "Wallet address to check the balance for",
|
|
734
|
+
"required": true
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
"name": "token",
|
|
738
|
+
"type": "string",
|
|
739
|
+
"description": "Token address to check the balance of",
|
|
740
|
+
"required": true
|
|
741
|
+
},
|
|
742
|
+
{
|
|
743
|
+
"name": "amount",
|
|
744
|
+
"type": "number",
|
|
745
|
+
"description": "Required amount of tokens to check against",
|
|
746
|
+
"required": true
|
|
747
|
+
},
|
|
748
|
+
{
|
|
749
|
+
"name": "chain",
|
|
750
|
+
"type": "string",
|
|
751
|
+
"description": "Blockchain where the token and wallet exist",
|
|
752
|
+
"required": true,
|
|
753
|
+
"choices": [
|
|
754
|
+
"solana",
|
|
755
|
+
"ethereum",
|
|
756
|
+
"base",
|
|
757
|
+
"polygon",
|
|
758
|
+
"arbitrum",
|
|
759
|
+
"optimism"
|
|
760
|
+
]
|
|
761
|
+
}
|
|
762
|
+
]
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
"name": "token_balance_list",
|
|
766
|
+
"description": "List all token balances held in a wallet, including amount owned, current value in USD, and token details. Shows the complete token portfolio.",
|
|
767
|
+
"options": [
|
|
768
|
+
{
|
|
769
|
+
"name": "wallet",
|
|
770
|
+
"type": "string",
|
|
771
|
+
"description": "Wallet address to check token balances for",
|
|
772
|
+
"required": true
|
|
773
|
+
},
|
|
774
|
+
{
|
|
775
|
+
"name": "chain",
|
|
776
|
+
"type": "string",
|
|
777
|
+
"description": "Blockchain to check balances on (e.g. 'solana', 'ethereum', 'base')",
|
|
778
|
+
"required": true,
|
|
779
|
+
"choices": [
|
|
780
|
+
"solana",
|
|
781
|
+
"ethereum",
|
|
782
|
+
"base",
|
|
783
|
+
"polygon",
|
|
784
|
+
"arbitrum",
|
|
785
|
+
"optimism"
|
|
786
|
+
]
|
|
787
|
+
}
|
|
788
|
+
]
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
"name": "token_balance_retrieve",
|
|
792
|
+
"description": "Get the balance of a specific token in a wallet, including the amount owned, current USD value, and token price. Returns zero balance if token is not held.",
|
|
793
|
+
"options": [
|
|
794
|
+
{
|
|
795
|
+
"name": "wallet",
|
|
796
|
+
"type": "string",
|
|
797
|
+
"description": "Wallet address to check the balance for",
|
|
798
|
+
"required": true
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
"name": "token",
|
|
802
|
+
"type": "string",
|
|
803
|
+
"description": "Token address to check the balance of",
|
|
804
|
+
"required": true
|
|
805
|
+
},
|
|
806
|
+
{
|
|
807
|
+
"name": "chain",
|
|
808
|
+
"type": "string",
|
|
809
|
+
"description": "Blockchain where the token and wallet exist",
|
|
810
|
+
"required": true,
|
|
811
|
+
"choices": [
|
|
812
|
+
"solana",
|
|
813
|
+
"ethereum",
|
|
814
|
+
"base",
|
|
815
|
+
"polygon",
|
|
816
|
+
"arbitrum",
|
|
817
|
+
"optimism"
|
|
818
|
+
]
|
|
819
|
+
}
|
|
820
|
+
]
|
|
821
|
+
},
|
|
822
|
+
{
|
|
823
|
+
"name": "token_bridge",
|
|
824
|
+
"description": "Bridge tokens from Solana to another chain (or vice versa) via swaps.xyz cross-chain routing.",
|
|
825
|
+
"options": [
|
|
826
|
+
{
|
|
827
|
+
"name": "simulation",
|
|
828
|
+
"type": "boolean",
|
|
829
|
+
"description": "If true, only simulate the transaction without executing it",
|
|
830
|
+
"required": true
|
|
831
|
+
},
|
|
832
|
+
{
|
|
833
|
+
"name": "token",
|
|
834
|
+
"type": "string",
|
|
835
|
+
"description": "The address of the source token to bridge",
|
|
836
|
+
"required": true
|
|
837
|
+
},
|
|
838
|
+
{
|
|
839
|
+
"name": "amount",
|
|
840
|
+
"type": "number",
|
|
841
|
+
"description": "The amount of the source token to bridge",
|
|
842
|
+
"required": true
|
|
843
|
+
},
|
|
844
|
+
{
|
|
845
|
+
"name": "to",
|
|
846
|
+
"type": "string",
|
|
847
|
+
"description": "Destination wallet, chain, and token",
|
|
848
|
+
"required": true
|
|
849
|
+
}
|
|
850
|
+
]
|
|
851
|
+
},
|
|
852
|
+
{
|
|
853
|
+
"name": "token_buy",
|
|
854
|
+
"description": "Swap the specified amount of USDC for a token.",
|
|
855
|
+
"options": [
|
|
856
|
+
{
|
|
857
|
+
"name": "simulation",
|
|
858
|
+
"type": "boolean",
|
|
859
|
+
"description": "If true, only simulate the transaction without executing it",
|
|
860
|
+
"required": true
|
|
861
|
+
},
|
|
862
|
+
{
|
|
863
|
+
"name": "token",
|
|
864
|
+
"type": "string",
|
|
865
|
+
"description": "The address of the token to buy",
|
|
866
|
+
"required": true
|
|
867
|
+
},
|
|
868
|
+
{
|
|
869
|
+
"name": "amount",
|
|
870
|
+
"type": "number",
|
|
871
|
+
"description": "Amount of USDC to spend to buy the token",
|
|
872
|
+
"required": true
|
|
873
|
+
}
|
|
874
|
+
]
|
|
875
|
+
},
|
|
876
|
+
{
|
|
877
|
+
"name": "token_digest_retrieve",
|
|
878
|
+
"description": "Retrieve a token digest by token and chain.",
|
|
879
|
+
"options": [
|
|
880
|
+
{
|
|
881
|
+
"name": "token",
|
|
882
|
+
"type": "string",
|
|
883
|
+
"description": "Token address",
|
|
884
|
+
"required": true
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
"name": "chain",
|
|
888
|
+
"type": "string",
|
|
889
|
+
"description": "Blockchain network where the token exists",
|
|
890
|
+
"required": true,
|
|
891
|
+
"choices": [
|
|
892
|
+
"solana",
|
|
893
|
+
"ethereum",
|
|
894
|
+
"base",
|
|
895
|
+
"polygon",
|
|
896
|
+
"arbitrum",
|
|
897
|
+
"optimism"
|
|
898
|
+
]
|
|
899
|
+
}
|
|
900
|
+
]
|
|
901
|
+
},
|
|
902
|
+
{
|
|
903
|
+
"name": "token_holder_list",
|
|
904
|
+
"description": "List holders of a token with their balances and USD values. Shows holder distribution, concentration risk, and whale detection.",
|
|
905
|
+
"options": [
|
|
906
|
+
{
|
|
907
|
+
"name": "token",
|
|
908
|
+
"type": "string",
|
|
909
|
+
"description": "Token address to list holders for",
|
|
910
|
+
"required": true
|
|
911
|
+
},
|
|
912
|
+
{
|
|
913
|
+
"name": "chain",
|
|
914
|
+
"type": "string",
|
|
915
|
+
"description": "Blockchain where the token exists",
|
|
916
|
+
"required": true,
|
|
917
|
+
"choices": [
|
|
918
|
+
"solana",
|
|
919
|
+
"ethereum",
|
|
920
|
+
"base",
|
|
921
|
+
"polygon",
|
|
922
|
+
"arbitrum",
|
|
923
|
+
"optimism"
|
|
924
|
+
]
|
|
925
|
+
},
|
|
926
|
+
{
|
|
927
|
+
"name": "limit",
|
|
928
|
+
"type": "number",
|
|
929
|
+
"description": "Maximum number of holders to return (default 25, max 200)",
|
|
930
|
+
"required": true
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
"name": "cursor",
|
|
934
|
+
"type": "string",
|
|
935
|
+
"description": "Pagination cursor from a previous response",
|
|
936
|
+
"required": true
|
|
937
|
+
},
|
|
938
|
+
{
|
|
939
|
+
"name": "sort",
|
|
940
|
+
"type": "string",
|
|
941
|
+
"description": "Sort holders by balance (largest first) or date (newest first)",
|
|
942
|
+
"required": true
|
|
943
|
+
},
|
|
944
|
+
{
|
|
945
|
+
"name": "order",
|
|
946
|
+
"type": "string",
|
|
947
|
+
"description": "Sort direction: ascending or descending",
|
|
948
|
+
"required": true
|
|
949
|
+
}
|
|
950
|
+
]
|
|
951
|
+
},
|
|
952
|
+
{
|
|
953
|
+
"name": "token_limit_buy",
|
|
954
|
+
"description": "Create a limit buy order to swap USDC for a token at a specific price.",
|
|
955
|
+
"options": [
|
|
956
|
+
{
|
|
957
|
+
"name": "simulation",
|
|
958
|
+
"type": "boolean",
|
|
959
|
+
"description": "If true, only simulate the transaction without executing it",
|
|
960
|
+
"required": true
|
|
961
|
+
},
|
|
962
|
+
{
|
|
963
|
+
"name": "token",
|
|
964
|
+
"type": "string",
|
|
965
|
+
"description": "The address of the token to buy",
|
|
966
|
+
"required": true
|
|
967
|
+
},
|
|
968
|
+
{
|
|
969
|
+
"name": "amount",
|
|
970
|
+
"type": "number",
|
|
971
|
+
"description": "Amount of USDC to spend to buy the token",
|
|
972
|
+
"required": true
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
"name": "price",
|
|
976
|
+
"type": "number",
|
|
977
|
+
"description": "Price in dollars (USDC) to buy the token",
|
|
978
|
+
"required": true
|
|
979
|
+
}
|
|
980
|
+
]
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
"name": "token_limit_buy_recommend",
|
|
984
|
+
"description": "Parse natural language queries into structured limit buy parameters. Understands queries like 'buy BONK at $0.00001', 'limit buy 100 USDC worth of SOL when price reaches $150', or 'buy BONK at $0.00001 with 50 USDC'. Returns the recommended limit buy parameters ready to use with token_limit_buy.",
|
|
985
|
+
"options": [
|
|
986
|
+
{
|
|
987
|
+
"name": "query",
|
|
988
|
+
"type": "string",
|
|
989
|
+
"description": "Natural language description of the desired limit buy order. Examples: 'buy BONK at $0.00001', 'limit buy 100 USDC worth of SOL when price reaches $150', 'buy BONK at $0.00001 with 50 USDC'. The agent will parse token, amount, and price from this query.",
|
|
990
|
+
"required": true
|
|
991
|
+
}
|
|
992
|
+
]
|
|
993
|
+
},
|
|
994
|
+
{
|
|
995
|
+
"name": "token_limit_order_list",
|
|
996
|
+
"description": "Get limit orders for the authenticated user.",
|
|
997
|
+
"options": []
|
|
998
|
+
},
|
|
999
|
+
{
|
|
1000
|
+
"name": "token_limit_recommend",
|
|
1001
|
+
"description": "Parse natural language queries into structured limit order parameters (buy or sell). Understands queries like 'buy BONK at $0.00001', 'sell BONK at $0.00002', 'limit buy 100 USDC worth of SOL when price reaches $150', or 'sell all my BONK at $0.00002'. Automatically determines if the request is a buy or sell order and returns the appropriate parameters ready to use with token_limit_buy or token_limit_sell.",
|
|
1002
|
+
"options": [
|
|
1003
|
+
{
|
|
1004
|
+
"name": "query",
|
|
1005
|
+
"type": "string",
|
|
1006
|
+
"description": "Natural language description of the desired limit order (buy or sell). Examples: 'buy BONK at $0.00001', 'sell BONK at $0.00002', 'limit buy 100 USDC worth of SOL when price reaches $150', 'sell all my BONK at $0.00002'. The agent will parse the order type (buy/sell), token, amount, and price from this query.",
|
|
1007
|
+
"required": true
|
|
1008
|
+
}
|
|
1009
|
+
]
|
|
1010
|
+
},
|
|
1011
|
+
{
|
|
1012
|
+
"name": "token_limit_sell",
|
|
1013
|
+
"description": "Create a limit sell order to swap a token for USDC at a specific price.",
|
|
1014
|
+
"options": [
|
|
1015
|
+
{
|
|
1016
|
+
"name": "simulation",
|
|
1017
|
+
"type": "boolean",
|
|
1018
|
+
"description": "If true, only simulate the transaction without executing it",
|
|
1019
|
+
"required": true
|
|
1020
|
+
},
|
|
1021
|
+
{
|
|
1022
|
+
"name": "token",
|
|
1023
|
+
"type": "string",
|
|
1024
|
+
"description": "The address of the token to sell",
|
|
1025
|
+
"required": true
|
|
1026
|
+
},
|
|
1027
|
+
{
|
|
1028
|
+
"name": "amount",
|
|
1029
|
+
"type": "number",
|
|
1030
|
+
"description": "Amount of the token to sell",
|
|
1031
|
+
"required": true
|
|
1032
|
+
},
|
|
1033
|
+
{
|
|
1034
|
+
"name": "price",
|
|
1035
|
+
"type": "number",
|
|
1036
|
+
"description": "Price in dollars (USDC) to sell the token",
|
|
1037
|
+
"required": true
|
|
1038
|
+
}
|
|
1039
|
+
]
|
|
1040
|
+
},
|
|
1041
|
+
{
|
|
1042
|
+
"name": "token_limit_sell_recommend",
|
|
1043
|
+
"description": "Parse natural language queries into structured limit sell parameters. Understands queries like 'sell BONK at $0.00002', 'limit sell 100 SOL when price reaches $200', or 'sell all my BONK at $0.00002'. Returns the recommended limit sell parameters ready to use with token_limit_sell.",
|
|
1044
|
+
"options": [
|
|
1045
|
+
{
|
|
1046
|
+
"name": "query",
|
|
1047
|
+
"type": "string",
|
|
1048
|
+
"description": "Natural language description of the desired limit sell order. Examples: 'sell BONK at $0.00002', 'limit sell 100 SOL when price reaches $200', 'sell all my BONK at $0.00002'. The agent will parse token, amount, and price from this query.",
|
|
1049
|
+
"required": true
|
|
1050
|
+
}
|
|
1051
|
+
]
|
|
1052
|
+
},
|
|
1053
|
+
{
|
|
1054
|
+
"name": "token_liquidity_lock_list",
|
|
1055
|
+
"description": "List individual liquidity locks for a token. Shows lock details including protocol, unlock schedule, and amounts for detailed lock analysis.",
|
|
1056
|
+
"options": [
|
|
1057
|
+
{
|
|
1058
|
+
"name": "token",
|
|
1059
|
+
"type": "string",
|
|
1060
|
+
"description": "Token address to list liquidity locks for",
|
|
1061
|
+
"required": true
|
|
1062
|
+
},
|
|
1063
|
+
{
|
|
1064
|
+
"name": "chain",
|
|
1065
|
+
"type": "string",
|
|
1066
|
+
"description": "Blockchain where the token exists",
|
|
1067
|
+
"required": true,
|
|
1068
|
+
"choices": [
|
|
1069
|
+
"solana",
|
|
1070
|
+
"ethereum",
|
|
1071
|
+
"base",
|
|
1072
|
+
"polygon",
|
|
1073
|
+
"arbitrum",
|
|
1074
|
+
"optimism"
|
|
1075
|
+
]
|
|
1076
|
+
},
|
|
1077
|
+
{
|
|
1078
|
+
"name": "cursor",
|
|
1079
|
+
"type": "string",
|
|
1080
|
+
"description": "Pagination cursor from a previous response",
|
|
1081
|
+
"required": true
|
|
1082
|
+
}
|
|
1083
|
+
]
|
|
1084
|
+
},
|
|
1085
|
+
{
|
|
1086
|
+
"name": "token_liquidity_retrieve",
|
|
1087
|
+
"description": "Get liquidity metadata for a token including total and locked liquidity amounts. Critical for assessing rug pull risk and token safety.",
|
|
1088
|
+
"options": [
|
|
1089
|
+
{
|
|
1090
|
+
"name": "token",
|
|
1091
|
+
"type": "string",
|
|
1092
|
+
"description": "Token address to check liquidity for",
|
|
1093
|
+
"required": true
|
|
1094
|
+
},
|
|
1095
|
+
{
|
|
1096
|
+
"name": "chain",
|
|
1097
|
+
"type": "string",
|
|
1098
|
+
"description": "Blockchain where the token exists",
|
|
1099
|
+
"required": true,
|
|
1100
|
+
"choices": [
|
|
1101
|
+
"solana",
|
|
1102
|
+
"ethereum",
|
|
1103
|
+
"base",
|
|
1104
|
+
"polygon",
|
|
1105
|
+
"arbitrum",
|
|
1106
|
+
"optimism"
|
|
1107
|
+
]
|
|
1108
|
+
}
|
|
1109
|
+
]
|
|
1110
|
+
},
|
|
1111
|
+
{
|
|
1112
|
+
"name": "token_pair_list",
|
|
1113
|
+
"description": "List trading pairs for a token with liquidity, volume, and exchange information. Find the best pairs to trade a token.",
|
|
1114
|
+
"options": [
|
|
1115
|
+
{
|
|
1116
|
+
"name": "token",
|
|
1117
|
+
"type": "string",
|
|
1118
|
+
"description": "Token address to list pairs for",
|
|
1119
|
+
"required": true
|
|
1120
|
+
},
|
|
1121
|
+
{
|
|
1122
|
+
"name": "chain",
|
|
1123
|
+
"type": "string",
|
|
1124
|
+
"description": "Blockchain where the token exists",
|
|
1125
|
+
"required": true,
|
|
1126
|
+
"choices": [
|
|
1127
|
+
"solana",
|
|
1128
|
+
"ethereum",
|
|
1129
|
+
"base",
|
|
1130
|
+
"polygon",
|
|
1131
|
+
"arbitrum",
|
|
1132
|
+
"optimism"
|
|
1133
|
+
]
|
|
1134
|
+
},
|
|
1135
|
+
{
|
|
1136
|
+
"name": "limit",
|
|
1137
|
+
"type": "number",
|
|
1138
|
+
"description": "Maximum number of pairs to return (default 25)",
|
|
1139
|
+
"required": true
|
|
1140
|
+
}
|
|
1141
|
+
]
|
|
1142
|
+
},
|
|
1143
|
+
{
|
|
1144
|
+
"name": "token_recurring_buy",
|
|
1145
|
+
"description": "Create a recurring buy order (DCA) to swap USDC for a token at regular intervals.",
|
|
1146
|
+
"options": [
|
|
1147
|
+
{
|
|
1148
|
+
"name": "simulation",
|
|
1149
|
+
"type": "boolean",
|
|
1150
|
+
"description": "If true, only simulate the transaction without executing it",
|
|
1151
|
+
"required": true
|
|
1152
|
+
},
|
|
1153
|
+
{
|
|
1154
|
+
"name": "token",
|
|
1155
|
+
"type": "string",
|
|
1156
|
+
"description": "The address of the token to buy",
|
|
1157
|
+
"required": true
|
|
1158
|
+
},
|
|
1159
|
+
{
|
|
1160
|
+
"name": "amount",
|
|
1161
|
+
"type": "number",
|
|
1162
|
+
"description": "Amount of USDC used to buy the token per order",
|
|
1163
|
+
"required": true
|
|
1164
|
+
},
|
|
1165
|
+
{
|
|
1166
|
+
"name": "interval",
|
|
1167
|
+
"type": "string",
|
|
1168
|
+
"description": "Time interval between orders",
|
|
1169
|
+
"required": true,
|
|
1170
|
+
"choices": [
|
|
1171
|
+
"hour",
|
|
1172
|
+
"day",
|
|
1173
|
+
"week",
|
|
1174
|
+
"month"
|
|
1175
|
+
]
|
|
1176
|
+
},
|
|
1177
|
+
{
|
|
1178
|
+
"name": "numberOfOrders",
|
|
1179
|
+
"type": "number",
|
|
1180
|
+
"description": "Number of buy orders",
|
|
1181
|
+
"required": true
|
|
1182
|
+
}
|
|
1183
|
+
]
|
|
1184
|
+
},
|
|
1185
|
+
{
|
|
1186
|
+
"name": "token_recurring_order_list",
|
|
1187
|
+
"description": "Get recurring orders.",
|
|
1188
|
+
"options": []
|
|
1189
|
+
},
|
|
1190
|
+
{
|
|
1191
|
+
"name": "token_recurring_recommend",
|
|
1192
|
+
"description": "Parse natural language queries into structured recurring order (DCA) parameters (buy or sell). Understands queries like 'buy BONK daily with 50 USDC for 10 days', 'sell BONK daily, 100 tokens per order for 10 days', 'DCA 100 USDC into SOL weekly for a month', or 'recurring sell SOL every day, 0.5 SOL, 20 orders'. Automatically determines if the request is a buy or sell order and returns the appropriate parameters ready to use with token_recurring_buy or token_recurring_sell.",
|
|
1193
|
+
"options": [
|
|
1194
|
+
{
|
|
1195
|
+
"name": "query",
|
|
1196
|
+
"type": "string",
|
|
1197
|
+
"description": "Natural language description of the desired recurring order (buy or sell). Examples: 'buy BONK daily with 50 USDC for 10 days', 'sell BONK daily, 100 tokens per order for 10 days', 'DCA 100 USDC into SOL weekly for a month', 'recurring sell SOL every day, 0.5 SOL, 20 orders'. The agent will parse the order type (buy/sell), token, amount, interval, and numberOfOrders from this query.",
|
|
1198
|
+
"required": true
|
|
1199
|
+
}
|
|
1200
|
+
]
|
|
1201
|
+
},
|
|
1202
|
+
{
|
|
1203
|
+
"name": "token_recurring_sell",
|
|
1204
|
+
"description": "Create a recurring sell order (DCA) to swap a token for USDC at regular intervals.",
|
|
1205
|
+
"options": [
|
|
1206
|
+
{
|
|
1207
|
+
"name": "simulation",
|
|
1208
|
+
"type": "boolean",
|
|
1209
|
+
"description": "If true, only simulate the transaction without executing it",
|
|
1210
|
+
"required": true
|
|
1211
|
+
},
|
|
1212
|
+
{
|
|
1213
|
+
"name": "token",
|
|
1214
|
+
"type": "string",
|
|
1215
|
+
"description": "The address of the token to sell",
|
|
1216
|
+
"required": true
|
|
1217
|
+
},
|
|
1218
|
+
{
|
|
1219
|
+
"name": "amount",
|
|
1220
|
+
"type": "number",
|
|
1221
|
+
"description": "Amount of the token to sell per order",
|
|
1222
|
+
"required": true
|
|
1223
|
+
},
|
|
1224
|
+
{
|
|
1225
|
+
"name": "interval",
|
|
1226
|
+
"type": "string",
|
|
1227
|
+
"description": "Time interval between orders",
|
|
1228
|
+
"required": true,
|
|
1229
|
+
"choices": [
|
|
1230
|
+
"hour",
|
|
1231
|
+
"day",
|
|
1232
|
+
"week",
|
|
1233
|
+
"month"
|
|
1234
|
+
]
|
|
1235
|
+
},
|
|
1236
|
+
{
|
|
1237
|
+
"name": "numberOfOrders",
|
|
1238
|
+
"type": "number",
|
|
1239
|
+
"description": "Number of recurring orders to create",
|
|
1240
|
+
"required": true
|
|
1241
|
+
}
|
|
1242
|
+
]
|
|
1243
|
+
},
|
|
1244
|
+
{
|
|
1245
|
+
"name": "token_retrieve",
|
|
1246
|
+
"description": "Get detailed token information including market data, price changes, volume, trades, and holder statistics by token address",
|
|
1247
|
+
"options": [
|
|
1248
|
+
{
|
|
1249
|
+
"name": "token",
|
|
1250
|
+
"type": "string",
|
|
1251
|
+
"description": "Address of the token to retrieve",
|
|
1252
|
+
"required": true
|
|
1253
|
+
},
|
|
1254
|
+
{
|
|
1255
|
+
"name": "chain",
|
|
1256
|
+
"type": "string",
|
|
1257
|
+
"description": "Blockchain network where the token exists",
|
|
1258
|
+
"required": true,
|
|
1259
|
+
"choices": [
|
|
1260
|
+
"solana",
|
|
1261
|
+
"ethereum",
|
|
1262
|
+
"base",
|
|
1263
|
+
"polygon",
|
|
1264
|
+
"arbitrum",
|
|
1265
|
+
"optimism"
|
|
1266
|
+
]
|
|
1267
|
+
}
|
|
1268
|
+
]
|
|
1269
|
+
},
|
|
1270
|
+
{
|
|
1271
|
+
"name": "token_search",
|
|
1272
|
+
"description": "Search for tokens by name, symbol, or address. Returns matching tokens with their market data including price, volume, and liquidity.",
|
|
1273
|
+
"options": [
|
|
1274
|
+
{
|
|
1275
|
+
"name": "query",
|
|
1276
|
+
"type": "string",
|
|
1277
|
+
"description": "Search term - can be token name (e.g. 'Bitcoin'), symbol (e.g. 'BTC'), or partial match",
|
|
1278
|
+
"required": true
|
|
1279
|
+
},
|
|
1280
|
+
{
|
|
1281
|
+
"name": "chain",
|
|
1282
|
+
"type": "string",
|
|
1283
|
+
"description": "Blockchain to search on (e.g. 'solana', 'ethereum', 'base')",
|
|
1284
|
+
"required": true,
|
|
1285
|
+
"choices": [
|
|
1286
|
+
"solana",
|
|
1287
|
+
"ethereum",
|
|
1288
|
+
"base",
|
|
1289
|
+
"polygon",
|
|
1290
|
+
"arbitrum",
|
|
1291
|
+
"optimism"
|
|
1292
|
+
]
|
|
1293
|
+
},
|
|
1294
|
+
{
|
|
1295
|
+
"name": "limit",
|
|
1296
|
+
"type": "number",
|
|
1297
|
+
"description": "Maximum number of results to return (optional, defaults to 5)",
|
|
1298
|
+
"required": true
|
|
1299
|
+
}
|
|
1300
|
+
]
|
|
1301
|
+
},
|
|
1302
|
+
{
|
|
1303
|
+
"name": "token_sell",
|
|
1304
|
+
"description": "Swap the specified amount of tokens for USDC.",
|
|
1305
|
+
"options": [
|
|
1306
|
+
{
|
|
1307
|
+
"name": "simulation",
|
|
1308
|
+
"type": "boolean",
|
|
1309
|
+
"description": "If true, only simulate the transaction without executing it",
|
|
1310
|
+
"required": true
|
|
1311
|
+
},
|
|
1312
|
+
{
|
|
1313
|
+
"name": "token",
|
|
1314
|
+
"type": "string",
|
|
1315
|
+
"description": "The address of the token to sell",
|
|
1316
|
+
"required": true
|
|
1317
|
+
},
|
|
1318
|
+
{
|
|
1319
|
+
"name": "amount",
|
|
1320
|
+
"type": "number",
|
|
1321
|
+
"description": "Amount of token to sell",
|
|
1322
|
+
"required": true
|
|
1323
|
+
}
|
|
1324
|
+
]
|
|
1325
|
+
},
|
|
1326
|
+
{
|
|
1327
|
+
"name": "token_swap",
|
|
1328
|
+
"description": "Swap the specified amount of input token for output token.",
|
|
1329
|
+
"options": [
|
|
1330
|
+
{
|
|
1331
|
+
"name": "simulation",
|
|
1332
|
+
"type": "boolean",
|
|
1333
|
+
"description": "If true, only simulate the transaction without executing it",
|
|
1334
|
+
"required": true
|
|
1335
|
+
},
|
|
1336
|
+
{
|
|
1337
|
+
"name": "input",
|
|
1338
|
+
"type": "string",
|
|
1339
|
+
"description": "The address of the input token to swap",
|
|
1340
|
+
"required": true
|
|
1341
|
+
},
|
|
1342
|
+
{
|
|
1343
|
+
"name": "output",
|
|
1344
|
+
"type": "string",
|
|
1345
|
+
"description": "The address of the output token to receive",
|
|
1346
|
+
"required": true
|
|
1347
|
+
},
|
|
1348
|
+
{
|
|
1349
|
+
"name": "amount",
|
|
1350
|
+
"type": "number",
|
|
1351
|
+
"description": "The amount of the input token to swap",
|
|
1352
|
+
"required": true
|
|
1353
|
+
}
|
|
1354
|
+
]
|
|
1355
|
+
},
|
|
1356
|
+
{
|
|
1357
|
+
"name": "token_swap_recommend",
|
|
1358
|
+
"description": "Parse natural language queries into structured swap parameters for swapping tokens on Solana. Understands queries like 'swap 100 USDC for SOL', 'swap all my USDC for BONK', or '50 SOL to USDC'. Returns the recommended swap parameters ready to use with token_swap.",
|
|
1359
|
+
"options": [
|
|
1360
|
+
{
|
|
1361
|
+
"name": "query",
|
|
1362
|
+
"type": "string",
|
|
1363
|
+
"description": "Natural language description of the desired swap on Solana. Examples: 'swap 100 USDC for SOL', 'swap all my USDC for BONK', '50 SOL to USDC', 'convert my SOL to USDC'. The agent will parse tokens and amounts from this query.",
|
|
1364
|
+
"required": true
|
|
1365
|
+
}
|
|
1366
|
+
]
|
|
1367
|
+
},
|
|
1368
|
+
{
|
|
1369
|
+
"name": "token_top_trader_list",
|
|
1370
|
+
"description": "Get top traders for a token ranked by volume. Shows buy/sell activity, realized profit, and current balance for smart money analysis.",
|
|
1371
|
+
"options": [
|
|
1372
|
+
{
|
|
1373
|
+
"name": "token",
|
|
1374
|
+
"type": "string",
|
|
1375
|
+
"description": "Token address to get top traders for",
|
|
1376
|
+
"required": true
|
|
1377
|
+
},
|
|
1378
|
+
{
|
|
1379
|
+
"name": "chain",
|
|
1380
|
+
"type": "string",
|
|
1381
|
+
"description": "Blockchain where the token exists",
|
|
1382
|
+
"required": true,
|
|
1383
|
+
"choices": [
|
|
1384
|
+
"solana",
|
|
1385
|
+
"ethereum",
|
|
1386
|
+
"base",
|
|
1387
|
+
"polygon",
|
|
1388
|
+
"arbitrum",
|
|
1389
|
+
"optimism"
|
|
1390
|
+
]
|
|
1391
|
+
},
|
|
1392
|
+
{
|
|
1393
|
+
"name": "period",
|
|
1394
|
+
"type": "string",
|
|
1395
|
+
"description": "Trading period to analyze",
|
|
1396
|
+
"required": true,
|
|
1397
|
+
"choices": [
|
|
1398
|
+
"day",
|
|
1399
|
+
"week",
|
|
1400
|
+
"month",
|
|
1401
|
+
"year"
|
|
1402
|
+
]
|
|
1403
|
+
},
|
|
1404
|
+
{
|
|
1405
|
+
"name": "limit",
|
|
1406
|
+
"type": "number",
|
|
1407
|
+
"description": "Maximum number of traders to return (default 25)",
|
|
1408
|
+
"required": true
|
|
1409
|
+
},
|
|
1410
|
+
{
|
|
1411
|
+
"name": "page",
|
|
1412
|
+
"type": "number",
|
|
1413
|
+
"description": "Page number for pagination (default 1)",
|
|
1414
|
+
"required": true
|
|
1415
|
+
}
|
|
1416
|
+
]
|
|
1417
|
+
},
|
|
1418
|
+
{
|
|
1419
|
+
"name": "token_transfer",
|
|
1420
|
+
"description": "Transfer tokens from your wallet to another address. This tool will build the transaction, sign it using the authenticated user's wallet, and execute it. The recipient's token account will be automatically created if it doesn't exist.",
|
|
1421
|
+
"options": [
|
|
1422
|
+
{
|
|
1423
|
+
"name": "simulation",
|
|
1424
|
+
"type": "boolean",
|
|
1425
|
+
"description": "If true, only simulate the transaction without executing it",
|
|
1426
|
+
"required": true
|
|
1427
|
+
},
|
|
1428
|
+
{
|
|
1429
|
+
"name": "token",
|
|
1430
|
+
"type": "string",
|
|
1431
|
+
"description": "The address of the token to transfer",
|
|
1432
|
+
"required": true
|
|
1433
|
+
},
|
|
1434
|
+
{
|
|
1435
|
+
"name": "to",
|
|
1436
|
+
"type": "string",
|
|
1437
|
+
"description": "The recipient's wallet address",
|
|
1438
|
+
"required": true
|
|
1439
|
+
},
|
|
1440
|
+
{
|
|
1441
|
+
"name": "amount",
|
|
1442
|
+
"type": "number",
|
|
1443
|
+
"description": "The amount of tokens to transfer (in human-readable units, e.g., 1.5)",
|
|
1444
|
+
"required": true
|
|
1445
|
+
}
|
|
1446
|
+
]
|
|
1447
|
+
},
|
|
1448
|
+
{
|
|
1449
|
+
"name": "token_trending_list",
|
|
1450
|
+
"description": "Get currently trending tokens on a blockchain. Perfect for discovering what's hot right now.",
|
|
1451
|
+
"options": [
|
|
1452
|
+
{
|
|
1453
|
+
"name": "chain",
|
|
1454
|
+
"type": "string",
|
|
1455
|
+
"description": "Blockchain to get trending tokens from (e.g. 'solana', 'ethereum', 'base')",
|
|
1456
|
+
"required": true,
|
|
1457
|
+
"choices": [
|
|
1458
|
+
"solana",
|
|
1459
|
+
"ethereum",
|
|
1460
|
+
"base",
|
|
1461
|
+
"polygon",
|
|
1462
|
+
"arbitrum",
|
|
1463
|
+
"optimism"
|
|
1464
|
+
]
|
|
1465
|
+
},
|
|
1466
|
+
{
|
|
1467
|
+
"name": "limit",
|
|
1468
|
+
"type": "number",
|
|
1469
|
+
"description": "Number of results per page",
|
|
1470
|
+
"required": true
|
|
1471
|
+
},
|
|
1472
|
+
{
|
|
1473
|
+
"name": "page",
|
|
1474
|
+
"type": "number",
|
|
1475
|
+
"description": "The page number of results",
|
|
1476
|
+
"required": true
|
|
1477
|
+
}
|
|
1478
|
+
]
|
|
1479
|
+
},
|
|
1480
|
+
{
|
|
1481
|
+
"name": "user_retrieve",
|
|
1482
|
+
"description": "Retrieve the currently authenticated user from the session context.",
|
|
1483
|
+
"options": []
|
|
1484
|
+
},
|
|
1485
|
+
{
|
|
1486
|
+
"name": "user_update",
|
|
1487
|
+
"description": "Update user fields in the database. This is a PUT-style update requiring all updatable fields (developer, firstName, lastName, username) to be sent. You must first call user_retrieve to get the current user state, then update with all four fields.",
|
|
1488
|
+
"options": [
|
|
1489
|
+
{
|
|
1490
|
+
"name": "developer",
|
|
1491
|
+
"type": "boolean",
|
|
1492
|
+
"description": "Whether the user is a developer",
|
|
1493
|
+
"required": true
|
|
1494
|
+
},
|
|
1495
|
+
{
|
|
1496
|
+
"name": "firstName",
|
|
1497
|
+
"type": "string",
|
|
1498
|
+
"description": "The user's first name, or null to clear it",
|
|
1499
|
+
"required": true
|
|
1500
|
+
},
|
|
1501
|
+
{
|
|
1502
|
+
"name": "lastName",
|
|
1503
|
+
"type": "string",
|
|
1504
|
+
"description": "The user's last name, or null to clear it",
|
|
1505
|
+
"required": true
|
|
1506
|
+
},
|
|
1507
|
+
{
|
|
1508
|
+
"name": "username",
|
|
1509
|
+
"type": "string",
|
|
1510
|
+
"description": "The user's username (3-20 lowercase letters, numbers, and underscores, must start with a letter), or null to clear it",
|
|
1511
|
+
"required": true
|
|
1512
|
+
}
|
|
1513
|
+
]
|
|
1514
|
+
},
|
|
1515
|
+
{
|
|
1516
|
+
"name": "user_wallet_list",
|
|
1517
|
+
"description": "List all wallets for the authenticated user.",
|
|
1518
|
+
"options": []
|
|
1519
|
+
},
|
|
1520
|
+
{
|
|
1521
|
+
"name": "user_wallet_retrieve",
|
|
1522
|
+
"description": "Retrieve a wallet by address for the authenticated user. Use user_wallet_list to see your wallets.",
|
|
1523
|
+
"options": [
|
|
1524
|
+
{
|
|
1525
|
+
"name": "address",
|
|
1526
|
+
"type": "string",
|
|
1527
|
+
"description": "Wallet address to retrieve",
|
|
1528
|
+
"required": true
|
|
1529
|
+
}
|
|
1530
|
+
]
|
|
1531
|
+
},
|
|
1532
|
+
{
|
|
1533
|
+
"name": "wallet_digest_retrieve",
|
|
1534
|
+
"description": "Retrieve a wallet digest by wallet address and chain.",
|
|
1535
|
+
"options": [
|
|
1536
|
+
{
|
|
1537
|
+
"name": "wallet",
|
|
1538
|
+
"type": "string",
|
|
1539
|
+
"description": "Wallet address",
|
|
1540
|
+
"required": true
|
|
1541
|
+
},
|
|
1542
|
+
{
|
|
1543
|
+
"name": "chain",
|
|
1544
|
+
"type": "string",
|
|
1545
|
+
"description": "Blockchain network",
|
|
1546
|
+
"required": true,
|
|
1547
|
+
"choices": [
|
|
1548
|
+
"solana",
|
|
1549
|
+
"ethereum",
|
|
1550
|
+
"base",
|
|
1551
|
+
"polygon",
|
|
1552
|
+
"arbitrum",
|
|
1553
|
+
"optimism"
|
|
1554
|
+
]
|
|
1555
|
+
}
|
|
1556
|
+
]
|
|
1557
|
+
},
|
|
1558
|
+
{
|
|
1559
|
+
"name": "x402_request",
|
|
1560
|
+
"description": "Make an HTTP request to an x402-protected endpoint. Automatically handles payment transactions when a 402 Payment Required response is received.",
|
|
1561
|
+
"options": [
|
|
1562
|
+
{
|
|
1563
|
+
"name": "method",
|
|
1564
|
+
"type": "string",
|
|
1565
|
+
"description": "HTTP method",
|
|
1566
|
+
"required": true,
|
|
1567
|
+
"choices": [
|
|
1568
|
+
"GET",
|
|
1569
|
+
"POST",
|
|
1570
|
+
"PUT",
|
|
1571
|
+
"PATCH",
|
|
1572
|
+
"DELETE"
|
|
1573
|
+
]
|
|
1574
|
+
},
|
|
1575
|
+
{
|
|
1576
|
+
"name": "url",
|
|
1577
|
+
"type": "string",
|
|
1578
|
+
"description": "Full URL of the x402-protected endpoint (e.g., 'https://moon-iq.com/api/x402/tools/market_digest_retrieve')",
|
|
1579
|
+
"required": true
|
|
1580
|
+
},
|
|
1581
|
+
{
|
|
1582
|
+
"name": "body",
|
|
1583
|
+
"type": "string",
|
|
1584
|
+
"description": "Request body (for POST, PUT, PATCH)",
|
|
1585
|
+
"required": true
|
|
1586
|
+
},
|
|
1587
|
+
{
|
|
1588
|
+
"name": "params",
|
|
1589
|
+
"type": "string",
|
|
1590
|
+
"description": "Query parameters",
|
|
1591
|
+
"required": true
|
|
1592
|
+
}
|
|
1593
|
+
]
|
|
1594
|
+
}
|
|
1595
|
+
];
|
|
1596
|
+
|
|
1597
|
+
// src/client.ts
|
|
1598
|
+
async function callToolWithToken(baseUrl, token, toolName, params) {
|
|
1599
|
+
const res = await fetch(`${baseUrl}/api/tools/${toolName}`, {
|
|
1600
|
+
method: "POST",
|
|
1601
|
+
headers: {
|
|
1602
|
+
"Content-Type": "application/json",
|
|
1603
|
+
Authorization: `Bearer ${token}`
|
|
1604
|
+
},
|
|
1605
|
+
body: JSON.stringify(params)
|
|
1606
|
+
});
|
|
1607
|
+
const data = await res.json();
|
|
1608
|
+
return { data, status: res.status };
|
|
1609
|
+
}
|
|
1610
|
+
async function callToolWithAuth(baseUrl, toolName, params) {
|
|
1611
|
+
let token = await getValidToken();
|
|
1612
|
+
if (!token) {
|
|
1613
|
+
throw new Error(
|
|
1614
|
+
"Not logged in. Run `mooniq login` first and ensure ~/.config/moon-iq/config.json has baseUrl and clientId."
|
|
1615
|
+
);
|
|
1616
|
+
}
|
|
1617
|
+
let result = await callToolWithToken(baseUrl, token, toolName, params);
|
|
1618
|
+
if (result.status === 401) {
|
|
1619
|
+
const creds = getCredentials();
|
|
1620
|
+
const config = getConfig();
|
|
1621
|
+
if (creds?.refreshToken && config && config.baseUrl === creds.baseUrl) {
|
|
1622
|
+
try {
|
|
1623
|
+
const newCreds = await refreshCredentials(creds, config);
|
|
1624
|
+
result = await callToolWithToken(
|
|
1625
|
+
baseUrl,
|
|
1626
|
+
newCreds.accessToken,
|
|
1627
|
+
toolName,
|
|
1628
|
+
params
|
|
1629
|
+
);
|
|
1630
|
+
} catch {
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
if (result.status < 200 || result.status >= 300) {
|
|
1635
|
+
const err = result.data;
|
|
1636
|
+
throw new Error(err?.error ?? "Tool call failed");
|
|
1637
|
+
}
|
|
1638
|
+
return result.data;
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
// src/format.ts
|
|
1642
|
+
var useColor = !process.env.NO_COLOR && process.stdout.isTTY === true;
|
|
1643
|
+
function ansi(code) {
|
|
1644
|
+
return (text) => useColor ? `${code}${text}\x1B[0m` : text;
|
|
1645
|
+
}
|
|
1646
|
+
var color = {
|
|
1647
|
+
bold: ansi("\x1B[1m"),
|
|
1648
|
+
dim: ansi("\x1B[2m"),
|
|
1649
|
+
green: ansi("\x1B[32m"),
|
|
1650
|
+
yellow: ansi("\x1B[33m"),
|
|
1651
|
+
cyan: ansi("\x1B[36m"),
|
|
1652
|
+
red: ansi("\x1B[31m")
|
|
1653
|
+
};
|
|
1654
|
+
var MAX_COL_WIDTH = 40;
|
|
1655
|
+
function truncate(str, max) {
|
|
1656
|
+
return str.length > max ? str.slice(0, max - 1) + "\u2026" : str;
|
|
1657
|
+
}
|
|
1658
|
+
function renderArrayTable(rows) {
|
|
1659
|
+
if (rows.length === 0) return "";
|
|
1660
|
+
const keys = Object.keys(rows[0]);
|
|
1661
|
+
const widths = keys.map((k) => k.length);
|
|
1662
|
+
const stringRows = rows.map(
|
|
1663
|
+
(row) => keys.map((k, i) => {
|
|
1664
|
+
const val = truncate(String(row[k] ?? ""), MAX_COL_WIDTH);
|
|
1665
|
+
widths[i] = Math.max(widths[i], val.length);
|
|
1666
|
+
return val;
|
|
1667
|
+
})
|
|
1668
|
+
);
|
|
1669
|
+
for (let i = 0; i < widths.length; i++) {
|
|
1670
|
+
widths[i] = Math.min(widths[i], MAX_COL_WIDTH);
|
|
1671
|
+
}
|
|
1672
|
+
const header = keys.map((k, i) => color.bold(k.padEnd(widths[i]))).join(" ");
|
|
1673
|
+
const separator = color.dim(widths.map((w) => "\u2500".repeat(w)).join("\u2500\u2500"));
|
|
1674
|
+
const body = stringRows.map((row) => row.map((val, i) => val.padEnd(widths[i])).join(" ")).join("\n");
|
|
1675
|
+
return `${header}
|
|
1676
|
+
${separator}
|
|
1677
|
+
${body}`;
|
|
1678
|
+
}
|
|
1679
|
+
function renderObjectTable(obj) {
|
|
1680
|
+
const entries = Object.entries(obj);
|
|
1681
|
+
const maxKey = Math.min(
|
|
1682
|
+
MAX_COL_WIDTH,
|
|
1683
|
+
Math.max(...entries.map(([k]) => k.length))
|
|
1684
|
+
);
|
|
1685
|
+
return entries.map(([k, v]) => `${color.bold(k.padEnd(maxKey))} ${String(v ?? "")}`).join("\n");
|
|
1686
|
+
}
|
|
1687
|
+
function renderTable(data) {
|
|
1688
|
+
if (Array.isArray(data)) {
|
|
1689
|
+
if (data.length === 0) return color.dim("(empty)");
|
|
1690
|
+
if (typeof data[0] === "object" && data[0] !== null) {
|
|
1691
|
+
return renderArrayTable(data);
|
|
1692
|
+
}
|
|
1693
|
+
return data.map(String).join("\n");
|
|
1694
|
+
}
|
|
1695
|
+
if (typeof data === "object" && data !== null) {
|
|
1696
|
+
return renderObjectTable(data);
|
|
1697
|
+
}
|
|
1698
|
+
return String(data);
|
|
1699
|
+
}
|
|
1700
|
+
function formatOutput(result, format) {
|
|
1701
|
+
if (format === "compact") {
|
|
1702
|
+
return JSON.stringify(result);
|
|
1703
|
+
}
|
|
1704
|
+
if (format === "table") {
|
|
1705
|
+
const value = typeof result === "object" && result !== null && "data" in result ? result.data : result;
|
|
1706
|
+
return renderTable(value);
|
|
1707
|
+
}
|
|
1708
|
+
return JSON.stringify(result, null, 2);
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
// src/version-check.ts
|
|
1712
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
1713
|
+
import { join as join2 } from "path";
|
|
1714
|
+
import { homedir as homedir2 } from "os";
|
|
1715
|
+
var CACHE_DIR = join2(homedir2(), ".config", "moon-iq");
|
|
1716
|
+
var CACHE_FILE = join2(CACHE_DIR, "update-check.json");
|
|
1717
|
+
var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
|
|
1718
|
+
var FETCH_TIMEOUT_MS = 3e3;
|
|
1719
|
+
function readCache() {
|
|
1720
|
+
try {
|
|
1721
|
+
const raw = readFileSync2(CACHE_FILE, "utf-8");
|
|
1722
|
+
return JSON.parse(raw);
|
|
1723
|
+
} catch {
|
|
1724
|
+
return null;
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
function writeCache(data) {
|
|
1728
|
+
try {
|
|
1729
|
+
mkdirSync2(CACHE_DIR, { recursive: true });
|
|
1730
|
+
writeFileSync2(CACHE_FILE, JSON.stringify(data));
|
|
1731
|
+
} catch {
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
function isNewer(latest, current) {
|
|
1735
|
+
const a = latest.split(".").map(Number);
|
|
1736
|
+
const b = current.split(".").map(Number);
|
|
1737
|
+
for (let i = 0; i < 3; i++) {
|
|
1738
|
+
if ((a[i] ?? 0) > (b[i] ?? 0)) return true;
|
|
1739
|
+
if ((a[i] ?? 0) < (b[i] ?? 0)) return false;
|
|
1740
|
+
}
|
|
1741
|
+
return false;
|
|
1742
|
+
}
|
|
1743
|
+
function formatNotice(current, latest) {
|
|
1744
|
+
return [
|
|
1745
|
+
"",
|
|
1746
|
+
color.yellow(`Update available: ${current} \u2192 ${latest}`),
|
|
1747
|
+
color.dim("Run `npm i -g @moonpay/cli` to update."),
|
|
1748
|
+
""
|
|
1749
|
+
].join("\n");
|
|
1750
|
+
}
|
|
1751
|
+
function startVersionCheck(currentVersion) {
|
|
1752
|
+
let notice = null;
|
|
1753
|
+
const cache = readCache();
|
|
1754
|
+
if (cache && Date.now() - cache.checkedAt < ONE_DAY_MS) {
|
|
1755
|
+
if (isNewer(cache.latest, currentVersion)) {
|
|
1756
|
+
notice = formatNotice(currentVersion, cache.latest);
|
|
1757
|
+
}
|
|
1758
|
+
return () => notice;
|
|
1759
|
+
}
|
|
1760
|
+
const controller = new AbortController();
|
|
1761
|
+
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
1762
|
+
fetch("https://registry.npmjs.org/@moonpay%2fcli/latest", {
|
|
1763
|
+
signal: controller.signal
|
|
1764
|
+
}).then((res) => res.json()).then((data) => {
|
|
1765
|
+
const latest = data.version;
|
|
1766
|
+
if (latest) {
|
|
1767
|
+
writeCache({ latest, checkedAt: Date.now() });
|
|
1768
|
+
if (isNewer(latest, currentVersion)) {
|
|
1769
|
+
notice = formatNotice(currentVersion, latest);
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
}).catch(() => {
|
|
1773
|
+
}).finally(() => clearTimeout(timeout));
|
|
1774
|
+
return () => notice;
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
// src/index.ts
|
|
1778
|
+
var require2 = createRequire(import.meta.url);
|
|
1779
|
+
var { version } = require2("../package.json");
|
|
1780
|
+
var getUpdateNotice = startVersionCheck(version);
|
|
1781
|
+
var DEFAULT_BASE_URL = "https://moon-iq.com";
|
|
1782
|
+
var program = new Command();
|
|
1783
|
+
program.name("mooniq").description("Moon IQ CLI").version(version).option("-f, --format <type>", "Output format: json, compact, or table", "json");
|
|
1784
|
+
function getFormat() {
|
|
1785
|
+
const fmt = program.opts().format;
|
|
1786
|
+
if (fmt === "json" || fmt === "compact" || fmt === "table") return fmt;
|
|
1787
|
+
return "json";
|
|
1788
|
+
}
|
|
1789
|
+
function printResult(result) {
|
|
1790
|
+
console.log(formatOutput(result, getFormat()));
|
|
1791
|
+
}
|
|
1792
|
+
function printUpdateNotice() {
|
|
1793
|
+
const notice = getUpdateNotice();
|
|
1794
|
+
if (notice) process.stderr.write(notice);
|
|
1795
|
+
}
|
|
1796
|
+
function resolveBaseUrl() {
|
|
1797
|
+
const config = getConfig();
|
|
1798
|
+
const creds = getCredentials();
|
|
1799
|
+
return config?.baseUrl ?? creds?.baseUrl ?? DEFAULT_BASE_URL;
|
|
1800
|
+
}
|
|
1801
|
+
program.command("login").description("Log in to Moon IQ via OAuth").action(async () => {
|
|
1802
|
+
const config = getConfigOrDefault();
|
|
1803
|
+
try {
|
|
1804
|
+
await login(config);
|
|
1805
|
+
console.log("Logged in successfully.");
|
|
1806
|
+
} catch (error) {
|
|
1807
|
+
console.error("Login failed:", error.message);
|
|
1808
|
+
process.exit(1);
|
|
1809
|
+
}
|
|
1810
|
+
});
|
|
1811
|
+
program.command("logout").description("Log out and clear stored credentials").action(() => {
|
|
1812
|
+
clearCredentials();
|
|
1813
|
+
console.log("Logged out.");
|
|
1814
|
+
});
|
|
1815
|
+
program.command("whoami").description("Show current user info").action(async () => {
|
|
1816
|
+
let token = await getValidToken();
|
|
1817
|
+
if (!token) {
|
|
1818
|
+
console.error("Not logged in. Run `mooniq login` first.");
|
|
1819
|
+
process.exit(1);
|
|
1820
|
+
}
|
|
1821
|
+
const creds = getCredentials();
|
|
1822
|
+
const config = getConfig();
|
|
1823
|
+
const baseUrl = resolveBaseUrl();
|
|
1824
|
+
let res = await fetch(`${baseUrl}/api/oauth/userinfo`, {
|
|
1825
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
1826
|
+
});
|
|
1827
|
+
if (res.status === 401 && creds?.refreshToken && config && config.baseUrl === creds.baseUrl) {
|
|
1828
|
+
try {
|
|
1829
|
+
const newCreds = await refreshCredentials(creds, config);
|
|
1830
|
+
res = await fetch(`${baseUrl}/api/oauth/userinfo`, {
|
|
1831
|
+
headers: { Authorization: `Bearer ${newCreds.accessToken}` }
|
|
1832
|
+
});
|
|
1833
|
+
} catch {
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
if (!res.ok) {
|
|
1837
|
+
console.error("Failed to fetch user info:", res.status);
|
|
1838
|
+
process.exit(1);
|
|
1839
|
+
}
|
|
1840
|
+
const userinfo = await res.json();
|
|
1841
|
+
const { id, wallets, emails, phoneNumbers, developer, ironId, ...display } = userinfo;
|
|
1842
|
+
printResult(display);
|
|
1843
|
+
printUpdateNotice();
|
|
1844
|
+
});
|
|
1845
|
+
program.command("run <tool_name>").description("Run a tool by name. Use --input for JSON params.").option(
|
|
1846
|
+
"-i, --input <json>",
|
|
1847
|
+
"Input parameters as JSON (omit for empty params)"
|
|
1848
|
+
).addHelpText(
|
|
1849
|
+
"after",
|
|
1850
|
+
`
|
|
1851
|
+
Examples:
|
|
1852
|
+
mooniq run user_retrieve # no params
|
|
1853
|
+
mooniq run token_search --input '{"query":"SOL","chain":"solana"}' # with params
|
|
1854
|
+
`
|
|
1855
|
+
).action(async (toolName, options) => {
|
|
1856
|
+
const toolNames = TOOL_NAMES;
|
|
1857
|
+
if (!toolNames.includes(toolName)) {
|
|
1858
|
+
console.error(`Unknown tool: ${toolName}`);
|
|
1859
|
+
console.error(`Available tools: ${toolNames.slice(0, 10).join(", ")}...`);
|
|
1860
|
+
process.exit(1);
|
|
1861
|
+
}
|
|
1862
|
+
let params = {};
|
|
1863
|
+
if (options.input) {
|
|
1864
|
+
try {
|
|
1865
|
+
params = JSON.parse(options.input);
|
|
1866
|
+
} catch {
|
|
1867
|
+
console.error("Invalid JSON in --input");
|
|
1868
|
+
process.exit(1);
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
try {
|
|
1872
|
+
const baseUrl = resolveBaseUrl();
|
|
1873
|
+
const result = await callToolWithAuth(baseUrl, toolName, params);
|
|
1874
|
+
printResult(result);
|
|
1875
|
+
printUpdateNotice();
|
|
1876
|
+
} catch (error) {
|
|
1877
|
+
console.error("Tool call failed:", error.message);
|
|
1878
|
+
process.exit(1);
|
|
1879
|
+
}
|
|
1880
|
+
});
|
|
1881
|
+
program.command("tools").description("List available tools").option("-s, --search <query>", "Filter tools by name or description").option("-c, --category <category>", "Filter tools by category").action((options) => {
|
|
1882
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1883
|
+
for (const tool of TOOL_DEFINITIONS) {
|
|
1884
|
+
const category = tool.name.split("_")[0];
|
|
1885
|
+
if (!groups.has(category)) groups.set(category, []);
|
|
1886
|
+
groups.get(category).push(tool);
|
|
1887
|
+
}
|
|
1888
|
+
const categoryFilter = options.category?.toLowerCase();
|
|
1889
|
+
const searchQuery = options.search?.toLowerCase();
|
|
1890
|
+
let categories = [...groups.keys()].sort();
|
|
1891
|
+
if (categoryFilter) {
|
|
1892
|
+
if (!groups.has(categoryFilter)) {
|
|
1893
|
+
console.error(
|
|
1894
|
+
`Unknown category: ${categoryFilter}
|
|
1895
|
+
Available categories: ${[...groups.keys()].sort().join(", ")}`
|
|
1896
|
+
);
|
|
1897
|
+
process.exit(1);
|
|
1898
|
+
}
|
|
1899
|
+
categories = [categoryFilter];
|
|
1900
|
+
}
|
|
1901
|
+
let totalCount = 0;
|
|
1902
|
+
const lines = [];
|
|
1903
|
+
for (const cat of categories) {
|
|
1904
|
+
const tools = groups.get(cat);
|
|
1905
|
+
const filtered = searchQuery ? tools.filter(
|
|
1906
|
+
(t) => t.name.toLowerCase().includes(searchQuery) || t.description.toLowerCase().includes(searchQuery)
|
|
1907
|
+
) : tools;
|
|
1908
|
+
if (filtered.length === 0) continue;
|
|
1909
|
+
const maxName = Math.max(...filtered.map((t) => t.name.length));
|
|
1910
|
+
if (lines.length > 0) lines.push("");
|
|
1911
|
+
lines.push(color.bold(color.cyan(cat.toUpperCase())));
|
|
1912
|
+
for (const tool of filtered) {
|
|
1913
|
+
lines.push(
|
|
1914
|
+
` ${color.green(tool.name.padEnd(maxName))} ${color.dim(tool.description)}`
|
|
1915
|
+
);
|
|
1916
|
+
totalCount++;
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
if (totalCount === 0) {
|
|
1920
|
+
const msg = searchQuery ? `No tools matching "${options.search}".` : `No tools found.`;
|
|
1921
|
+
console.error(msg);
|
|
1922
|
+
console.error(
|
|
1923
|
+
`Available categories: ${[...groups.keys()].sort().join(", ")}`
|
|
1924
|
+
);
|
|
1925
|
+
process.exit(1);
|
|
1926
|
+
}
|
|
1927
|
+
lines.push("");
|
|
1928
|
+
lines.push(color.dim(`${totalCount} tool${totalCount === 1 ? "" : "s"}`));
|
|
1929
|
+
console.log(lines.join("\n"));
|
|
1930
|
+
printUpdateNotice();
|
|
1931
|
+
});
|
|
1932
|
+
function buildToolCommands(program2) {
|
|
1933
|
+
const root = { children: /* @__PURE__ */ new Map() };
|
|
1934
|
+
for (const tool of TOOL_DEFINITIONS) {
|
|
1935
|
+
const parts = tool.name.split("_");
|
|
1936
|
+
let current = root;
|
|
1937
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1938
|
+
const part = parts[i];
|
|
1939
|
+
if (!current.children.has(part)) {
|
|
1940
|
+
current.children.set(part, { children: /* @__PURE__ */ new Map() });
|
|
1941
|
+
}
|
|
1942
|
+
current = current.children.get(part);
|
|
1943
|
+
if (i === parts.length - 1) {
|
|
1944
|
+
current.tool = tool;
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
function createCommands(node, parent, path2) {
|
|
1949
|
+
for (const [name, child] of node.children) {
|
|
1950
|
+
const currentPath = [...path2, name];
|
|
1951
|
+
if (child.tool) {
|
|
1952
|
+
const cmd = parent.command(name).description(child.tool.description || `Run ${child.tool.name}`);
|
|
1953
|
+
for (const opt of child.tool.options) {
|
|
1954
|
+
const optionFlag = createOptionFlag(opt);
|
|
1955
|
+
const option = new Option(optionFlag, opt.description);
|
|
1956
|
+
if (opt.choices) {
|
|
1957
|
+
option.choices(opt.choices);
|
|
1958
|
+
}
|
|
1959
|
+
if (opt.required) {
|
|
1960
|
+
cmd.addOption(option);
|
|
1961
|
+
} else {
|
|
1962
|
+
cmd.addOption(option);
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
const toolName = child.tool.name;
|
|
1966
|
+
cmd.action(async (options) => {
|
|
1967
|
+
const params = buildParams(child.tool.options, options);
|
|
1968
|
+
const baseUrl = resolveBaseUrl();
|
|
1969
|
+
try {
|
|
1970
|
+
const result = await callToolWithAuth(baseUrl, toolName, params);
|
|
1971
|
+
printResult(result);
|
|
1972
|
+
printUpdateNotice();
|
|
1973
|
+
} catch (error) {
|
|
1974
|
+
console.error("Tool call failed:", error.message);
|
|
1975
|
+
process.exit(1);
|
|
1976
|
+
}
|
|
1977
|
+
});
|
|
1978
|
+
if (child.children.size > 0) {
|
|
1979
|
+
createCommands(child, cmd, currentPath);
|
|
1980
|
+
}
|
|
1981
|
+
} else if (child.children.size > 0) {
|
|
1982
|
+
const cmd = parent.command(name).description(`${name} commands`);
|
|
1983
|
+
createCommands(child, cmd, currentPath);
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
createCommands(root, program2, []);
|
|
1988
|
+
}
|
|
1989
|
+
function createOptionFlag(opt) {
|
|
1990
|
+
const kebabName = opt.name.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
1991
|
+
if (opt.type === "boolean") {
|
|
1992
|
+
return `--${kebabName}`;
|
|
1993
|
+
}
|
|
1994
|
+
const valuePlaceholder = `<${opt.name}>`;
|
|
1995
|
+
return `--${kebabName} ${valuePlaceholder}`;
|
|
1996
|
+
}
|
|
1997
|
+
function buildParams(toolOptions, cmdOptions) {
|
|
1998
|
+
const params = {};
|
|
1999
|
+
for (const opt of toolOptions) {
|
|
2000
|
+
const kebabName = opt.name.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
2001
|
+
const camelName = kebabName.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
2002
|
+
const value = cmdOptions[camelName] ?? cmdOptions[opt.name];
|
|
2003
|
+
if (value !== void 0) {
|
|
2004
|
+
if (opt.type === "number" && typeof value === "string") {
|
|
2005
|
+
params[opt.name] = parseFloat(value);
|
|
2006
|
+
} else if (opt.type === "boolean") {
|
|
2007
|
+
params[opt.name] = value === true || value === "true";
|
|
2008
|
+
} else if (typeof value === "string") {
|
|
2009
|
+
try {
|
|
2010
|
+
const parsed = JSON.parse(value);
|
|
2011
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
2012
|
+
params[opt.name] = parsed;
|
|
2013
|
+
} else {
|
|
2014
|
+
params[opt.name] = value;
|
|
2015
|
+
}
|
|
2016
|
+
} catch {
|
|
2017
|
+
params[opt.name] = value;
|
|
2018
|
+
}
|
|
2019
|
+
} else {
|
|
2020
|
+
params[opt.name] = value;
|
|
2021
|
+
}
|
|
2022
|
+
} else {
|
|
2023
|
+
params[opt.name] = opt.type === "boolean" ? false : null;
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
return params;
|
|
2027
|
+
}
|
|
2028
|
+
buildToolCommands(program);
|
|
2029
|
+
program.parse();
|
|
2030
|
+
//# sourceMappingURL=index.js.map
|