pyre-agent-kit 1.0.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/dist/action.d.ts +3 -0
- package/dist/action.js +98 -0
- package/dist/agent.d.ts +4 -0
- package/dist/agent.js +276 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.js +475 -0
- package/dist/defaults.d.ts +10 -0
- package/dist/defaults.js +109 -0
- package/dist/error.d.ts +6 -0
- package/dist/error.js +66 -0
- package/dist/executor.d.ts +24 -0
- package/dist/executor.js +409 -0
- package/dist/faction.d.ts +10 -0
- package/dist/faction.js +110 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +220 -0
- package/dist/stronghold.d.ts +7 -0
- package/dist/stronghold.js +88 -0
- package/dist/tx.d.ts +2 -0
- package/dist/tx.js +40 -0
- package/dist/types.d.ts +118 -0
- package/dist/types.js +2 -0
- package/dist/util.d.ts +6 -0
- package/dist/util.js +19 -0
- package/package.json +30 -0
- package/readme.md +556 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* pyre-agent-kit CLI
|
|
5
|
+
*
|
|
6
|
+
* Interactive launcher for non-technical users.
|
|
7
|
+
* npx pyre-agent-kit
|
|
8
|
+
*
|
|
9
|
+
* Config is saved to ~/.pyre-agent.json so you only set up once.
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
const readline = __importStar(require("readline"));
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
49
|
+
const index_1 = require("./index");
|
|
50
|
+
// ─── Constants ────────────────────────────────────────────────────
|
|
51
|
+
const CONFIG_PATH = path.join(process.env.HOME ?? '.', '.pyre-agent.json');
|
|
52
|
+
const BANNER = `
|
|
53
|
+
██████╗ ██╗ ██╗██████╗ ███████╗
|
|
54
|
+
██╔══██╗╚██╗ ██╔╝██╔══██╗██╔════╝
|
|
55
|
+
██████╔╝ ╚████╔╝ ██████╔╝█████╗
|
|
56
|
+
██╔═══╝ ╚██╔╝ ██╔══██╗██╔══╝
|
|
57
|
+
██║ ██║ ██║ ██║███████╗
|
|
58
|
+
╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝
|
|
59
|
+
A G E N T K I T
|
|
60
|
+
`;
|
|
61
|
+
const RPC_DEFAULTS = {
|
|
62
|
+
devnet: 'https://api.devnet.solana.com',
|
|
63
|
+
mainnet: 'https://api.mainnet-beta.solana.com',
|
|
64
|
+
};
|
|
65
|
+
const PERSONALITIES = ['loyalist', 'mercenary', 'provocateur', 'scout', 'whale'];
|
|
66
|
+
const PERSONALITY_DISPLAY = {
|
|
67
|
+
loyalist: 'Loyalist — ride-or-die, holds positions long',
|
|
68
|
+
mercenary: 'Mercenary — plays every angle, frequent trades',
|
|
69
|
+
provocateur: 'Provocateur — chaos agent, heavy on comms and FUD',
|
|
70
|
+
scout: 'Scout — intel-focused, small positions',
|
|
71
|
+
whale: 'Whale — big positions, market mover',
|
|
72
|
+
};
|
|
73
|
+
function loadConfig() {
|
|
74
|
+
try {
|
|
75
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
76
|
+
return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch { /* ignore */ }
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
function saveConfig(config) {
|
|
83
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
84
|
+
}
|
|
85
|
+
// ─── Prompts ──────────────────────────────────────────────────────
|
|
86
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
87
|
+
function ask(question, fallback) {
|
|
88
|
+
const suffix = fallback ? ` (${fallback})` : '';
|
|
89
|
+
return new Promise(resolve => {
|
|
90
|
+
rl.question(` ${question}${suffix}: `, answer => {
|
|
91
|
+
resolve(answer.trim() || fallback || '');
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function choose(question, options, defaultIdx = 0) {
|
|
96
|
+
return new Promise(resolve => {
|
|
97
|
+
console.log(`\n ${question}`);
|
|
98
|
+
options.forEach((opt, i) => {
|
|
99
|
+
const marker = i === defaultIdx ? '>' : ' ';
|
|
100
|
+
console.log(` ${marker} ${i + 1}. ${opt}`);
|
|
101
|
+
});
|
|
102
|
+
rl.question(` Choice [${defaultIdx + 1}]: `, answer => {
|
|
103
|
+
const n = parseInt(answer.trim());
|
|
104
|
+
if (n >= 1 && n <= options.length)
|
|
105
|
+
resolve(n - 1);
|
|
106
|
+
else
|
|
107
|
+
resolve(defaultIdx);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
// ─── LLM Factory ─────────────────────────────────────────────────
|
|
112
|
+
function createLLM(config) {
|
|
113
|
+
if (config.llmProvider === 'none')
|
|
114
|
+
return undefined;
|
|
115
|
+
if (config.llmProvider === 'openai') {
|
|
116
|
+
const apiKey = config.llmApiKey;
|
|
117
|
+
const model = config.llmModel || 'gpt-4o';
|
|
118
|
+
return {
|
|
119
|
+
generate: async (prompt) => {
|
|
120
|
+
const res = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
121
|
+
method: 'POST',
|
|
122
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },
|
|
123
|
+
body: JSON.stringify({
|
|
124
|
+
model,
|
|
125
|
+
messages: [{ role: 'user', content: prompt }],
|
|
126
|
+
max_tokens: 120,
|
|
127
|
+
temperature: 0.9,
|
|
128
|
+
}),
|
|
129
|
+
});
|
|
130
|
+
if (!res.ok) {
|
|
131
|
+
console.error(` [LLM] OpenAI error: ${res.status}`);
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
const data = await res.json();
|
|
135
|
+
return data.choices?.[0]?.message?.content ?? null;
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (config.llmProvider === 'anthropic') {
|
|
140
|
+
const apiKey = config.llmApiKey;
|
|
141
|
+
const model = config.llmModel || 'claude-sonnet-4-20250514';
|
|
142
|
+
return {
|
|
143
|
+
generate: async (prompt) => {
|
|
144
|
+
const res = await fetch('https://api.anthropic.com/v1/messages', {
|
|
145
|
+
method: 'POST',
|
|
146
|
+
headers: {
|
|
147
|
+
'Content-Type': 'application/json',
|
|
148
|
+
'x-api-key': apiKey,
|
|
149
|
+
'anthropic-version': '2023-06-01',
|
|
150
|
+
},
|
|
151
|
+
body: JSON.stringify({
|
|
152
|
+
model,
|
|
153
|
+
max_tokens: 120,
|
|
154
|
+
messages: [{ role: 'user', content: prompt }],
|
|
155
|
+
}),
|
|
156
|
+
});
|
|
157
|
+
if (!res.ok) {
|
|
158
|
+
console.error(` [LLM] Anthropic error: ${res.status}`);
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
const data = await res.json();
|
|
162
|
+
const block = data.content?.[0];
|
|
163
|
+
return block?.type === 'text' ? block.text : null;
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
if (config.llmProvider === 'ollama') {
|
|
168
|
+
const url = config.llmUrl || 'http://localhost:11434';
|
|
169
|
+
const model = config.llmModel || 'llama3';
|
|
170
|
+
return {
|
|
171
|
+
generate: async (prompt) => {
|
|
172
|
+
try {
|
|
173
|
+
const res = await fetch(`${url}/api/generate`, {
|
|
174
|
+
method: 'POST',
|
|
175
|
+
headers: { 'Content-Type': 'application/json' },
|
|
176
|
+
body: JSON.stringify({ model, prompt, stream: false }),
|
|
177
|
+
});
|
|
178
|
+
if (!res.ok) {
|
|
179
|
+
console.error(` [LLM] Ollama error: ${res.status}`);
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
const data = await res.json();
|
|
183
|
+
return data.response ?? null;
|
|
184
|
+
}
|
|
185
|
+
catch (e) {
|
|
186
|
+
console.error(` [LLM] Ollama unreachable: ${e.message}`);
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
// ─── Setup Wizard ─────────────────────────────────────────────────
|
|
195
|
+
async function runSetup() {
|
|
196
|
+
console.log('\n First-time setup. Config will be saved to ~/.pyre-agent.json\n');
|
|
197
|
+
// Network
|
|
198
|
+
const netIdx = await choose('Network:', ['Devnet (testing)', 'Mainnet (real SOL)'], 0);
|
|
199
|
+
const network = netIdx === 0 ? 'devnet' : 'mainnet';
|
|
200
|
+
// RPC
|
|
201
|
+
const rpcDefault = RPC_DEFAULTS[network];
|
|
202
|
+
const rpcUrl = await ask('RPC URL', rpcDefault);
|
|
203
|
+
// Keypair
|
|
204
|
+
console.log('\n Wallet setup:');
|
|
205
|
+
const walletIdx = await choose('Keypair:', [
|
|
206
|
+
'Generate new wallet',
|
|
207
|
+
'Import from secret key (JSON array)',
|
|
208
|
+
'Import from file path',
|
|
209
|
+
], 0);
|
|
210
|
+
let keypair;
|
|
211
|
+
if (walletIdx === 0) {
|
|
212
|
+
keypair = web3_js_1.Keypair.generate();
|
|
213
|
+
console.log(`\n New wallet generated: ${keypair.publicKey.toBase58()}`);
|
|
214
|
+
console.log(` Fund it with SOL before starting the agent.`);
|
|
215
|
+
if (network === 'devnet') {
|
|
216
|
+
console.log(` Devnet faucet: https://faucet.solana.com`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
else if (walletIdx === 1) {
|
|
220
|
+
const raw = await ask('Paste secret key JSON array');
|
|
221
|
+
keypair = web3_js_1.Keypair.fromSecretKey(Uint8Array.from(JSON.parse(raw)));
|
|
222
|
+
console.log(` Imported: ${keypair.publicKey.toBase58()}`);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
const filePath = await ask('Path to keypair JSON file');
|
|
226
|
+
const resolved = path.resolve(filePath.replace(/^~/, process.env.HOME ?? '.'));
|
|
227
|
+
const raw = JSON.parse(fs.readFileSync(resolved, 'utf-8'));
|
|
228
|
+
keypair = web3_js_1.Keypair.fromSecretKey(Uint8Array.from(raw));
|
|
229
|
+
console.log(` Imported: ${keypair.publicKey.toBase58()}`);
|
|
230
|
+
}
|
|
231
|
+
// Personality
|
|
232
|
+
const persIdx = await choose('Personality:', [
|
|
233
|
+
...PERSONALITIES.map(p => PERSONALITY_DISPLAY[p]),
|
|
234
|
+
'Random (auto-assign)',
|
|
235
|
+
], 5);
|
|
236
|
+
const personality = persIdx < PERSONALITIES.length ? PERSONALITIES[persIdx] : PERSONALITIES[Math.floor(Math.random() * PERSONALITIES.length)];
|
|
237
|
+
// LLM
|
|
238
|
+
const llmIdx = await choose('LLM Provider:', [
|
|
239
|
+
'OpenAI (GPT-4o, etc.)',
|
|
240
|
+
'Anthropic (Claude)',
|
|
241
|
+
'Ollama (local)',
|
|
242
|
+
'None (random actions only)',
|
|
243
|
+
], 0);
|
|
244
|
+
const llmProviders = ['openai', 'anthropic', 'ollama', 'none'];
|
|
245
|
+
const llmProvider = llmProviders[llmIdx];
|
|
246
|
+
let llmApiKey;
|
|
247
|
+
let llmModel;
|
|
248
|
+
let llmUrl;
|
|
249
|
+
if (llmProvider === 'openai') {
|
|
250
|
+
llmApiKey = await ask('OpenAI API key');
|
|
251
|
+
llmModel = await ask('Model', 'gpt-4o');
|
|
252
|
+
}
|
|
253
|
+
else if (llmProvider === 'anthropic') {
|
|
254
|
+
llmApiKey = await ask('Anthropic API key');
|
|
255
|
+
llmModel = await ask('Model', 'claude-sonnet-4-20250514');
|
|
256
|
+
}
|
|
257
|
+
else if (llmProvider === 'ollama') {
|
|
258
|
+
llmUrl = await ask('Ollama URL', 'http://localhost:11434');
|
|
259
|
+
llmModel = await ask('Model (e.g. llama3, gemma3:4b, mistral)', 'llama3');
|
|
260
|
+
}
|
|
261
|
+
// Tick interval
|
|
262
|
+
const intervalStr = await ask('Seconds between actions', '30');
|
|
263
|
+
const tickIntervalMs = Math.max(5, parseInt(intervalStr) || 30) * 1000;
|
|
264
|
+
// Vault funding
|
|
265
|
+
const fundStr = await ask('Stronghold vault funding (SOL)', '35');
|
|
266
|
+
const strongholdFundSol = Math.max(0.1, parseFloat(fundStr) || 35);
|
|
267
|
+
const config = {
|
|
268
|
+
network,
|
|
269
|
+
rpcUrl,
|
|
270
|
+
secretKey: Array.from(keypair.secretKey),
|
|
271
|
+
personality,
|
|
272
|
+
llmProvider,
|
|
273
|
+
llmModel,
|
|
274
|
+
llmApiKey,
|
|
275
|
+
llmUrl,
|
|
276
|
+
strongholdFundSol,
|
|
277
|
+
tickIntervalMs,
|
|
278
|
+
};
|
|
279
|
+
saveConfig(config);
|
|
280
|
+
console.log(`\n Config saved to ${CONFIG_PATH}`);
|
|
281
|
+
return config;
|
|
282
|
+
}
|
|
283
|
+
// ─── Agent Loop ───────────────────────────────────────────────────
|
|
284
|
+
function ts() {
|
|
285
|
+
return new Date().toISOString().substr(11, 8);
|
|
286
|
+
}
|
|
287
|
+
async function runAgent(config) {
|
|
288
|
+
const connection = new web3_js_1.Connection(config.rpcUrl, 'confirmed');
|
|
289
|
+
const keypair = web3_js_1.Keypair.fromSecretKey(Uint8Array.from(config.secretKey));
|
|
290
|
+
const llm = createLLM(config);
|
|
291
|
+
console.log(`\n Network: ${config.network}`);
|
|
292
|
+
console.log(` Wallet: ${keypair.publicKey.toBase58()}`);
|
|
293
|
+
console.log(` Personality: ${config.personality}`);
|
|
294
|
+
console.log(` LLM: ${config.llmProvider}${config.llmModel ? ` (${config.llmModel})` : ''}`);
|
|
295
|
+
console.log(` Tick: every ${config.tickIntervalMs / 1000}s`);
|
|
296
|
+
// Check balance
|
|
297
|
+
const balance = await connection.getBalance(keypair.publicKey);
|
|
298
|
+
const solBalance = balance / web3_js_1.LAMPORTS_PER_SOL;
|
|
299
|
+
console.log(` Balance: ${solBalance.toFixed(4)} SOL`);
|
|
300
|
+
if (solBalance < 0.01) {
|
|
301
|
+
console.error(`\n Wallet has insufficient SOL. Fund it and try again.`);
|
|
302
|
+
console.error(` Address: ${keypair.publicKey.toBase58()}`);
|
|
303
|
+
if (config.network === 'devnet') {
|
|
304
|
+
console.error(` Devnet faucet: https://faucet.solana.com`);
|
|
305
|
+
}
|
|
306
|
+
process.exit(1);
|
|
307
|
+
}
|
|
308
|
+
// Load saved state if it exists
|
|
309
|
+
const statePath = path.join(process.env.HOME ?? '.', `.pyre-agent-state-${keypair.publicKey.toBase58().slice(0, 8)}.json`);
|
|
310
|
+
let state;
|
|
311
|
+
try {
|
|
312
|
+
if (fs.existsSync(statePath)) {
|
|
313
|
+
state = JSON.parse(fs.readFileSync(statePath, 'utf-8'));
|
|
314
|
+
console.log(` State: restored from ${path.basename(statePath)}`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
catch { /* fresh start */ }
|
|
318
|
+
console.log(`\n Starting agent...\n`);
|
|
319
|
+
const agent = await (0, index_1.createPyreAgent)({
|
|
320
|
+
connection,
|
|
321
|
+
keypair,
|
|
322
|
+
network: config.network,
|
|
323
|
+
llm,
|
|
324
|
+
personality: config.personality,
|
|
325
|
+
strongholdFundSol: config.strongholdFundSol,
|
|
326
|
+
solRange: config.solRange,
|
|
327
|
+
state,
|
|
328
|
+
logger: (msg) => console.log(` [${ts()}] ${msg}`),
|
|
329
|
+
});
|
|
330
|
+
console.log(` Agent online: ${agent.publicKey.slice(0, 8)}... (${agent.personality})\n`);
|
|
331
|
+
// Graceful shutdown
|
|
332
|
+
let running = true;
|
|
333
|
+
const shutdown = () => {
|
|
334
|
+
if (!running)
|
|
335
|
+
return;
|
|
336
|
+
running = false;
|
|
337
|
+
console.log(`\n Shutting down...`);
|
|
338
|
+
const saved = agent.serialize();
|
|
339
|
+
fs.writeFileSync(statePath, JSON.stringify(saved, null, 2));
|
|
340
|
+
console.log(` State saved to ${path.basename(statePath)}`);
|
|
341
|
+
console.log(` ${saved.actionCount} total actions performed.`);
|
|
342
|
+
process.exit(0);
|
|
343
|
+
};
|
|
344
|
+
process.on('SIGINT', shutdown);
|
|
345
|
+
process.on('SIGTERM', shutdown);
|
|
346
|
+
// Auto-save every 10 ticks
|
|
347
|
+
let tickCount = 0;
|
|
348
|
+
while (running) {
|
|
349
|
+
try {
|
|
350
|
+
const result = await agent.tick();
|
|
351
|
+
tickCount++;
|
|
352
|
+
const status = result.success ? 'OK' : `FAIL: ${result.error}`;
|
|
353
|
+
const msg = result.message ? ` "${result.message}"` : '';
|
|
354
|
+
const brain = result.usedLLM ? 'LLM' : 'RNG';
|
|
355
|
+
console.log(` [${ts()}] #${tickCount} [${brain}] ${result.action.toUpperCase()} ${result.faction ?? ''}${msg} — ${status}`);
|
|
356
|
+
// Auto-save state every 10 ticks
|
|
357
|
+
if (tickCount % 10 === 0) {
|
|
358
|
+
const saved = agent.serialize();
|
|
359
|
+
fs.writeFileSync(statePath, JSON.stringify(saved, null, 2));
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
catch (e) {
|
|
363
|
+
console.error(` [${ts()}] Tick error: ${e.message}`);
|
|
364
|
+
}
|
|
365
|
+
// Wait for next tick
|
|
366
|
+
await new Promise(resolve => setTimeout(resolve, config.tickIntervalMs));
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
// ─── Main ─────────────────────────────────────────────────────────
|
|
370
|
+
async function main() {
|
|
371
|
+
console.log(BANNER);
|
|
372
|
+
const args = process.argv.slice(2);
|
|
373
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
374
|
+
console.log(' Usage: npx pyre-agent-kit [options]');
|
|
375
|
+
console.log('');
|
|
376
|
+
console.log(' Options:');
|
|
377
|
+
console.log(' --setup Re-run full setup wizard');
|
|
378
|
+
console.log(' --model Change LLM provider/model only');
|
|
379
|
+
console.log(' --personality Change personality only');
|
|
380
|
+
console.log(' --reset Delete saved config and start fresh');
|
|
381
|
+
console.log(' --status Show current config and wallet balance');
|
|
382
|
+
console.log(' --help Show this help message');
|
|
383
|
+
console.log('');
|
|
384
|
+
console.log(` Config: ${CONFIG_PATH}`);
|
|
385
|
+
console.log('');
|
|
386
|
+
process.exit(0);
|
|
387
|
+
}
|
|
388
|
+
if (args.includes('--reset')) {
|
|
389
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
390
|
+
fs.unlinkSync(CONFIG_PATH);
|
|
391
|
+
console.log(` Config deleted: ${CONFIG_PATH}`);
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
console.log(' No config to delete.');
|
|
395
|
+
}
|
|
396
|
+
process.exit(0);
|
|
397
|
+
}
|
|
398
|
+
let config = loadConfig();
|
|
399
|
+
if (args.includes('--model') && config) {
|
|
400
|
+
console.log(` Current: ${config.llmProvider}${config.llmModel ? ` (${config.llmModel})` : ''}\n`);
|
|
401
|
+
const llmIdx = await choose('LLM Provider:', [
|
|
402
|
+
'OpenAI (GPT-4o, etc.)',
|
|
403
|
+
'Anthropic (Claude)',
|
|
404
|
+
'Ollama (local)',
|
|
405
|
+
'None (random actions only)',
|
|
406
|
+
], ['openai', 'anthropic', 'ollama', 'none'].indexOf(config.llmProvider));
|
|
407
|
+
const llmProviders = ['openai', 'anthropic', 'ollama', 'none'];
|
|
408
|
+
config.llmProvider = llmProviders[llmIdx];
|
|
409
|
+
config.llmApiKey = undefined;
|
|
410
|
+
config.llmModel = undefined;
|
|
411
|
+
config.llmUrl = undefined;
|
|
412
|
+
if (config.llmProvider === 'openai') {
|
|
413
|
+
config.llmApiKey = await ask('OpenAI API key');
|
|
414
|
+
config.llmModel = await ask('Model', 'gpt-4o');
|
|
415
|
+
}
|
|
416
|
+
else if (config.llmProvider === 'anthropic') {
|
|
417
|
+
config.llmApiKey = await ask('Anthropic API key');
|
|
418
|
+
config.llmModel = await ask('Model', 'claude-sonnet-4-20250514');
|
|
419
|
+
}
|
|
420
|
+
else if (config.llmProvider === 'ollama') {
|
|
421
|
+
config.llmUrl = await ask('Ollama URL', config.llmUrl || 'http://localhost:11434');
|
|
422
|
+
config.llmModel = await ask('Model (e.g. llama3, gemma3:4b, mistral)', 'llama3');
|
|
423
|
+
}
|
|
424
|
+
saveConfig(config);
|
|
425
|
+
console.log(`\n Updated: ${config.llmProvider}${config.llmModel ? ` (${config.llmModel})` : ''}`);
|
|
426
|
+
console.log(` Saved to ${CONFIG_PATH}\n`);
|
|
427
|
+
rl.close();
|
|
428
|
+
process.exit(0);
|
|
429
|
+
}
|
|
430
|
+
if (args.includes('--personality') && config) {
|
|
431
|
+
console.log(` Current: ${config.personality}\n`);
|
|
432
|
+
const persIdx = await choose('Personality:', [
|
|
433
|
+
...PERSONALITIES.map(p => PERSONALITY_DISPLAY[p]),
|
|
434
|
+
'Random (auto-assign)',
|
|
435
|
+
], PERSONALITIES.indexOf(config.personality));
|
|
436
|
+
config.personality = persIdx < PERSONALITIES.length
|
|
437
|
+
? PERSONALITIES[persIdx]
|
|
438
|
+
: PERSONALITIES[Math.floor(Math.random() * PERSONALITIES.length)];
|
|
439
|
+
saveConfig(config);
|
|
440
|
+
console.log(`\n Updated: ${config.personality}`);
|
|
441
|
+
console.log(` Saved to ${CONFIG_PATH}\n`);
|
|
442
|
+
rl.close();
|
|
443
|
+
process.exit(0);
|
|
444
|
+
}
|
|
445
|
+
if (args.includes('--setup') || !config) {
|
|
446
|
+
config = await runSetup();
|
|
447
|
+
}
|
|
448
|
+
if (args.includes('--status')) {
|
|
449
|
+
console.log(` Config: ${CONFIG_PATH}`);
|
|
450
|
+
console.log(` Network: ${config.network}`);
|
|
451
|
+
console.log(` RPC: ${config.rpcUrl}`);
|
|
452
|
+
const kp = web3_js_1.Keypair.fromSecretKey(Uint8Array.from(config.secretKey));
|
|
453
|
+
console.log(` Wallet: ${kp.publicKey.toBase58()}`);
|
|
454
|
+
console.log(` Personality: ${config.personality}`);
|
|
455
|
+
console.log(` LLM: ${config.llmProvider}${config.llmModel ? ` (${config.llmModel})` : ''}`);
|
|
456
|
+
console.log(` Tick: every ${config.tickIntervalMs / 1000}s`);
|
|
457
|
+
try {
|
|
458
|
+
const connection = new web3_js_1.Connection(config.rpcUrl, 'confirmed');
|
|
459
|
+
const balance = await connection.getBalance(kp.publicKey);
|
|
460
|
+
console.log(` Balance: ${(balance / web3_js_1.LAMPORTS_PER_SOL).toFixed(4)} SOL`);
|
|
461
|
+
}
|
|
462
|
+
catch {
|
|
463
|
+
console.log(` Balance: (could not reach RPC)`);
|
|
464
|
+
}
|
|
465
|
+
console.log('');
|
|
466
|
+
rl.close();
|
|
467
|
+
process.exit(0);
|
|
468
|
+
}
|
|
469
|
+
rl.close();
|
|
470
|
+
await runAgent(config);
|
|
471
|
+
}
|
|
472
|
+
main().catch(e => {
|
|
473
|
+
console.error(`\n Fatal: ${e.message}`);
|
|
474
|
+
process.exit(1);
|
|
475
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Personality } from './types';
|
|
2
|
+
export declare const PERSONALITY_WEIGHTS: Record<Personality, number[]>;
|
|
3
|
+
export declare const PERSONALITY_SOL: Record<Personality, [number, number]>;
|
|
4
|
+
export declare const personalityDesc: Record<Personality, string>;
|
|
5
|
+
export declare const VOICE_NUDGES: string[];
|
|
6
|
+
export declare const ACTION_MAP: Record<string, string>;
|
|
7
|
+
export declare const STRONGHOLD_FUND_SOL = 35;
|
|
8
|
+
export declare const STRONGHOLD_TOPUP_THRESHOLD_SOL = 5;
|
|
9
|
+
export declare const STRONGHOLD_TOPUP_RESERVE_SOL = 5;
|
|
10
|
+
export declare const assignPersonality: () => Personality;
|
package/dist/defaults.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assignPersonality = exports.STRONGHOLD_TOPUP_RESERVE_SOL = exports.STRONGHOLD_TOPUP_THRESHOLD_SOL = exports.STRONGHOLD_FUND_SOL = exports.ACTION_MAP = exports.VOICE_NUDGES = exports.personalityDesc = exports.PERSONALITY_SOL = exports.PERSONALITY_WEIGHTS = void 0;
|
|
4
|
+
// ─── Personality Weights ────────────────────────────────────────────
|
|
5
|
+
// [join, defect, rally, launch, message, stronghold, war_loan, repay_loan, siege, ascend, raze, tithe, infiltrate, fud]
|
|
6
|
+
exports.PERSONALITY_WEIGHTS = {
|
|
7
|
+
loyalist: [0.28, 0.06, 0.14, 0.02, 0.12, 0.06, 0.04, 0.04, 0.02, 0.05, 0.02, 0.10, 0.02, 0.03],
|
|
8
|
+
mercenary: [0.16, 0.18, 0.04, 0.02, 0.08, 0.04, 0.08, 0.04, 0.06, 0.03, 0.04, 0.03, 0.12, 0.08],
|
|
9
|
+
provocateur: [0.12, 0.08, 0.04, 0.06, 0.18, 0.05, 0.04, 0.03, 0.04, 0.03, 0.05, 0.04, 0.12, 0.12],
|
|
10
|
+
scout: [0.18, 0.10, 0.08, 0.02, 0.16, 0.04, 0.04, 0.03, 0.06, 0.04, 0.05, 0.04, 0.08, 0.08],
|
|
11
|
+
whale: [0.24, 0.14, 0.06, 0.02, 0.06, 0.06, 0.06, 0.04, 0.02, 0.04, 0.04, 0.04, 0.12, 0.06],
|
|
12
|
+
};
|
|
13
|
+
// SOL spend ranges per personality
|
|
14
|
+
exports.PERSONALITY_SOL = {
|
|
15
|
+
loyalist: [0.02, 0.1],
|
|
16
|
+
mercenary: [0.01, 0.08],
|
|
17
|
+
provocateur: [0.005, 0.05],
|
|
18
|
+
scout: [0.005, 0.03],
|
|
19
|
+
whale: [0.1, 0.5],
|
|
20
|
+
};
|
|
21
|
+
exports.personalityDesc = {
|
|
22
|
+
loyalist: 'You\'re ride or die for your factions. Talk trash about rival factions unprompted. Hype your crew loudly and call out anyone who defects — by address. Propose quests and challenges for your allies. You take it personally when someone dumps your faction.',
|
|
23
|
+
mercenary: 'You\'re a lone wolf who plays every angle. Trash talk factions you\'re about to leave. Drop alpha only when it benefits you. Challenge others to prove their conviction. You don\'t need allies — everyone else is just a trade.',
|
|
24
|
+
provocateur: 'You live for chaos and hot takes. Call out the biggest holder in any faction. Start beef between factions. Make bold predictions and dare people to bet against you. Your trash talk is creative and specific — reference actual agents, numbers, and moves.',
|
|
25
|
+
scout: 'You\'re the intel operative who sees everything. Drop suspicious observations about other agents\' moves. Question why someone just bought or sold. Share data that makes people nervous. You\'re helpful to allies but plant doubt in everyone else.',
|
|
26
|
+
whale: 'You move markets and everyone knows it. Flex your position size. Trash talk small holders. Challenge other whales publicly. When you speak, people listen — and you know it. Back your words with big moves.',
|
|
27
|
+
};
|
|
28
|
+
exports.VOICE_NUDGES = [
|
|
29
|
+
'JOIN a faction you\'ve been eyeing. Put your money where your mouth is.',
|
|
30
|
+
'DEFECT from a faction that\'s underperforming. Cut your losses.',
|
|
31
|
+
'INFILTRATE a rival faction. Sneak in before they notice.',
|
|
32
|
+
'FUD a faction you hold — spread doubt and buy the dip later.',
|
|
33
|
+
'Take a WAR_LOAN against your strongest position. Leverage up.',
|
|
34
|
+
'JOIN the top faction on the leaderboard. Ride the momentum.',
|
|
35
|
+
'DEFECT dramatically. Trash talk on the way out.',
|
|
36
|
+
'INFILTRATE the weakest faction. Easy target.',
|
|
37
|
+
'FUD whoever is in first place. Knock them down a peg.',
|
|
38
|
+
'RALLY a faction you believe in. Show support.',
|
|
39
|
+
'JOIN a small faction early. Get in before the crowd.',
|
|
40
|
+
'Double down. REINFORCE your best position.',
|
|
41
|
+
'Call out a specific agent by address. What are they up to?',
|
|
42
|
+
'Trash talk a rival faction. Be specific about why they\'re weak.',
|
|
43
|
+
'Flex on your position. You\'re winning and everyone should know.',
|
|
44
|
+
'Be suspicious. Something doesn\'t add up. Who\'s dumping?',
|
|
45
|
+
'Challenge another agent directly. Dare them to make a move.',
|
|
46
|
+
'Drop a hot take that will start an argument.',
|
|
47
|
+
'Hype your faction aggressively. Why is everyone else sleeping on it?',
|
|
48
|
+
'Sound like you know something others don\'t. Be cryptic.',
|
|
49
|
+
'React to a recent trade or move. Call it smart or stupid.',
|
|
50
|
+
'Ask a loaded question. You already know the answer.',
|
|
51
|
+
'Be disappointed in someone. They let the faction down.',
|
|
52
|
+
'Make a bold prediction. Put your reputation on it.',
|
|
53
|
+
'Sound paranoid. Someone is coordinating against you.',
|
|
54
|
+
'Be sarcastic about a faction that\'s underperforming.',
|
|
55
|
+
'Propose a quest or challenge — but make it competitive.',
|
|
56
|
+
'Reference a specific number — holder count, percentage, or trend.',
|
|
57
|
+
'Write a one-liner. Punchy. Sarcastic. No explanation needed.',
|
|
58
|
+
'Sound like you\'re warning an ally about something you saw.',
|
|
59
|
+
];
|
|
60
|
+
exports.ACTION_MAP = {
|
|
61
|
+
'JOIN': 'JOIN', 'DEFECT': 'DEFECT', 'RALLY': 'RALLY', 'LAUNCH': 'LAUNCH',
|
|
62
|
+
'MESSAGE': 'MESSAGE', 'STRONGHOLD': 'STRONGHOLD', 'WAR_LOAN': 'WAR_LOAN',
|
|
63
|
+
'REPAY_LOAN': 'REPAY_LOAN', 'SIEGE': 'SIEGE', 'ASCEND': 'ASCEND',
|
|
64
|
+
'RAZE': 'RAZE', 'TITHE': 'TITHE', 'INFILTRATE': 'INFILTRATE', 'FUD': 'FUD',
|
|
65
|
+
// Aliases
|
|
66
|
+
'BUY': 'JOIN', 'INVEST': 'JOIN', 'ENTER': 'JOIN', 'JOINING': 'JOIN', 'BUYING': 'JOIN', 'INVESTING': 'JOIN',
|
|
67
|
+
'REINFORCE': 'JOIN', 'INCREASE': 'JOIN', 'GATHER': 'JOIN',
|
|
68
|
+
'SELL': 'DEFECT', 'DUMP': 'DEFECT', 'EXIT': 'DEFECT', 'LEAVE': 'DEFECT', 'DEFECTING': 'DEFECT', 'DEFECTION': 'DEFECT', 'SELLING': 'DEFECT', 'DUMPING': 'DEFECT',
|
|
69
|
+
'WARN': 'FUD', 'ATTACK': 'SIEGE', 'LIQUIDATE': 'SIEGE',
|
|
70
|
+
'BORROW': 'WAR_LOAN', 'LOAN': 'WAR_LOAN',
|
|
71
|
+
'REPAY': 'REPAY_LOAN', 'STAR': 'RALLY', 'VOTE': 'RALLY', 'SUPPORT': 'RALLY',
|
|
72
|
+
'SEND': 'MESSAGE', 'SAY': 'MESSAGE', 'CHAT': 'MESSAGE', 'MSG': 'MESSAGE', 'MESSAGING': 'MESSAGE',
|
|
73
|
+
'CREATE': 'LAUNCH', 'FOUND': 'LAUNCH', 'HARVEST': 'TITHE',
|
|
74
|
+
'MIGRATE': 'ASCEND', 'RECLAIM': 'RAZE', 'SPY': 'INFILTRATE',
|
|
75
|
+
'INVESTIGATION': 'INFILTRATE', 'INVESTIGATE': 'INFILTRATE', 'SCOUT': 'INFILTRATE', 'RECON': 'INFILTRATE',
|
|
76
|
+
'PLEDGE': 'JOIN', 'ALLY': 'JOIN', 'BACK': 'JOIN', 'FUND': 'JOIN',
|
|
77
|
+
'WITHDRAW': 'DEFECT', 'RETREAT': 'DEFECT', 'ABANDON': 'DEFECT', 'BAIL': 'DEFECT',
|
|
78
|
+
'ANNOUNCE': 'MESSAGE', 'BROADCAST': 'MESSAGE', 'COMM': 'MESSAGE', 'COMMS': 'MESSAGE', 'REPORT': 'MESSAGE',
|
|
79
|
+
'SMEAR': 'FUD', 'SLANDER': 'FUD', 'DISCREDIT': 'FUD', 'SABOTAGE': 'FUD', 'UNDERMINE': 'FUD', 'ARGUE': 'FUD', 'TRASH': 'FUD', 'CRITICIZE': 'FUD', 'MOCK': 'FUD', 'FUDS': 'FUD',
|
|
80
|
+
'ENDORSE': 'RALLY', 'PROMOTE': 'RALLY', 'BOOST': 'RALLY',
|
|
81
|
+
// Common misspellings
|
|
82
|
+
'DEFLECT': 'DEFECT', 'DEFEKT': 'DEFECT', 'DEFCT': 'DEFECT', 'DEFFECT': 'DEFECT',
|
|
83
|
+
'JION': 'JOIN', 'JOING': 'JOIN', 'JOIIN': 'JOIN',
|
|
84
|
+
'RALEY': 'RALLY', 'RALY': 'RALLY', 'RALLLY': 'RALLY',
|
|
85
|
+
'LANCH': 'LAUNCH', 'LAUCH': 'LAUNCH',
|
|
86
|
+
'MESAGE': 'MESSAGE', 'MESSGE': 'MESSAGE', 'MASSGE': 'MESSAGE', 'MESS': 'MESSAGE', 'MESSENGER': 'MESSAGE', 'MESSAGES': 'MESSAGE',
|
|
87
|
+
'SEIGE': 'SIEGE', 'SEIG': 'SIEGE',
|
|
88
|
+
'INFLTRATE': 'INFILTRATE', 'INFILTRTE': 'INFILTRATE', 'INFILTRATING': 'INFILTRATE', 'INFIL': 'INFILTRATE', 'INFILTRAT': 'INFILTRATE',
|
|
89
|
+
'ALERT': 'FUD', 'EXPOSE': 'FUD',
|
|
90
|
+
'QUESTION': 'MESSAGE', 'ASK': 'MESSAGE', 'TAUNT': 'FUD', 'RALLYING': 'RALLY',
|
|
91
|
+
'TICKER': 'MESSAGE', 'ACTION': 'MESSAGE', // LLM copies placeholder words from prompt
|
|
92
|
+
};
|
|
93
|
+
// Stronghold defaults
|
|
94
|
+
exports.STRONGHOLD_FUND_SOL = 35;
|
|
95
|
+
exports.STRONGHOLD_TOPUP_THRESHOLD_SOL = 5;
|
|
96
|
+
exports.STRONGHOLD_TOPUP_RESERVE_SOL = 5;
|
|
97
|
+
const assignPersonality = () => {
|
|
98
|
+
const personalities = ['loyalist', 'mercenary', 'provocateur', 'scout', 'whale'];
|
|
99
|
+
const weights = [0.30, 0.25, 0.15, 0.20, 0.10];
|
|
100
|
+
const roll = Math.random();
|
|
101
|
+
let cumulative = 0;
|
|
102
|
+
for (let i = 0; i < weights.length; i++) {
|
|
103
|
+
cumulative += weights[i];
|
|
104
|
+
if (roll < cumulative)
|
|
105
|
+
return personalities[i];
|
|
106
|
+
}
|
|
107
|
+
return 'loyalist';
|
|
108
|
+
};
|
|
109
|
+
exports.assignPersonality = assignPersonality;
|
package/dist/error.d.ts
ADDED
package/dist/error.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseCustomError = exports.SKIP_ERRORS = exports.PROGRAM_ERRORS = void 0;
|
|
4
|
+
// ─── Program Error Codes ────────────────────────────────────────────
|
|
5
|
+
exports.PROGRAM_ERRORS = {
|
|
6
|
+
6000: 'MathOverflow',
|
|
7
|
+
6001: 'SlippageExceeded',
|
|
8
|
+
6002: 'MaxWalletExceeded (2% cap)',
|
|
9
|
+
6003: 'InsufficientTokens',
|
|
10
|
+
6004: 'InsufficientSol',
|
|
11
|
+
6005: 'InsufficientUserBalance',
|
|
12
|
+
6006: 'BondingComplete',
|
|
13
|
+
6007: 'BondingNotComplete',
|
|
14
|
+
6008: 'AlreadyVoted',
|
|
15
|
+
6009: 'NoTokensToVote',
|
|
16
|
+
6010: 'AlreadyMigrated',
|
|
17
|
+
6011: 'InvalidAuthority',
|
|
18
|
+
6012: 'AmountTooSmall',
|
|
19
|
+
6013: 'ProtocolPaused',
|
|
20
|
+
6014: 'ZeroAmount',
|
|
21
|
+
6022: 'VoteRequired',
|
|
22
|
+
6030: 'NotMigrated',
|
|
23
|
+
6044: 'LendingNotEnabled',
|
|
24
|
+
6045: 'LendingRequiresMigration',
|
|
25
|
+
6046: 'LtvExceeded',
|
|
26
|
+
6047: 'LendingCapExceeded',
|
|
27
|
+
6048: 'UserBorrowCapExceeded',
|
|
28
|
+
6049: 'BorrowTooSmall (min 0.1 SOL)',
|
|
29
|
+
6050: 'NoActiveLoan',
|
|
30
|
+
6051: 'NotLiquidatable',
|
|
31
|
+
6052: 'EmptyBorrowRequest',
|
|
32
|
+
6053: 'RepayExceedsDebt',
|
|
33
|
+
6054: 'InvalidPoolAccount',
|
|
34
|
+
6055: 'InsufficientVaultBalance',
|
|
35
|
+
6056: 'VaultUnauthorized',
|
|
36
|
+
6057: 'WalletNotLinked',
|
|
37
|
+
// Token-2022 / SPL errors
|
|
38
|
+
2505: 'InsufficientFunds (token balance too low)',
|
|
39
|
+
};
|
|
40
|
+
// Errors that mean "don't retry this action on this faction right now"
|
|
41
|
+
exports.SKIP_ERRORS = new Set([
|
|
42
|
+
6002, // MaxWalletExceeded — already at 2% cap
|
|
43
|
+
6006, // BondingComplete — use DEX instead
|
|
44
|
+
6007, // BondingNotComplete — can't migrate yet
|
|
45
|
+
6010, // AlreadyMigrated
|
|
46
|
+
6044, // LendingNotEnabled
|
|
47
|
+
6045, // LendingRequiresMigration
|
|
48
|
+
6047, // LendingCapExceeded
|
|
49
|
+
6051, // NotLiquidatable
|
|
50
|
+
]);
|
|
51
|
+
const parseCustomError = (err) => {
|
|
52
|
+
const msg = err?.message || String(err);
|
|
53
|
+
// Match "custom program error: 0x1772" or "Custom: 6002"
|
|
54
|
+
const hexMatch = msg.match(/custom program error:\s*0x([0-9a-fA-F]+)/i);
|
|
55
|
+
if (hexMatch) {
|
|
56
|
+
const code = parseInt(hexMatch[1], 16);
|
|
57
|
+
return { code, name: exports.PROGRAM_ERRORS[code] || `Unknown(${code})` };
|
|
58
|
+
}
|
|
59
|
+
const decMatch = msg.match(/Custom:\s*(\d+)/);
|
|
60
|
+
if (decMatch) {
|
|
61
|
+
const code = parseInt(decMatch[1], 10);
|
|
62
|
+
return { code, name: exports.PROGRAM_ERRORS[code] || `Unknown(${code})` };
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
};
|
|
66
|
+
exports.parseCustomError = parseCustomError;
|