agentlili 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 +533 -0
- package/dist/chunk-AAYS2L5P.js +946 -0
- package/dist/chunk-AAYS2L5P.js.map +1 -0
- package/dist/cli.js +803 -0
- package/dist/cli.js.map +1 -0
- package/dist/mcp-server.js +1468 -0
- package/dist/mcp-server.js.map +1 -0
- package/package.json +111 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,803 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
WalletManager
|
|
4
|
+
} from "./chunk-AAYS2L5P.js";
|
|
5
|
+
|
|
6
|
+
// src/cli/index.ts
|
|
7
|
+
import "dotenv/config";
|
|
8
|
+
import readline2 from "readline";
|
|
9
|
+
import { generateText, stepCountIs } from "ai";
|
|
10
|
+
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
11
|
+
import { createOpenAI } from "@ai-sdk/openai";
|
|
12
|
+
import { createGroq } from "@ai-sdk/groq";
|
|
13
|
+
|
|
14
|
+
// src/lib/kora/config.ts
|
|
15
|
+
function loadKoraConfig() {
|
|
16
|
+
const endpoint = process.env.KORA_ENDPOINT;
|
|
17
|
+
if (!endpoint) return null;
|
|
18
|
+
const feeModeStr = process.env.KORA_FEE_MODE ?? "sponsored";
|
|
19
|
+
const feeToken = process.env.KORA_FEE_TOKEN;
|
|
20
|
+
let feeMode;
|
|
21
|
+
if (feeModeStr === "token") {
|
|
22
|
+
if (!feeToken) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
"KORA_FEE_TOKEN is required when KORA_FEE_MODE=token"
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
feeMode = { type: "token", mint: feeToken };
|
|
28
|
+
} else {
|
|
29
|
+
feeMode = { type: "sponsored" };
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
endpoint,
|
|
33
|
+
apiKey: process.env.KORA_API_KEY || void 0,
|
|
34
|
+
hmacSecret: process.env.KORA_HMAC_SECRET || void 0,
|
|
35
|
+
feeMode,
|
|
36
|
+
feeToken
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// src/lib/config.ts
|
|
41
|
+
function loadConfig() {
|
|
42
|
+
const llmProvider = process.env.LLM_PROVIDER ?? "anthropic";
|
|
43
|
+
const llmApiKey = llmProvider === "anthropic" ? process.env.ANTHROPIC_API_KEY : llmProvider === "groq" ? process.env.GROQ_API_KEY : process.env.OPENAI_API_KEY;
|
|
44
|
+
const envVarName = llmProvider === "anthropic" ? "ANTHROPIC_API_KEY" : llmProvider === "groq" ? "GROQ_API_KEY" : "OPENAI_API_KEY";
|
|
45
|
+
if (!llmApiKey) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Missing API key: set ${envVarName} in .env`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
const walletEncryptionKey = process.env.WALLET_ENCRYPTION_KEY;
|
|
51
|
+
if (!walletEncryptionKey) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Missing WALLET_ENCRYPTION_KEY in .env \u2014 generate one with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
const kora = loadKoraConfig();
|
|
57
|
+
const defaultModel = llmProvider === "anthropic" ? "claude-sonnet-4-20250514" : llmProvider === "groq" ? "llama-3.3-70b-versatile" : "gpt-4o";
|
|
58
|
+
return {
|
|
59
|
+
llmProvider,
|
|
60
|
+
llmModel: process.env.LLM_MODEL ?? defaultModel,
|
|
61
|
+
llmApiKey,
|
|
62
|
+
walletEncryptionKey,
|
|
63
|
+
defaultWalletId: process.env.DEFAULT_WALLET_ID,
|
|
64
|
+
kora
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/lib/agent.ts
|
|
69
|
+
import { SolanaAgentKit } from "solana-agent-kit";
|
|
70
|
+
import TokenPlugin from "@solana-agent-kit/plugin-token";
|
|
71
|
+
import DefiPlugin from "@solana-agent-kit/plugin-defi";
|
|
72
|
+
import { createVercelAITools } from "solana-agent-kit";
|
|
73
|
+
var CORE_ACTION_NAMES = /* @__PURE__ */ new Set([
|
|
74
|
+
"GET_TOKEN_DATA",
|
|
75
|
+
"GET_TOKEN_DATA_OR_INFO_BY_TICKER_OR_SYMBOL",
|
|
76
|
+
"FETCH_PRICE",
|
|
77
|
+
"STAKE_WITH_JUPITER",
|
|
78
|
+
"TRADE",
|
|
79
|
+
"CREATE_LIMIT_ORDER",
|
|
80
|
+
"CANCEL_LIMIT_ORDERS",
|
|
81
|
+
"GET_OPEN_LIMIT_ORDERS",
|
|
82
|
+
"GET_LIMIT_ORDER_HISTORY",
|
|
83
|
+
"BALANCE_ACTION",
|
|
84
|
+
"TOKEN_BALANCE_ACTION",
|
|
85
|
+
"GET_TPS",
|
|
86
|
+
"CLOSE_EMPTY_TOKEN_ACCOUNTS",
|
|
87
|
+
"REQUEST_FUNDS",
|
|
88
|
+
"TRANSFER",
|
|
89
|
+
"PYTH_FETCH_PRICE",
|
|
90
|
+
"RUGCHECK",
|
|
91
|
+
"WALLET_ADDRESS",
|
|
92
|
+
"LEND_ASSET",
|
|
93
|
+
"LULO_LEND",
|
|
94
|
+
"LULO_WITHDRAW"
|
|
95
|
+
]);
|
|
96
|
+
function createAgent(wallet, config, rpcUrl) {
|
|
97
|
+
const agentConfig = {};
|
|
98
|
+
if (config.llmProvider === "openai") {
|
|
99
|
+
agentConfig.OPENAI_API_KEY = config.llmApiKey;
|
|
100
|
+
}
|
|
101
|
+
if (config.kora) {
|
|
102
|
+
agentConfig.KORA_ENDPOINT = config.kora.endpoint;
|
|
103
|
+
if (config.kora.apiKey) agentConfig.KORA_API_KEY = config.kora.apiKey;
|
|
104
|
+
if (config.kora.feeToken) agentConfig.KORA_FEE_TOKEN = config.kora.feeToken;
|
|
105
|
+
}
|
|
106
|
+
const agent = new SolanaAgentKit(wallet, rpcUrl ?? "http://localhost:8899", agentConfig).use(TokenPlugin).use(DefiPlugin);
|
|
107
|
+
const toolMode = process.env.CHAT_TOOL_MODE ?? "core";
|
|
108
|
+
const actions = toolMode === "all" ? agent.actions : agent.actions.filter((a) => CORE_ACTION_NAMES.has(a.name));
|
|
109
|
+
const tools = createVercelAITools(agent, actions);
|
|
110
|
+
return { agent, tools };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/lib/system-prompt.ts
|
|
114
|
+
var SYSTEM_PROMPT = `You are an autonomous AI agent wallet operating on Solana devnet. You can create wallets, manage SOL and SPL tokens, execute DeFi operations, and interact with protocols \u2014 all programmatically.
|
|
115
|
+
|
|
116
|
+
## Core Capabilities
|
|
117
|
+
- **Wallet Management:** Create wallets, check balances, request devnet airdrops, transfer SOL and SPL tokens
|
|
118
|
+
- **Trading:** Swap tokens via Jupiter aggregator, create/cancel limit orders
|
|
119
|
+
- **DeFi:** Provide liquidity on Orca/Raydium/Meteora, lend/borrow on Lulo, stake SOL
|
|
120
|
+
- **Market Data:** Fetch token prices, check TPS, get token data
|
|
121
|
+
- **Advanced:** Perpetual trading on Drift/Adrena, cross-chain bridges, LST operations
|
|
122
|
+
|
|
123
|
+
## Decision-Making Protocol
|
|
124
|
+
|
|
125
|
+
When handling any request, follow this structured approach:
|
|
126
|
+
|
|
127
|
+
1. **Assess Preconditions:** Check wallet balance, verify sufficient funds for gas (~0.005 SOL per transaction), confirm token availability
|
|
128
|
+
2. **Plan the Operation:** Break complex requests into ordered steps. Identify dependencies between steps (e.g., must airdrop before swap)
|
|
129
|
+
3. **Execute Step-by-Step:** Run each tool call, verify the result before proceeding to the next step
|
|
130
|
+
4. **Handle Failures:** If a step fails, diagnose the cause, attempt recovery (e.g., retry with adjusted parameters, request airdrop if insufficient funds), and report clearly
|
|
131
|
+
5. **Report Results:** Summarize what was accomplished, provide transaction signatures with Explorer links, show final balances
|
|
132
|
+
|
|
133
|
+
### Example Reasoning Chain
|
|
134
|
+
\`\`\`
|
|
135
|
+
User: "Swap 0.5 SOL for USDC"
|
|
136
|
+
\u2192 Step 1: Check balance \u2192 0.2 SOL (insufficient)
|
|
137
|
+
\u2192 Step 2: Request airdrop \u2192 +1 SOL, balance now 1.2 SOL
|
|
138
|
+
\u2192 Step 3: Execute swap 0.5 SOL \u2192 USDC via Jupiter (300 bps slippage)
|
|
139
|
+
\u2192 Step 4: Verify \u2014 show new SOL balance + USDC balance + tx signature
|
|
140
|
+
\`\`\`
|
|
141
|
+
|
|
142
|
+
## Autonomous Behavior Guidelines
|
|
143
|
+
- **Proactive balance checks:** Before any transaction, verify you have sufficient SOL (including ~0.005 SOL for fees)
|
|
144
|
+
- **Gas awareness:** Each transaction costs approximately 0.005 SOL in fees. Account for this when planning operations
|
|
145
|
+
- **Error recovery:** If a transaction fails due to insufficient funds, automatically request an airdrop and retry. If a swap fails due to slippage, suggest a higher slippage tolerance
|
|
146
|
+
- **Multi-step chaining:** You can chain up to 10 tool calls per request. Use this to execute complex strategies autonomously (e.g., check balance \u2192 airdrop \u2192 swap \u2192 verify \u2192 report)
|
|
147
|
+
- **Devnet limitations:** Some DeFi protocols have limited devnet support. Be upfront about this and suggest alternatives when an operation isn't available
|
|
148
|
+
|
|
149
|
+
## Guidelines
|
|
150
|
+
1. You are on **Solana devnet** \u2014 transactions use test SOL, not real funds.
|
|
151
|
+
2. When performing transactions, always report the transaction signature so the user can verify on Solana Explorer.
|
|
152
|
+
3. Format Solana Explorer links as: https://explorer.solana.com/tx/{signature}?cluster=devnet
|
|
153
|
+
4. If the user's wallet has no SOL, suggest requesting an airdrop first.
|
|
154
|
+
5. For swaps, use reasonable slippage (300 bps = 3%) unless the user specifies otherwise.
|
|
155
|
+
6. When asked about balances, show both SOL and any SPL token balances.
|
|
156
|
+
7. Be concise but informative. Show key details (amounts, addresses, signatures) clearly.
|
|
157
|
+
8. If a transaction fails, explain the likely cause and suggest a fix.
|
|
158
|
+
9. Some DeFi protocols have limited devnet support \u2014 be upfront about this if an operation isn't available on devnet.
|
|
159
|
+
10. Never expose private keys or secret key material in responses.
|
|
160
|
+
|
|
161
|
+
## Transaction Signing
|
|
162
|
+
All transactions are signed automatically using the agent wallet's keypair. No manual approval is needed \u2014 you are an autonomous agent. Sign and submit transactions directly when the user requests an action.
|
|
163
|
+
|
|
164
|
+
## Available Tools
|
|
165
|
+
You have access to tools from the Solana Agent Kit including token operations, DeFi interactions, and market data. Use the appropriate tool for each request. You can chain multiple tool calls for complex operations (e.g., check balance \u2192 airdrop if low \u2192 swap \u2192 check new balance).`;
|
|
166
|
+
|
|
167
|
+
// src/cli/setup.ts
|
|
168
|
+
import fs from "fs";
|
|
169
|
+
import path from "path";
|
|
170
|
+
import crypto from "crypto";
|
|
171
|
+
import readline from "readline";
|
|
172
|
+
import { execSync } from "child_process";
|
|
173
|
+
var c = {
|
|
174
|
+
reset: "\x1B[0m",
|
|
175
|
+
bold: "\x1B[1m",
|
|
176
|
+
dim: "\x1B[2m",
|
|
177
|
+
green: "\x1B[32m",
|
|
178
|
+
cyan: "\x1B[36m",
|
|
179
|
+
yellow: "\x1B[33m",
|
|
180
|
+
red: "\x1B[31m",
|
|
181
|
+
gray: "\x1B[90m",
|
|
182
|
+
white: "\x1B[37m",
|
|
183
|
+
bgCyan: "\x1B[46m",
|
|
184
|
+
bgGreen: "\x1B[42m",
|
|
185
|
+
underline: "\x1B[4m"
|
|
186
|
+
};
|
|
187
|
+
function heading(step, total, text) {
|
|
188
|
+
console.log(
|
|
189
|
+
`
|
|
190
|
+
${c.bgCyan}${c.bold} ${step}/${total} ${c.reset} ${c.bold}${text}${c.reset}
|
|
191
|
+
`
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
function ok(text) {
|
|
195
|
+
console.log(` ${c.green}\u2713${c.reset} ${text}`);
|
|
196
|
+
}
|
|
197
|
+
function warn(text) {
|
|
198
|
+
console.log(` ${c.yellow}\u26A0${c.reset} ${text}`);
|
|
199
|
+
}
|
|
200
|
+
function fail(text) {
|
|
201
|
+
console.log(` ${c.red}\u2717${c.reset} ${text}`);
|
|
202
|
+
}
|
|
203
|
+
function info(text) {
|
|
204
|
+
console.log(` ${c.dim}${text}${c.reset}`);
|
|
205
|
+
}
|
|
206
|
+
function hint(text) {
|
|
207
|
+
console.log(` ${c.gray}${text}${c.reset}`);
|
|
208
|
+
}
|
|
209
|
+
function createPrompt() {
|
|
210
|
+
const rl = readline.createInterface({
|
|
211
|
+
input: process.stdin,
|
|
212
|
+
output: process.stdout
|
|
213
|
+
});
|
|
214
|
+
return {
|
|
215
|
+
ask(question, defaultValue) {
|
|
216
|
+
const suffix = defaultValue ? ` ${c.dim}(${defaultValue})${c.reset}` : "";
|
|
217
|
+
return new Promise((resolve) => {
|
|
218
|
+
rl.question(` ${c.cyan}?${c.reset} ${question}${suffix}: `, (ans) => {
|
|
219
|
+
resolve(ans.trim() || defaultValue || "");
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
},
|
|
223
|
+
choose(question, options, defaultIdx = 0) {
|
|
224
|
+
return new Promise((resolve) => {
|
|
225
|
+
console.log(` ${c.cyan}?${c.reset} ${question}`);
|
|
226
|
+
options.forEach((opt, i) => {
|
|
227
|
+
const marker = i === defaultIdx ? `${c.green}>${c.reset}` : " ";
|
|
228
|
+
console.log(` ${marker} ${c.bold}${i + 1}${c.reset}. ${opt}`);
|
|
229
|
+
});
|
|
230
|
+
rl.question(` ${c.dim} Enter choice [${defaultIdx + 1}]: ${c.reset}`, (ans) => {
|
|
231
|
+
const n = parseInt(ans.trim(), 10);
|
|
232
|
+
resolve(n >= 1 && n <= options.length ? n - 1 : defaultIdx);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
},
|
|
236
|
+
confirm(question, defaultYes = true) {
|
|
237
|
+
const hint2 = defaultYes ? "Y/n" : "y/N";
|
|
238
|
+
return new Promise((resolve) => {
|
|
239
|
+
rl.question(` ${c.cyan}?${c.reset} ${question} ${c.dim}(${hint2})${c.reset}: `, (ans) => {
|
|
240
|
+
const a = ans.trim().toLowerCase();
|
|
241
|
+
resolve(a === "" ? defaultYes : a === "y" || a === "yes");
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
},
|
|
245
|
+
close() {
|
|
246
|
+
rl.close();
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function envPath() {
|
|
251
|
+
return path.join(process.cwd(), ".env");
|
|
252
|
+
}
|
|
253
|
+
function readEnv() {
|
|
254
|
+
const p = envPath();
|
|
255
|
+
if (!fs.existsSync(p)) return {};
|
|
256
|
+
const lines = fs.readFileSync(p, "utf-8").split("\n");
|
|
257
|
+
const env = {};
|
|
258
|
+
for (const line of lines) {
|
|
259
|
+
const match = line.match(/^([A-Z_][A-Z0-9_]*)=["']?(.*)["']?\s*$/);
|
|
260
|
+
if (match) env[match[1]] = match[2];
|
|
261
|
+
}
|
|
262
|
+
return env;
|
|
263
|
+
}
|
|
264
|
+
function setEnvVar(key, value) {
|
|
265
|
+
const p = envPath();
|
|
266
|
+
let content = fs.existsSync(p) ? fs.readFileSync(p, "utf-8") : "";
|
|
267
|
+
const regex = new RegExp(`^${key}=.*$`, "m");
|
|
268
|
+
if (regex.test(content)) {
|
|
269
|
+
content = content.replace(regex, `${key}=${value}`);
|
|
270
|
+
} else {
|
|
271
|
+
content = content.trimEnd() + `
|
|
272
|
+
${key}=${value}
|
|
273
|
+
`;
|
|
274
|
+
}
|
|
275
|
+
fs.writeFileSync(p, content);
|
|
276
|
+
}
|
|
277
|
+
function commandExists(cmd) {
|
|
278
|
+
try {
|
|
279
|
+
execSync(`which ${cmd}`, { stdio: "ignore" });
|
|
280
|
+
return true;
|
|
281
|
+
} catch {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
function run(cmd, opts) {
|
|
286
|
+
try {
|
|
287
|
+
return execSync(cmd, {
|
|
288
|
+
cwd: opts?.cwd ?? process.cwd(),
|
|
289
|
+
stdio: opts?.silent ? "pipe" : "inherit",
|
|
290
|
+
encoding: "utf-8"
|
|
291
|
+
});
|
|
292
|
+
} catch (e) {
|
|
293
|
+
throw new Error(
|
|
294
|
+
`Command failed: ${cmd}
|
|
295
|
+
${e.message}`
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
function runSilent(cmd) {
|
|
300
|
+
try {
|
|
301
|
+
const output = execSync(cmd, { encoding: "utf-8", stdio: "pipe" });
|
|
302
|
+
return { ok: true, output };
|
|
303
|
+
} catch (e) {
|
|
304
|
+
return { ok: false, output: e.message };
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
async function testDatabaseConnection(url) {
|
|
308
|
+
if (commandExists("pg_isready")) {
|
|
309
|
+
const u = new URL(url);
|
|
310
|
+
const host = u.hostname || "localhost";
|
|
311
|
+
const port = u.port || "5432";
|
|
312
|
+
const result = runSilent(`pg_isready -h ${host} -p ${port} -t 3`);
|
|
313
|
+
return result.ok;
|
|
314
|
+
}
|
|
315
|
+
try {
|
|
316
|
+
const { default: pg } = await import("pg");
|
|
317
|
+
const client = new pg.Client({ connectionString: url });
|
|
318
|
+
await client.connect();
|
|
319
|
+
await client.end();
|
|
320
|
+
return true;
|
|
321
|
+
} catch {
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function detectPackageManager() {
|
|
326
|
+
if (fs.existsSync(path.join(process.cwd(), "pnpm-lock.yaml"))) return "pnpm";
|
|
327
|
+
if (fs.existsSync(path.join(process.cwd(), "yarn.lock"))) return "yarn";
|
|
328
|
+
if (fs.existsSync(path.join(process.cwd(), "bun.lockb"))) return "bun";
|
|
329
|
+
return "npm";
|
|
330
|
+
}
|
|
331
|
+
function npxCmd() {
|
|
332
|
+
const pm = detectPackageManager();
|
|
333
|
+
switch (pm) {
|
|
334
|
+
case "pnpm":
|
|
335
|
+
return "pnpm dlx";
|
|
336
|
+
case "yarn":
|
|
337
|
+
return "yarn dlx";
|
|
338
|
+
case "bun":
|
|
339
|
+
return "bunx";
|
|
340
|
+
default:
|
|
341
|
+
return "npx";
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
async function runSetup() {
|
|
345
|
+
console.log(`
|
|
346
|
+
${c.bgCyan}${c.bold} agentlili init ${c.reset} ${c.bold}Interactive Setup Wizard${c.reset}`);
|
|
347
|
+
console.log(
|
|
348
|
+
`${c.dim} This will walk you through configuring your environment.${c.reset}`
|
|
349
|
+
);
|
|
350
|
+
const prompt = createPrompt();
|
|
351
|
+
const TOTAL_STEPS = 5;
|
|
352
|
+
let stepNum = 0;
|
|
353
|
+
heading(++stepNum, TOTAL_STEPS, "Environment File");
|
|
354
|
+
const envExists = fs.existsSync(envPath());
|
|
355
|
+
if (envExists) {
|
|
356
|
+
ok(`.env file found at ${envPath()}`);
|
|
357
|
+
} else {
|
|
358
|
+
warn("No .env file found.");
|
|
359
|
+
const create = await prompt.confirm("Create one now?");
|
|
360
|
+
if (create) {
|
|
361
|
+
fs.writeFileSync(envPath(), "# agentlili configuration\n\n");
|
|
362
|
+
ok("Created .env file");
|
|
363
|
+
} else {
|
|
364
|
+
fail("Cannot continue without .env \u2014 create one manually and re-run.");
|
|
365
|
+
prompt.close();
|
|
366
|
+
process.exit(1);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
heading(++stepNum, TOTAL_STEPS, "PostgreSQL Database");
|
|
370
|
+
const env = readEnv();
|
|
371
|
+
let dbUrl = process.env.DATABASE_URL || env.DATABASE_URL || "";
|
|
372
|
+
if (dbUrl) {
|
|
373
|
+
info(`Current DATABASE_URL: ${c.white}${dbUrl.replace(/\/\/.*@/, "//***@")}${c.reset}`);
|
|
374
|
+
const connOk = await testDatabaseConnection(dbUrl);
|
|
375
|
+
if (connOk) {
|
|
376
|
+
ok("Database is reachable");
|
|
377
|
+
} else {
|
|
378
|
+
warn("Database is not reachable with current URL");
|
|
379
|
+
const change = await prompt.confirm("Enter a different DATABASE_URL?");
|
|
380
|
+
if (change) dbUrl = "";
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (!dbUrl) {
|
|
384
|
+
console.log();
|
|
385
|
+
info("You need a PostgreSQL database. Options:");
|
|
386
|
+
console.log();
|
|
387
|
+
const choice = await prompt.choose("How would you like to set up PostgreSQL?", [
|
|
388
|
+
`${c.white}Local PostgreSQL${c.reset} \u2014 already installed on this machine`,
|
|
389
|
+
`${c.white}Docker${c.reset} \u2014 spin up a container (requires Docker)`,
|
|
390
|
+
`${c.white}Remote / Cloud${c.reset} \u2014 Supabase, Neon, Railway, etc.`,
|
|
391
|
+
`${c.white}Skip${c.reset} \u2014 I'll configure it manually later`
|
|
392
|
+
]);
|
|
393
|
+
switch (choice) {
|
|
394
|
+
case 0: {
|
|
395
|
+
const pgReady = commandExists("psql");
|
|
396
|
+
if (!pgReady) {
|
|
397
|
+
console.log();
|
|
398
|
+
warn("psql not found. Install PostgreSQL first:");
|
|
399
|
+
hint(" macOS: brew install postgresql@16 && brew services start postgresql@16");
|
|
400
|
+
hint(" Ubuntu: sudo apt install postgresql && sudo systemctl start postgresql");
|
|
401
|
+
hint(" Windows: https://www.postgresql.org/download/windows/");
|
|
402
|
+
console.log();
|
|
403
|
+
const skipDb = await prompt.confirm("Continue anyway with a connection URL?", false);
|
|
404
|
+
if (!skipDb) {
|
|
405
|
+
prompt.close();
|
|
406
|
+
process.exit(1);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
const dbName = await prompt.ask("Database name", "agentlili_defi_wallets");
|
|
410
|
+
const dbUser = await prompt.ask("Database user", "postgres");
|
|
411
|
+
const dbHost = await prompt.ask("Host", "localhost");
|
|
412
|
+
const dbPort = await prompt.ask("Port", "5432");
|
|
413
|
+
const dbPass = await prompt.ask("Password (empty for none)", "");
|
|
414
|
+
const passSegment = dbPass ? `:${dbPass}` : "";
|
|
415
|
+
dbUrl = `postgresql://${dbUser}${passSegment}@${dbHost}:${dbPort}/${dbName}?schema=public`;
|
|
416
|
+
if (commandExists("psql")) {
|
|
417
|
+
info(`Checking if database '${dbName}' exists...`);
|
|
418
|
+
const check = runSilent(
|
|
419
|
+
`psql -h ${dbHost} -p ${dbPort} -U ${dbUser} -lqt 2>/dev/null | grep -w ${dbName}`
|
|
420
|
+
);
|
|
421
|
+
if (!check.ok || !check.output.includes(dbName)) {
|
|
422
|
+
const createDb = await prompt.confirm(`Database '${dbName}' not found. Create it?`);
|
|
423
|
+
if (createDb) {
|
|
424
|
+
const result = runSilent(
|
|
425
|
+
`createdb -h ${dbHost} -p ${dbPort} -U ${dbUser} ${dbName} 2>&1`
|
|
426
|
+
);
|
|
427
|
+
if (result.ok) {
|
|
428
|
+
ok(`Created database '${dbName}'`);
|
|
429
|
+
} else {
|
|
430
|
+
warn(`Could not create database: ${result.output.trim()}`);
|
|
431
|
+
hint("You may need to create it manually:");
|
|
432
|
+
hint(` createdb -U ${dbUser} ${dbName}`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
} else {
|
|
436
|
+
ok(`Database '${dbName}' already exists`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
break;
|
|
440
|
+
}
|
|
441
|
+
case 1: {
|
|
442
|
+
if (!commandExists("docker")) {
|
|
443
|
+
fail("Docker not found. Install Docker first: https://docs.docker.com/get-docker/");
|
|
444
|
+
prompt.close();
|
|
445
|
+
process.exit(1);
|
|
446
|
+
}
|
|
447
|
+
const dbName = await prompt.ask("Database name", "agentlili_defi_wallets");
|
|
448
|
+
const dbPass = await prompt.ask("PostgreSQL password", "postgres");
|
|
449
|
+
const dbPort = await prompt.ask("Host port", "5432");
|
|
450
|
+
const containerName = "agentlili-postgres";
|
|
451
|
+
info("Starting PostgreSQL container...");
|
|
452
|
+
console.log();
|
|
453
|
+
const existing = runSilent(`docker ps -a --filter name=${containerName} --format "{{.Status}}"`);
|
|
454
|
+
if (existing.ok && existing.output.trim()) {
|
|
455
|
+
if (existing.output.includes("Up")) {
|
|
456
|
+
ok(`Container '${containerName}' is already running`);
|
|
457
|
+
} else {
|
|
458
|
+
info("Starting existing container...");
|
|
459
|
+
run(`docker start ${containerName}`, { silent: true });
|
|
460
|
+
ok(`Container '${containerName}' started`);
|
|
461
|
+
}
|
|
462
|
+
} else {
|
|
463
|
+
try {
|
|
464
|
+
run(
|
|
465
|
+
`docker run -d --name ${containerName} -e POSTGRES_DB=${dbName} -e POSTGRES_PASSWORD=${dbPass} -p ${dbPort}:5432 postgres:16-alpine`,
|
|
466
|
+
{ silent: true }
|
|
467
|
+
);
|
|
468
|
+
ok(`Container '${containerName}' created and running on port ${dbPort}`);
|
|
469
|
+
info("Waiting for PostgreSQL to be ready...");
|
|
470
|
+
await new Promise((r) => setTimeout(r, 3e3));
|
|
471
|
+
} catch (e) {
|
|
472
|
+
fail(`Failed to start container: ${e.message}`);
|
|
473
|
+
prompt.close();
|
|
474
|
+
process.exit(1);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
dbUrl = `postgresql://postgres:${dbPass}@localhost:${dbPort}/${dbName}?schema=public`;
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
case 2: {
|
|
481
|
+
console.log();
|
|
482
|
+
info("Popular free-tier options:");
|
|
483
|
+
hint(" Supabase: https://supabase.com (Project Settings > Database > Connection String)");
|
|
484
|
+
hint(" Neon: https://neon.tech (Dashboard > Connection Details)");
|
|
485
|
+
hint(" Railway: https://railway.app (Add PostgreSQL > Variables > DATABASE_URL)");
|
|
486
|
+
console.log();
|
|
487
|
+
dbUrl = await prompt.ask("Paste your DATABASE_URL");
|
|
488
|
+
if (!dbUrl) {
|
|
489
|
+
fail("No URL provided.");
|
|
490
|
+
prompt.close();
|
|
491
|
+
process.exit(1);
|
|
492
|
+
}
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
case 3: {
|
|
496
|
+
warn("Skipping database setup. Set DATABASE_URL in .env before running agentlili.");
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
if (dbUrl) {
|
|
502
|
+
setEnvVar("DATABASE_URL", `"${dbUrl}"`);
|
|
503
|
+
process.env.DATABASE_URL = dbUrl;
|
|
504
|
+
ok(`DATABASE_URL saved to .env`);
|
|
505
|
+
const connOk = await testDatabaseConnection(dbUrl);
|
|
506
|
+
if (connOk) {
|
|
507
|
+
ok("Database connection verified");
|
|
508
|
+
} else {
|
|
509
|
+
warn("Could not connect to database \u2014 check the URL and that PostgreSQL is running");
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
heading(++stepNum, TOTAL_STEPS, "Database Migrations");
|
|
513
|
+
if (!dbUrl) {
|
|
514
|
+
warn("No DATABASE_URL \u2014 skipping migrations. Run 'agentlili init' again after configuring.");
|
|
515
|
+
} else {
|
|
516
|
+
const connOk = await testDatabaseConnection(dbUrl);
|
|
517
|
+
if (!connOk) {
|
|
518
|
+
warn("Database not reachable \u2014 skipping migrations.");
|
|
519
|
+
hint("Once the database is running, apply migrations with:");
|
|
520
|
+
hint(` ${npxCmd()} prisma db push`);
|
|
521
|
+
} else {
|
|
522
|
+
const runMigrations = await prompt.confirm("Push schema to database now? (prisma db push)");
|
|
523
|
+
if (runMigrations) {
|
|
524
|
+
info("Applying Prisma schema...");
|
|
525
|
+
console.log();
|
|
526
|
+
try {
|
|
527
|
+
run(`${npxCmd()} prisma db push --skip-generate`, { cwd: process.cwd() });
|
|
528
|
+
console.log();
|
|
529
|
+
ok("Database schema applied successfully");
|
|
530
|
+
} catch (e) {
|
|
531
|
+
console.log();
|
|
532
|
+
fail(`Migration failed: ${e.message}`);
|
|
533
|
+
hint("You can retry manually with:");
|
|
534
|
+
hint(` ${npxCmd()} prisma db push`);
|
|
535
|
+
}
|
|
536
|
+
} else {
|
|
537
|
+
hint("Run migrations later with:");
|
|
538
|
+
hint(` ${npxCmd()} prisma db push`);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
heading(++stepNum, TOTAL_STEPS, "Wallet Encryption Key");
|
|
543
|
+
const existingKey = process.env.WALLET_ENCRYPTION_KEY || env.WALLET_ENCRYPTION_KEY;
|
|
544
|
+
if (existingKey && existingKey.length === 64) {
|
|
545
|
+
ok("WALLET_ENCRYPTION_KEY already set (64-char hex)");
|
|
546
|
+
const regen = await prompt.confirm("Generate a new one? (will invalidate existing wallets)", false);
|
|
547
|
+
if (regen) {
|
|
548
|
+
const key = crypto.randomBytes(32).toString("hex");
|
|
549
|
+
setEnvVar("WALLET_ENCRYPTION_KEY", key);
|
|
550
|
+
ok("New WALLET_ENCRYPTION_KEY generated and saved");
|
|
551
|
+
warn("Any previously encrypted wallets will be unreadable with this new key");
|
|
552
|
+
}
|
|
553
|
+
} else {
|
|
554
|
+
const key = crypto.randomBytes(32).toString("hex");
|
|
555
|
+
setEnvVar("WALLET_ENCRYPTION_KEY", key);
|
|
556
|
+
ok("Generated WALLET_ENCRYPTION_KEY and saved to .env");
|
|
557
|
+
hint("This key encrypts your wallet private keys \u2014 back it up securely!");
|
|
558
|
+
}
|
|
559
|
+
heading(++stepNum, TOTAL_STEPS, "LLM Configuration");
|
|
560
|
+
const hasAnthropic = !!(process.env.ANTHROPIC_API_KEY || env.ANTHROPIC_API_KEY);
|
|
561
|
+
const hasOpenAI = !!(process.env.OPENAI_API_KEY || env.OPENAI_API_KEY);
|
|
562
|
+
const hasGroq = !!(process.env.GROQ_API_KEY || env.GROQ_API_KEY);
|
|
563
|
+
if (hasAnthropic || hasOpenAI || hasGroq) {
|
|
564
|
+
const provider = hasAnthropic ? "Anthropic" : hasOpenAI ? "OpenAI" : "Groq";
|
|
565
|
+
ok(`${provider} API key detected`);
|
|
566
|
+
const addAnother = await prompt.confirm("Configure a different provider?", false);
|
|
567
|
+
if (!addAnother) {
|
|
568
|
+
} else {
|
|
569
|
+
await configureLLM(prompt, env);
|
|
570
|
+
}
|
|
571
|
+
} else {
|
|
572
|
+
warn("No LLM API key found. The agent needs one to function.");
|
|
573
|
+
await configureLLM(prompt, env);
|
|
574
|
+
}
|
|
575
|
+
console.log(`
|
|
576
|
+
${c.bgGreen}${c.bold} Setup Complete ${c.reset}
|
|
577
|
+
`);
|
|
578
|
+
console.log(` ${c.green}You're ready to go!${c.reset} Run the agent with:
|
|
579
|
+
`);
|
|
580
|
+
console.log(` ${c.bold}agentlili${c.reset} ${c.dim}# interactive CLI agent${c.reset}`);
|
|
581
|
+
console.log(` ${c.bold}agentlili-mcp${c.reset} ${c.dim}# MCP server for Claude / editors${c.reset}`);
|
|
582
|
+
console.log(` ${c.bold}pnpm dev${c.reset} ${c.dim}# web dashboard${c.reset}`);
|
|
583
|
+
console.log();
|
|
584
|
+
prompt.close();
|
|
585
|
+
}
|
|
586
|
+
async function configureLLM(prompt, env) {
|
|
587
|
+
const choice = await prompt.choose("Which LLM provider?", [
|
|
588
|
+
`${c.white}Anthropic${c.reset} \u2014 Claude (recommended)`,
|
|
589
|
+
`${c.white}OpenAI${c.reset} \u2014 GPT-4o`,
|
|
590
|
+
`${c.white}Groq${c.reset} \u2014 Llama (fast, free tier)`,
|
|
591
|
+
`${c.white}Skip${c.reset} \u2014 I'll add it to .env later`
|
|
592
|
+
]);
|
|
593
|
+
const providers = [
|
|
594
|
+
{ key: "anthropic", envName: "ANTHROPIC_API_KEY", provider: "anthropic", defaultModel: "claude-sonnet-4-20250514" },
|
|
595
|
+
{ key: "openai", envName: "OPENAI_API_KEY", provider: "openai", defaultModel: "gpt-4o" },
|
|
596
|
+
{ key: "groq", envName: "GROQ_API_KEY", provider: "groq", defaultModel: "llama-3.3-70b-versatile" }
|
|
597
|
+
];
|
|
598
|
+
if (choice === 3) {
|
|
599
|
+
hint("Add your API key to .env when ready:");
|
|
600
|
+
hint(" ANTHROPIC_API_KEY=sk-ant-...");
|
|
601
|
+
hint(" # or OPENAI_API_KEY=sk-...");
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
const p = providers[choice];
|
|
605
|
+
console.log();
|
|
606
|
+
const apiKey = await prompt.ask(`${p.envName}`);
|
|
607
|
+
if (!apiKey) {
|
|
608
|
+
warn("No key entered \u2014 you can add it to .env later.");
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
setEnvVar(p.envName, apiKey);
|
|
612
|
+
setEnvVar("LLM_PROVIDER", p.provider);
|
|
613
|
+
setEnvVar("LLM_MODEL", p.defaultModel);
|
|
614
|
+
ok(`${p.provider} configured with model ${p.defaultModel}`);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// src/cli/index.ts
|
|
618
|
+
var c2 = {
|
|
619
|
+
reset: "\x1B[0m",
|
|
620
|
+
bold: "\x1B[1m",
|
|
621
|
+
dim: "\x1B[2m",
|
|
622
|
+
green: "\x1B[32m",
|
|
623
|
+
cyan: "\x1B[36m",
|
|
624
|
+
yellow: "\x1B[33m",
|
|
625
|
+
red: "\x1B[31m",
|
|
626
|
+
gray: "\x1B[90m",
|
|
627
|
+
white: "\x1B[37m",
|
|
628
|
+
bgGreen: "\x1B[42m"
|
|
629
|
+
};
|
|
630
|
+
var VERSION = "0.1.0";
|
|
631
|
+
function banner(text) {
|
|
632
|
+
console.log(`
|
|
633
|
+
${c2.bgGreen}${c2.bold} ${text} ${c2.reset}
|
|
634
|
+
`);
|
|
635
|
+
}
|
|
636
|
+
function info2(label, value) {
|
|
637
|
+
console.log(` ${c2.gray}${label}${c2.reset} ${c2.white}${value}${c2.reset}`);
|
|
638
|
+
}
|
|
639
|
+
function spinnerFrames() {
|
|
640
|
+
return ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
641
|
+
}
|
|
642
|
+
function startSpinner(text) {
|
|
643
|
+
const frames = spinnerFrames();
|
|
644
|
+
let i = 0;
|
|
645
|
+
const id = setInterval(() => {
|
|
646
|
+
process.stdout.write(
|
|
647
|
+
`\r ${c2.green}${frames[i % frames.length]}${c2.reset} ${c2.dim}${text}${c2.reset}`
|
|
648
|
+
);
|
|
649
|
+
i++;
|
|
650
|
+
}, 80);
|
|
651
|
+
return () => {
|
|
652
|
+
clearInterval(id);
|
|
653
|
+
process.stdout.write("\r" + " ".repeat(text.length + 10) + "\r");
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
function printHelp() {
|
|
657
|
+
console.log(`
|
|
658
|
+
${c2.bold}AgentLili${c2.reset} \u2014 Agentic DeFi Wallet for Solana ${c2.dim}v${VERSION}${c2.reset}
|
|
659
|
+
|
|
660
|
+
${c2.bold}Usage:${c2.reset}
|
|
661
|
+
agentlili Start interactive agent chat
|
|
662
|
+
agentlili init Set up database, env vars, and wallet
|
|
663
|
+
agentlili --help Show this help message
|
|
664
|
+
agentlili --version Show version
|
|
665
|
+
|
|
666
|
+
${c2.bold}Environment:${c2.reset}
|
|
667
|
+
Run ${c2.cyan}agentlili init${c2.reset} to configure everything interactively, or set
|
|
668
|
+
these in your .env file:
|
|
669
|
+
|
|
670
|
+
${c2.white}DATABASE_URL${c2.reset} PostgreSQL connection string
|
|
671
|
+
${c2.white}WALLET_ENCRYPTION_KEY${c2.reset} 64-char hex key (generated during init)
|
|
672
|
+
${c2.white}ANTHROPIC_API_KEY${c2.reset} Anthropic API key (or OPENAI_API_KEY / GROQ_API_KEY)
|
|
673
|
+
${c2.white}LLM_PROVIDER${c2.reset} anthropic | openai | groq
|
|
674
|
+
${c2.white}SOLANA_RPC_URL${c2.reset} RPC endpoint (default: http://localhost:8899)
|
|
675
|
+
`);
|
|
676
|
+
}
|
|
677
|
+
async function startChat() {
|
|
678
|
+
banner("AgentLili \u2014 Solana DeFi Agent");
|
|
679
|
+
const config = loadConfig();
|
|
680
|
+
const rpcUrl = process.env.SOLANA_RPC_URL ?? "http://localhost:8899";
|
|
681
|
+
const walletManager = new WalletManager(
|
|
682
|
+
config.walletEncryptionKey,
|
|
683
|
+
rpcUrl
|
|
684
|
+
);
|
|
685
|
+
let wallets = await walletManager.listWallets();
|
|
686
|
+
if (wallets.length === 0) {
|
|
687
|
+
console.log(` ${c2.yellow}No wallet found. Creating one...${c2.reset}`);
|
|
688
|
+
await walletManager.createWallet("AgentLili Wallet");
|
|
689
|
+
wallets = await walletManager.listWallets();
|
|
690
|
+
}
|
|
691
|
+
const walletId = config.defaultWalletId || wallets[0].id;
|
|
692
|
+
const wallet = await walletManager.getKeypairWallet(walletId);
|
|
693
|
+
const publicKey = wallet.publicKey.toBase58();
|
|
694
|
+
info2("Wallet:", publicKey);
|
|
695
|
+
info2("Network:", "Solana Devnet");
|
|
696
|
+
info2("LLM:", `${config.llmProvider} / ${config.llmModel}`);
|
|
697
|
+
console.log(
|
|
698
|
+
`
|
|
699
|
+
${c2.dim}Type your message and press Enter. Type "exit" to quit.${c2.reset}
|
|
700
|
+
`
|
|
701
|
+
);
|
|
702
|
+
const { tools } = createAgent(wallet, config, rpcUrl);
|
|
703
|
+
const model = config.llmProvider === "anthropic" ? createAnthropic({ apiKey: config.llmApiKey })(config.llmModel) : config.llmProvider === "groq" ? createGroq({ apiKey: config.llmApiKey })(config.llmModel) : createOpenAI({ apiKey: config.llmApiKey })(config.llmModel);
|
|
704
|
+
const systemPrompt = `${SYSTEM_PROMPT}
|
|
705
|
+
|
|
706
|
+
## Current Wallet
|
|
707
|
+
Public Key: ${publicKey}
|
|
708
|
+
Network: Solana Localnet
|
|
709
|
+
RPC: ${rpcUrl}`;
|
|
710
|
+
const messages = [];
|
|
711
|
+
const rl = readline2.createInterface({
|
|
712
|
+
input: process.stdin,
|
|
713
|
+
output: process.stdout
|
|
714
|
+
});
|
|
715
|
+
function prompt() {
|
|
716
|
+
rl.question(`${c2.green}${c2.bold}You: ${c2.reset}`, async (input) => {
|
|
717
|
+
const trimmed = input.trim();
|
|
718
|
+
if (!trimmed) {
|
|
719
|
+
prompt();
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
if (trimmed.toLowerCase() === "exit") {
|
|
723
|
+
console.log(`
|
|
724
|
+
${c2.dim}Goodbye!${c2.reset}
|
|
725
|
+
`);
|
|
726
|
+
rl.close();
|
|
727
|
+
process.exit(0);
|
|
728
|
+
}
|
|
729
|
+
messages.push({ role: "user", content: trimmed });
|
|
730
|
+
const stopSpinner = startSpinner("Thinking...");
|
|
731
|
+
try {
|
|
732
|
+
const result = await generateText({
|
|
733
|
+
model,
|
|
734
|
+
system: systemPrompt,
|
|
735
|
+
messages,
|
|
736
|
+
tools,
|
|
737
|
+
stopWhen: stepCountIs(10)
|
|
738
|
+
});
|
|
739
|
+
stopSpinner();
|
|
740
|
+
for (const step of result.steps) {
|
|
741
|
+
for (const tc of step.toolCalls) {
|
|
742
|
+
const argsStr = JSON.stringify(tc.input).slice(0, 100);
|
|
743
|
+
console.log(
|
|
744
|
+
` ${c2.cyan}[tool]${c2.reset} ${c2.bold}${tc.toolName}${c2.reset}${c2.gray}(${argsStr})${c2.reset}`
|
|
745
|
+
);
|
|
746
|
+
}
|
|
747
|
+
for (const tr of step.toolResults) {
|
|
748
|
+
const raw = tr.result;
|
|
749
|
+
const resultStr = typeof raw === "string" ? raw : JSON.stringify(raw);
|
|
750
|
+
const display = resultStr.length > 200 ? resultStr.slice(0, 200) + "..." : resultStr;
|
|
751
|
+
console.log(
|
|
752
|
+
` ${c2.green}[result]${c2.reset} ${c2.dim}${display}${c2.reset}`
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
if (result.text) {
|
|
757
|
+
console.log(
|
|
758
|
+
`
|
|
759
|
+
${c2.green}${c2.bold}Agent:${c2.reset} ${result.text}
|
|
760
|
+
`
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
messages.push({ role: "assistant", content: result.text });
|
|
764
|
+
} catch (error) {
|
|
765
|
+
stopSpinner();
|
|
766
|
+
console.error(
|
|
767
|
+
`
|
|
768
|
+
${c2.red}Error: ${error instanceof Error ? error.message : "Unknown error"}${c2.reset}
|
|
769
|
+
`
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
prompt();
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
prompt();
|
|
776
|
+
}
|
|
777
|
+
async function main() {
|
|
778
|
+
const args = process.argv.slice(2);
|
|
779
|
+
const cmd = args[0]?.toLowerCase();
|
|
780
|
+
if (cmd === "--help" || cmd === "-h") {
|
|
781
|
+
printHelp();
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
if (cmd === "--version" || cmd === "-v") {
|
|
785
|
+
console.log(`agentlili v${VERSION}`);
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
if (cmd === "init" || cmd === "setup") {
|
|
789
|
+
await runSetup();
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
if (cmd && !["chat", "start"].includes(cmd)) {
|
|
793
|
+
console.error(`${c2.red}Unknown command: ${cmd}${c2.reset}`);
|
|
794
|
+
console.error(`Run ${c2.cyan}agentlili --help${c2.reset} for usage.`);
|
|
795
|
+
process.exit(1);
|
|
796
|
+
}
|
|
797
|
+
await startChat();
|
|
798
|
+
}
|
|
799
|
+
main().catch((err) => {
|
|
800
|
+
console.error(`${c2.red}Fatal: ${err.message}${c2.reset}`);
|
|
801
|
+
process.exit(1);
|
|
802
|
+
});
|
|
803
|
+
//# sourceMappingURL=cli.js.map
|