@glide-pool/cli 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/glidepool +311 -0
  2. package/package.json +2 -3
package/bin/glidepool ADDED
@@ -0,0 +1,311 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
5
+ import { homedir } from 'os';
6
+ import { join } from 'path';
7
+ import { createRequire } from 'module';
8
+
9
+ const require = createRequire(import.meta.url);
10
+ const pkg = require('../package.json');
11
+
12
+ // ─────────────────────────────────────────
13
+ // Config
14
+ // ─────────────────────────────────────────
15
+ const CONFIG_DIR = join(homedir(), '.glidepool');
16
+ const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
17
+
18
+ function loadConfig() {
19
+ if (!existsSync(CONFIG_FILE)) return {};
20
+ try { return JSON.parse(readFileSync(CONFIG_FILE, 'utf8')); } catch { return {}; }
21
+ }
22
+
23
+ function saveConfig(data) {
24
+ mkdirSync(CONFIG_DIR, { recursive: true });
25
+ writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2));
26
+ }
27
+
28
+ function getApiUrl(opts) {
29
+ const url = opts.api || process.env.GLIDEPOOL_API_URL || loadConfig().apiUrl;
30
+ if (!url) {
31
+ console.error('Error: API URL required. Set via --api <url> or GLIDEPOOL_API_URL env or run: glidepool config set-api <url>');
32
+ process.exit(1);
33
+ }
34
+ return url.replace(/\/$/, '');
35
+ }
36
+
37
+ // ─────────────────────────────────────────
38
+ // HTTP helper
39
+ // ─────────────────────────────────────────
40
+ async function apiFetch(apiUrl, path, options = {}) {
41
+ const url = `${apiUrl}${path}`;
42
+ const res = await fetch(url, {
43
+ ...options,
44
+ headers: { 'Content-Type': 'application/json', ...options.headers },
45
+ });
46
+ const data = await res.json().catch(() => ({}));
47
+ if (!res.ok) {
48
+ if (res.status === 402) {
49
+ console.error('\n[402] Payment Required');
50
+ console.error(` Send ${data.amount || '0.05'} ${data.currency || 'USDC'} on Base to:`);
51
+ console.error(` ${data.recipient}`);
52
+ console.error(` Token: ${data.token}`);
53
+ console.error(`\n Then retry with: --payment-proof <base64(JSON{txHash,from,amount})>`);
54
+ process.exit(2);
55
+ }
56
+ console.error(`API error (${res.status}): ${data.message || data.error || 'Unknown error'}`);
57
+ process.exit(1);
58
+ }
59
+ return data;
60
+ }
61
+
62
+ function printJson(data) {
63
+ console.log(JSON.stringify(data, null, 2));
64
+ }
65
+
66
+ function printTable(rows, columns) {
67
+ const widths = columns.map(c => Math.max(c.label.length, ...rows.map(r => String(r[c.key] ?? '').length)));
68
+ const header = columns.map((c, i) => c.label.padEnd(widths[i])).join(' ');
69
+ const divider = widths.map(w => '-'.repeat(w)).join(' ');
70
+ console.log(header);
71
+ console.log(divider);
72
+ rows.forEach(row => {
73
+ console.log(columns.map((c, i) => String(row[c.key] ?? '').padEnd(widths[i])).join(' '));
74
+ });
75
+ }
76
+
77
+ // ─────────────────────────────────────────
78
+ // CLI
79
+ // ─────────────────────────────────────────
80
+ const program = new Command();
81
+
82
+ program
83
+ .name('glidepool')
84
+ .description('CLI for GlidePool — autonomous DLMM agent platform on Base Mainnet')
85
+ .version(pkg.version)
86
+ .option('--api <url>', 'GlidePool API URL (or set GLIDEPOOL_API_URL env var)')
87
+ .option('--json', 'Output raw JSON');
88
+
89
+ // ── config ──────────────────────────────
90
+ const config = program.command('config').description('Manage CLI configuration');
91
+
92
+ config
93
+ .command('set-api <url>')
94
+ .description('Save the GlidePool API URL to ~/.glidepool/config.json')
95
+ .action((url) => {
96
+ const cfg = loadConfig();
97
+ cfg.apiUrl = url.replace(/\/$/, '');
98
+ saveConfig(cfg);
99
+ console.log(`Saved API URL: ${cfg.apiUrl}`);
100
+ });
101
+
102
+ config
103
+ .command('show')
104
+ .description('Show current configuration')
105
+ .action(() => {
106
+ printJson({ configFile: CONFIG_FILE, ...loadConfig() });
107
+ });
108
+
109
+ // ── pools ───────────────────────────────
110
+ const pools = program.command('pools').description('List and inspect Maverick V2 pools');
111
+
112
+ pools
113
+ .command('list')
114
+ .description('List all supported pools with live TVL, price, and fee rate')
115
+ .action(async (_, cmd) => {
116
+ const opts = program.opts();
117
+ const api = getApiUrl(opts);
118
+ const data = await apiFetch(api, '/api/pools');
119
+ if (opts.json) { printJson(data); return; }
120
+ printTable(data, [
121
+ { key: 'tokenASymbol', label: 'TOKEN A' },
122
+ { key: 'tokenBSymbol', label: 'TOKEN B' },
123
+ { key: 'tvlUsd', label: 'TVL (USD)' },
124
+ { key: 'currentPrice', label: 'PRICE' },
125
+ { key: 'feeRate', label: 'FEE' },
126
+ { key: 'poolAddress', label: 'ADDRESS' },
127
+ ]);
128
+ });
129
+
130
+ pools
131
+ .command('get <address>')
132
+ .description('Get details for a specific pool')
133
+ .action(async (address) => {
134
+ const opts = program.opts();
135
+ const api = getApiUrl(opts);
136
+ const data = await apiFetch(api, `/api/pools/${address}`);
137
+ printJson(data);
138
+ });
139
+
140
+ // ── agent ────────────────────────────────
141
+ const agent = program.command('agent').description('Create and manage autonomous agents');
142
+
143
+ agent
144
+ .command('create')
145
+ .description('Deploy a new autonomous DLMM agent')
146
+ .requiredOption('--wallet <address>', 'Your wallet address (0x...)')
147
+ .requiredOption('--pool <address>', 'Maverick V2 pool address to monitor')
148
+ .option('--strategy <strategy>', 'Strategy: conservative | balanced | aggressive', 'balanced')
149
+ .option('--budget <usdc>', 'Max USDC budget for liquidity operations', '100')
150
+ .option('--interval <seconds>', 'Analysis interval in seconds (min: 30)', '60')
151
+ .action(async (opts) => {
152
+ const api = getApiUrl(program.opts());
153
+ const data = await apiFetch(api, '/api/agents', {
154
+ method: 'POST',
155
+ body: JSON.stringify({
156
+ userAddress: opts.wallet,
157
+ poolAddress: opts.pool,
158
+ strategy: opts.strategy,
159
+ budgetUsdc: Number(opts.budget),
160
+ analysisIntervalSec: Number(opts.interval),
161
+ }),
162
+ });
163
+ if (program.opts().json) { printJson(data); return; }
164
+ console.log(`\nAgent deployed`);
165
+ console.log(` ID: ${data.id}`);
166
+ console.log(` Pool: ${data.poolAddress}`);
167
+ console.log(` Strategy: ${data.strategy}`);
168
+ console.log(` Budget: ${data.budgetUsdc} USDC`);
169
+ console.log(` Status: ${data.status}`);
170
+ console.log(`\nMonitor with: glidepool agent actions ${data.id}`);
171
+ });
172
+
173
+ agent
174
+ .command('list')
175
+ .description('List all agents for a wallet address')
176
+ .requiredOption('--wallet <address>', 'Wallet address (0x...)')
177
+ .action(async (opts) => {
178
+ const api = getApiUrl(program.opts());
179
+ const data = await apiFetch(api, `/api/agents?userAddress=${encodeURIComponent(opts.wallet)}`);
180
+ if (program.opts().json) { printJson(data); return; }
181
+ if (!data.length) { console.log('No agents found for this wallet.'); return; }
182
+ printTable(data, [
183
+ { key: 'id', label: 'AGENT ID' },
184
+ { key: 'poolAddress', label: 'POOL' },
185
+ { key: 'strategy', label: 'STRATEGY' },
186
+ { key: 'budgetUsdc', label: 'BUDGET USDC' },
187
+ { key: 'status', label: 'STATUS' },
188
+ { key: 'lastAnalysisAt', label: 'LAST ANALYSIS' },
189
+ ]);
190
+ });
191
+
192
+ agent
193
+ .command('get <agentId>')
194
+ .description('Get details for a specific agent')
195
+ .action(async (agentId) => {
196
+ const api = getApiUrl(program.opts());
197
+ const data = await apiFetch(api, `/api/agents/${agentId}`);
198
+ printJson(data);
199
+ });
200
+
201
+ agent
202
+ .command('pause <agentId>')
203
+ .description('Pause a running agent')
204
+ .action(async (agentId) => {
205
+ const api = getApiUrl(program.opts());
206
+ await apiFetch(api, `/api/agents/${agentId}/status`, {
207
+ method: 'PUT',
208
+ body: JSON.stringify({ status: 'paused' }),
209
+ });
210
+ console.log(`Agent ${agentId} paused.`);
211
+ });
212
+
213
+ agent
214
+ .command('resume <agentId>')
215
+ .description('Resume a paused agent')
216
+ .action(async (agentId) => {
217
+ const api = getApiUrl(program.opts());
218
+ await apiFetch(api, `/api/agents/${agentId}/status`, {
219
+ method: 'PUT',
220
+ body: JSON.stringify({ status: 'active' }),
221
+ });
222
+ console.log(`Agent ${agentId} resumed.`);
223
+ });
224
+
225
+ agent
226
+ .command('stop <agentId>')
227
+ .description('Stop an agent permanently')
228
+ .action(async (agentId) => {
229
+ const api = getApiUrl(program.opts());
230
+ await apiFetch(api, `/api/agents/${agentId}/status`, {
231
+ method: 'PUT',
232
+ body: JSON.stringify({ status: 'stopped' }),
233
+ });
234
+ console.log(`Agent ${agentId} stopped.`);
235
+ });
236
+
237
+ agent
238
+ .command('actions <agentId>')
239
+ .description('Show LLM decisions (actions) for an agent')
240
+ .option('--limit <n>', 'Number of actions to show', '10')
241
+ .action(async (agentId, opts) => {
242
+ const api = getApiUrl(program.opts());
243
+ const data = await apiFetch(api, `/api/agents/${agentId}/actions?limit=${opts.limit}`);
244
+ if (program.opts().json) { printJson(data); return; }
245
+ if (!data.length) { console.log('No actions yet. Agent will analyze on its next cycle.'); return; }
246
+ data.forEach((a) => {
247
+ const ts = new Date(a.createdAt).toLocaleTimeString();
248
+ const rec = a.llmRecommendation;
249
+ console.log(`\n[${ts}] ${a.actionType.toUpperCase()} — ${a.status}`);
250
+ if (rec?.riskLevel) console.log(` Risk: ${rec.riskLevel}`);
251
+ if (a.llmReasoning) console.log(` Reason: ${a.llmReasoning.slice(0, 200)}...`);
252
+ if (rec?.recommendation?.suggestedBinRange) {
253
+ const r = rec.recommendation.suggestedBinRange;
254
+ console.log(` Bins: lower=${r.lowerTick} upper=${r.upperTick}`);
255
+ }
256
+ if (a.txHash) console.log(` TX: ${a.txHash}`);
257
+ });
258
+ });
259
+
260
+ // ── positions ────────────────────────────
261
+ program
262
+ .command('positions <walletAddress>')
263
+ .description('List Maverick V2 LP positions for a wallet on Base Mainnet')
264
+ .action(async (walletAddress) => {
265
+ const api = getApiUrl(program.opts());
266
+ const data = await apiFetch(api, `/api/positions/${walletAddress}`);
267
+ if (program.opts().json) { printJson(data); return; }
268
+ if (!data.length) { console.log('No Maverick V2 positions found for this wallet.'); return; }
269
+ printTable(data, [
270
+ { key: 'nftId', label: 'NFT ID' },
271
+ { key: 'tokenASymbol', label: 'TOKEN A' },
272
+ { key: 'tokenBSymbol', label: 'TOKEN B' },
273
+ { key: 'valueUsd', label: 'VALUE USD' },
274
+ { key: 'amountA', label: 'AMOUNT A' },
275
+ { key: 'amountB', label: 'AMOUNT B' },
276
+ { key: 'binCount', label: 'BINS' },
277
+ ]);
278
+ });
279
+
280
+ // ── advisor ──────────────────────────────
281
+ program
282
+ .command('advisor')
283
+ .description('Get a Claude Opus 4 AI recommendation for a pool position')
284
+ .requiredOption('--pool <address>', 'Pool address to analyze')
285
+ .requiredOption('--goal <text>', 'Your liquidity goal (e.g. "maximize fee income with low IL")')
286
+ .option('--nft <id>', 'NFT position ID if analyzing an existing position')
287
+ .option('--payment-proof <base64>', 'x402 payment proof header (if server requires payment)')
288
+ .action(async (opts) => {
289
+ const api = getApiUrl(program.opts());
290
+ const qs = new URLSearchParams({ poolAddress: opts.pool, userGoal: opts.goal });
291
+ if (opts.nft) qs.set('nftId', opts.nft);
292
+ const headers = {};
293
+ if (opts.paymentProof) headers['x-payment-proof'] = opts.paymentProof;
294
+ const data = await apiFetch(api, `/api/advisor?${qs}`, { headers });
295
+ if (program.opts().json) { printJson(data); return; }
296
+ console.log(`\n── AI Advisor ──────────────────────────`);
297
+ console.log(` Action: ${data.recommendation?.action?.toUpperCase()}`);
298
+ console.log(` Risk: ${data.riskLevel}`);
299
+ console.log(` Summary: ${data.summary}`);
300
+ if (data.recommendation?.suggestedBinRange) {
301
+ const r = data.recommendation.suggestedBinRange;
302
+ console.log(` Bins: lower=${r.lowerTick} upper=${r.upperTick}`);
303
+ }
304
+ if (data.recommendation?.suggestedWithdrawPercent) {
305
+ console.log(` Withdraw: ${data.recommendation.suggestedWithdrawPercent}%`);
306
+ }
307
+ console.log(`\n Reasoning:`);
308
+ console.log(` ${data.recommendation?.reasoning}`);
309
+ });
310
+
311
+ program.parse();
package/package.json CHANGED
@@ -1,14 +1,13 @@
1
1
  {
2
2
  "name": "@glide-pool/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "CLI for managing GlidePool autonomous DLMM agents on Base Mainnet",
5
5
  "type": "module",
6
6
  "bin": {
7
- "glidepool": "./bin/glidepool.js"
7
+ "glidepool": "./bin/glidepool"
8
8
  },
9
9
  "files": [
10
10
  "bin",
11
- "src",
12
11
  "README.md"
13
12
  ],
14
13
  "keywords": [