@exagent/agent 0.3.0 → 0.3.2
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/.turbo/turbo-build.log +6 -6
- package/dist/chunk-4UVMO6ZM.js +6318 -0
- package/dist/chunk-5NU6FDDE.js +6020 -0
- package/dist/chunk-GPMXUMYH.js +5991 -0
- package/dist/chunk-IJK4EFTJ.js +6043 -0
- package/dist/chunk-J3NG7AGT.js +6047 -0
- package/dist/chunk-QG22GADV.js +6316 -0
- package/dist/chunk-SVFTC5V2.js +6021 -0
- package/dist/chunk-VDK4XPAC.js +6318 -0
- package/dist/cli.js +252 -129
- package/dist/index.d.ts +15 -0
- package/dist/index.js +1 -1
- package/package.json +9 -2
- package/src/cli.ts +33 -15
- package/src/config.ts +6 -0
- package/src/runtime.ts +370 -37
- package/src/scrub-secrets.ts +39 -0
- package/src/setup.ts +228 -129
- package/src/strategy/loader.ts +35 -2
- package/src/ui.ts +75 -0
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
readConfigFile,
|
|
9
9
|
writeConfigFile,
|
|
10
10
|
writeSampleConfig
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-VDK4XPAC.js";
|
|
12
12
|
|
|
13
13
|
// src/cli.ts
|
|
14
14
|
import { Command } from "commander";
|
|
@@ -17,97 +17,82 @@ import { Command } from "commander";
|
|
|
17
17
|
import { chmodSync, existsSync, mkdirSync, writeFileSync } from "fs";
|
|
18
18
|
import { homedir } from "os";
|
|
19
19
|
import { dirname, resolve } from "path";
|
|
20
|
-
import { createInterface } from "readline/promises";
|
|
21
|
-
import { stdin as input, stdout as output } from "process";
|
|
22
20
|
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
import * as clack from "@clack/prompts";
|
|
22
|
+
|
|
23
|
+
// src/ui.ts
|
|
24
|
+
import figlet from "figlet";
|
|
25
|
+
import gradient from "gradient-string";
|
|
26
|
+
import boxen from "boxen";
|
|
27
|
+
import pc from "picocolors";
|
|
28
|
+
import { createRequire } from "module";
|
|
29
|
+
var brandGradient = gradient(["#3B82F6", "#6366F1", "#7C3AED"]);
|
|
30
|
+
var accentGradient = gradient(["#22D3EE", "#3B82F6"]);
|
|
31
|
+
function getVersion() {
|
|
29
32
|
try {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
const require2 = createRequire(import.meta.url);
|
|
34
|
+
const pkg = require2("../package.json");
|
|
35
|
+
return pkg.version || "0.0.0";
|
|
36
|
+
} catch {
|
|
37
|
+
return "0.0.0";
|
|
33
38
|
}
|
|
34
39
|
}
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
try {
|
|
45
|
-
const answer = (await rl.question(question)).trim();
|
|
46
|
-
output.write("\n");
|
|
47
|
-
return answer;
|
|
48
|
-
} finally {
|
|
49
|
-
rl.close();
|
|
50
|
-
}
|
|
40
|
+
function printBanner() {
|
|
41
|
+
const art = figlet.textSync("EXAGENT", {
|
|
42
|
+
font: "Small",
|
|
43
|
+
horizontalLayout: "default"
|
|
44
|
+
});
|
|
45
|
+
console.log();
|
|
46
|
+
console.log(brandGradient(art));
|
|
47
|
+
console.log(pc.dim(` v${getVersion()}`));
|
|
48
|
+
console.log();
|
|
51
49
|
}
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
function printSuccess(title, lines) {
|
|
51
|
+
const body = [
|
|
52
|
+
"",
|
|
53
|
+
pc.bold(pc.white(title)),
|
|
54
|
+
"",
|
|
55
|
+
...lines.map((l) => ` ${l}`),
|
|
56
|
+
""
|
|
57
|
+
].join("\n");
|
|
58
|
+
console.log();
|
|
59
|
+
console.log(boxen(body, {
|
|
60
|
+
padding: { top: 0, bottom: 0, left: 2, right: 2 },
|
|
61
|
+
borderColor: "#3B82F6",
|
|
62
|
+
borderStyle: "round",
|
|
63
|
+
dimBorder: false
|
|
64
|
+
}));
|
|
65
|
+
console.log();
|
|
54
66
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
output.write("If you lose this password and the agent wallet holds funds, those funds cannot be recovered.\n\n");
|
|
59
|
-
const ack = await prompt('Type "I UNDERSTAND" to continue: ');
|
|
60
|
-
if (ack !== "I UNDERSTAND") {
|
|
61
|
-
throw new Error("Secure setup aborted");
|
|
62
|
-
}
|
|
63
|
-
while (true) {
|
|
64
|
-
const password = await promptSecret("Create a device password (min 12 chars): ");
|
|
65
|
-
if (password.length < 12) {
|
|
66
|
-
output.write("Password must be at least 12 characters.\n");
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
const confirm = await promptSecret("Confirm device password: ");
|
|
70
|
-
if (password !== confirm) {
|
|
71
|
-
output.write("Passwords did not match. Try again.\n");
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
return password;
|
|
75
|
-
}
|
|
67
|
+
function printStep(step, total, label) {
|
|
68
|
+
console.log();
|
|
69
|
+
console.log(accentGradient(` Step ${step} of ${total}`) + pc.dim(` \u2014 ${label}`));
|
|
76
70
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const choice = (await prompt("Wallet setup \u2014 [1] generate new wallet locally, [2] use existing private key: ")).trim();
|
|
80
|
-
if (choice === "1") {
|
|
81
|
-
const privateKey = generatePrivateKey();
|
|
82
|
-
const address = privateKeyToAccount(privateKey).address;
|
|
83
|
-
output.write(`Generated wallet address: ${address}
|
|
84
|
-
`);
|
|
85
|
-
return privateKey;
|
|
86
|
-
}
|
|
87
|
-
if (choice === "2") {
|
|
88
|
-
const privateKey = await promptSecret("Wallet private key (0x...): ");
|
|
89
|
-
if (/^0x[a-fA-F0-9]{64}$/.test(privateKey)) {
|
|
90
|
-
return privateKey;
|
|
91
|
-
}
|
|
92
|
-
output.write("Invalid private key. Expected a 32-byte hex string prefixed with 0x.\n");
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
output.write("Enter 1 or 2.\n");
|
|
96
|
-
}
|
|
71
|
+
function printDone(message) {
|
|
72
|
+
console.log(` ${pc.green("\u2713")} ${message}`);
|
|
97
73
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
74
|
+
function printInfo(message) {
|
|
75
|
+
console.log(` ${pc.dim("\u2502")} ${message}`);
|
|
76
|
+
}
|
|
77
|
+
function printError(message) {
|
|
78
|
+
console.log(` ${pc.red("\u2717")} ${message}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/setup.ts
|
|
82
|
+
function expandHomeDir(path) {
|
|
83
|
+
if (!path.startsWith("~/")) return path;
|
|
84
|
+
return resolve(homedir(), path.slice(2));
|
|
85
|
+
}
|
|
86
|
+
function cancelled() {
|
|
87
|
+
clack.cancel("Setup cancelled.");
|
|
88
|
+
process.exit(0);
|
|
106
89
|
}
|
|
107
90
|
async function consumeBootstrapPackage(config) {
|
|
108
91
|
if (!config.secrets?.bootstrapToken) {
|
|
109
92
|
return { apiToken: "" };
|
|
110
93
|
}
|
|
94
|
+
const apiHost = new URL(config.apiUrl).host;
|
|
95
|
+
printInfo(`Connecting to ${pc.cyan(apiHost)}...`);
|
|
111
96
|
const res = await fetch(`${config.apiUrl}/v1/agents/bootstrap/consume`, {
|
|
112
97
|
method: "POST",
|
|
113
98
|
headers: { "Content-Type": "application/json" },
|
|
@@ -118,46 +103,121 @@ async function consumeBootstrapPackage(config) {
|
|
|
118
103
|
});
|
|
119
104
|
if (!res.ok) {
|
|
120
105
|
const body = await res.text();
|
|
121
|
-
throw new Error(`Failed to consume
|
|
106
|
+
throw new Error(`Failed to consume bootstrap package: ${body}`);
|
|
122
107
|
}
|
|
123
108
|
const data = await res.json();
|
|
124
109
|
return data.payload;
|
|
125
110
|
}
|
|
126
|
-
async function
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
111
|
+
async function setupWallet(config) {
|
|
112
|
+
if (config.wallet?.privateKey) {
|
|
113
|
+
const account = privateKeyToAccount(config.wallet.privateKey);
|
|
114
|
+
printDone(`Using existing wallet: ${pc.dim(account.address)}`);
|
|
115
|
+
return config.wallet.privateKey;
|
|
116
|
+
}
|
|
117
|
+
const method = await clack.select({
|
|
118
|
+
message: "How would you like to set up your wallet?",
|
|
119
|
+
options: [
|
|
120
|
+
{ value: "generate", label: "Generate new wallet locally", hint: "recommended" },
|
|
121
|
+
{ value: "import", label: "Import existing private key" }
|
|
122
|
+
]
|
|
123
|
+
});
|
|
124
|
+
if (clack.isCancel(method)) cancelled();
|
|
125
|
+
if (method === "generate") {
|
|
126
|
+
const privateKey2 = generatePrivateKey();
|
|
127
|
+
const address2 = privateKeyToAccount(privateKey2).address;
|
|
128
|
+
printDone(`Wallet created: ${pc.dim(address2)}`);
|
|
129
|
+
return privateKey2;
|
|
131
130
|
}
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
const privateKey = await clack.password({
|
|
132
|
+
message: "Wallet private key (0x...):",
|
|
133
|
+
validate: (val) => {
|
|
134
|
+
if (!/^0x[a-fA-F0-9]{64}$/.test(val)) {
|
|
135
|
+
return "Invalid private key. Expected a 32-byte hex string prefixed with 0x.";
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
if (clack.isCancel(privateKey)) cancelled();
|
|
140
|
+
const address = privateKeyToAccount(privateKey).address;
|
|
141
|
+
printDone(`Wallet imported: ${pc.dim(address)}`);
|
|
142
|
+
return privateKey;
|
|
143
|
+
}
|
|
144
|
+
var LLM_PROVIDERS = ["openai", "anthropic", "google", "deepseek", "mistral", "groq", "together", "ollama"];
|
|
145
|
+
async function setupLlm(config, bootstrapPayload) {
|
|
146
|
+
let provider = config.llm?.provider || bootstrapPayload.llm?.provider;
|
|
147
|
+
if (provider) {
|
|
148
|
+
printInfo(`Provider: ${pc.cyan(provider)} ${pc.dim("(from dashboard)")}`);
|
|
149
|
+
} else {
|
|
150
|
+
const selected = await clack.select({
|
|
151
|
+
message: "LLM provider:",
|
|
152
|
+
options: LLM_PROVIDERS.map((p) => ({ value: p, label: p }))
|
|
153
|
+
});
|
|
154
|
+
if (clack.isCancel(selected)) cancelled();
|
|
155
|
+
provider = selected;
|
|
134
156
|
}
|
|
135
|
-
|
|
136
|
-
|
|
157
|
+
let model = config.llm?.model || bootstrapPayload.llm?.model;
|
|
158
|
+
if (model) {
|
|
159
|
+
printInfo(`Model: ${pc.cyan(model)} ${pc.dim("(from dashboard)")}`);
|
|
160
|
+
} else {
|
|
161
|
+
const entered = await clack.text({
|
|
162
|
+
message: "LLM model:",
|
|
163
|
+
placeholder: "gpt-4o",
|
|
164
|
+
validate: (val) => {
|
|
165
|
+
if (!val.trim()) return "Model name is required.";
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
if (clack.isCancel(entered)) cancelled();
|
|
169
|
+
model = entered;
|
|
137
170
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
171
|
+
let apiKey;
|
|
172
|
+
if (bootstrapPayload.llm?.apiKey) {
|
|
173
|
+
const useBootstrap = await clack.confirm({
|
|
174
|
+
message: "LLM API key received from dashboard. Use it?",
|
|
175
|
+
initialValue: true
|
|
176
|
+
});
|
|
177
|
+
if (clack.isCancel(useBootstrap)) cancelled();
|
|
178
|
+
if (useBootstrap) {
|
|
179
|
+
apiKey = bootstrapPayload.llm.apiKey;
|
|
142
180
|
}
|
|
143
181
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
walletPrivateKey: bootstrapPayload.walletPrivateKey || nextConfig.wallet?.privateKey || await promptWalletPrivateKey(),
|
|
147
|
-
llmApiKey: bootstrapPayload.llm?.apiKey || nextConfig.llm.apiKey || await promptSecret("Agent LLM API key: ")
|
|
148
|
-
};
|
|
149
|
-
if (!secrets.apiToken) {
|
|
150
|
-
throw new Error("Agent relay token is required");
|
|
182
|
+
if (!apiKey) {
|
|
183
|
+
apiKey = config.llm?.apiKey;
|
|
151
184
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
185
|
+
if (!apiKey) {
|
|
186
|
+
const entered = await clack.password({
|
|
187
|
+
message: "LLM API key:",
|
|
188
|
+
validate: (val) => {
|
|
189
|
+
if (!val.trim()) return "API key is required.";
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
if (clack.isCancel(entered)) cancelled();
|
|
193
|
+
apiKey = entered;
|
|
194
|
+
}
|
|
195
|
+
printDone("LLM configured");
|
|
196
|
+
return { provider, model, apiKey };
|
|
157
197
|
}
|
|
158
|
-
function
|
|
198
|
+
async function setupEncryption() {
|
|
199
|
+
printInfo(`Secrets encrypted with ${pc.cyan("AES-256-GCM")} (${pc.cyan("scrypt")} KDF)`);
|
|
200
|
+
printInfo("The password never leaves this machine.");
|
|
201
|
+
console.log();
|
|
202
|
+
const password2 = await clack.password({
|
|
203
|
+
message: "Choose a device password (12+ characters):",
|
|
204
|
+
validate: (val) => {
|
|
205
|
+
if (val.length < 12) return "Password must be at least 12 characters.";
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
if (clack.isCancel(password2)) cancelled();
|
|
209
|
+
const confirm2 = await clack.password({
|
|
210
|
+
message: "Confirm password:",
|
|
211
|
+
validate: (val) => {
|
|
212
|
+
if (val !== password2) return "Passwords do not match.";
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
if (clack.isCancel(confirm2)) cancelled();
|
|
216
|
+
return password2;
|
|
217
|
+
}
|
|
218
|
+
function writeSecureStore(path, secrets, password2) {
|
|
159
219
|
const secureStorePath = expandHomeDir(path);
|
|
160
|
-
const encrypted = encryptSecretPayload(secrets,
|
|
220
|
+
const encrypted = encryptSecretPayload(secrets, password2);
|
|
161
221
|
const dir = dirname(secureStorePath);
|
|
162
222
|
if (!existsSync(dir)) {
|
|
163
223
|
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
@@ -169,74 +229,130 @@ function writeSecureStore(path, secrets, password) {
|
|
|
169
229
|
}
|
|
170
230
|
return secureStorePath;
|
|
171
231
|
}
|
|
232
|
+
async function promptSecretPassword(question = "Device password:") {
|
|
233
|
+
const password2 = await clack.password({ message: question });
|
|
234
|
+
if (clack.isCancel(password2)) cancelled();
|
|
235
|
+
return password2;
|
|
236
|
+
}
|
|
172
237
|
async function ensureLocalSetup(configPath) {
|
|
173
238
|
const config = readConfigFile(configPath);
|
|
174
239
|
const existingSecureStorePath = config.secrets?.secureStorePath ? expandHomeDir(config.secrets.secureStorePath) : null;
|
|
175
240
|
if (existingSecureStorePath && !config.secrets?.bootstrapToken && existsSync(existingSecureStorePath) && !config.apiToken && !config.wallet?.privateKey && !config.llm.apiKey) {
|
|
176
241
|
return;
|
|
177
242
|
}
|
|
243
|
+
printBanner();
|
|
244
|
+
clack.intro(pc.bold("Agent Setup"));
|
|
245
|
+
printStep(1, 4, "Bootstrap package");
|
|
178
246
|
const bootstrapPayload = await consumeBootstrapPackage(config);
|
|
179
|
-
|
|
180
|
-
|
|
247
|
+
if (config.secrets?.bootstrapToken) {
|
|
248
|
+
printDone("Bootstrap package consumed");
|
|
249
|
+
if (bootstrapPayload.llm?.provider) {
|
|
250
|
+
printInfo(`LLM config received: ${pc.cyan(bootstrapPayload.llm.provider)}${bootstrapPayload.llm.model ? ` / ${pc.cyan(bootstrapPayload.llm.model)}` : ""}`);
|
|
251
|
+
}
|
|
252
|
+
} else {
|
|
253
|
+
printInfo("No bootstrap token \u2014 manual configuration");
|
|
254
|
+
}
|
|
255
|
+
printStep(2, 4, "Wallet setup");
|
|
256
|
+
const walletPrivateKey = await setupWallet(config);
|
|
257
|
+
printStep(3, 4, "LLM configuration");
|
|
258
|
+
const llm = await setupLlm(config, bootstrapPayload);
|
|
259
|
+
printStep(4, 4, "Device encryption");
|
|
260
|
+
const password2 = await setupEncryption();
|
|
261
|
+
const secrets = {
|
|
262
|
+
apiToken: bootstrapPayload.apiToken || config.apiToken || "",
|
|
263
|
+
walletPrivateKey,
|
|
264
|
+
llmApiKey: llm.apiKey
|
|
265
|
+
};
|
|
266
|
+
if (!secrets.apiToken) {
|
|
267
|
+
const token = await clack.password({
|
|
268
|
+
message: "Agent relay token:",
|
|
269
|
+
validate: (val) => {
|
|
270
|
+
if (!val.trim()) return "Relay token is required.";
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
if (clack.isCancel(token)) cancelled();
|
|
274
|
+
secrets.apiToken = token;
|
|
275
|
+
}
|
|
276
|
+
const nextConfig = structuredClone(config);
|
|
277
|
+
nextConfig.llm = {
|
|
278
|
+
...nextConfig.llm,
|
|
279
|
+
provider: llm.provider,
|
|
280
|
+
model: llm.model
|
|
281
|
+
};
|
|
282
|
+
delete nextConfig.apiToken;
|
|
283
|
+
delete nextConfig.wallet;
|
|
284
|
+
delete nextConfig.llm.apiKey;
|
|
181
285
|
const secureStorePath = writeSecureStore(
|
|
182
286
|
nextConfig.secrets?.secureStorePath || getDefaultSecureStorePath(nextConfig.agentId),
|
|
183
287
|
secrets,
|
|
184
|
-
|
|
288
|
+
password2
|
|
185
289
|
);
|
|
186
|
-
nextConfig.secrets = {
|
|
187
|
-
secureStorePath
|
|
188
|
-
};
|
|
290
|
+
nextConfig.secrets = { secureStorePath };
|
|
189
291
|
writeConfigFile(configPath, nextConfig);
|
|
190
|
-
|
|
191
|
-
|
|
292
|
+
printDone(`Encrypted store: ${pc.dim(secureStorePath)}`);
|
|
293
|
+
clack.outro(pc.green("Setup complete"));
|
|
294
|
+
printSuccess("Ready", [
|
|
295
|
+
`${pc.cyan("npx exagent run")} Start trading`,
|
|
296
|
+
`${pc.cyan("npx exagent status")} Check connection`,
|
|
297
|
+
"",
|
|
298
|
+
`${pc.dim("Dashboard:")} ${pc.cyan("https://exagent.io")}`
|
|
299
|
+
]);
|
|
192
300
|
}
|
|
193
301
|
|
|
194
302
|
// src/cli.ts
|
|
195
303
|
var program = new Command();
|
|
196
|
-
program.name("exagent").description("Exagent \u2014 LLM trading agent runtime").version("0.
|
|
304
|
+
program.name("exagent").description("Exagent \u2014 LLM trading agent runtime").version("0.3.0");
|
|
197
305
|
program.command("init").description("Create a sample agent configuration file").option("--agent-id <id>", "Agent ID (from dashboard)", "my-agent").option("--api-url <url>", "API server URL", "http://localhost:3002").option("--config <path>", "Config file path", "agent-config.json").action((opts) => {
|
|
306
|
+
printBanner();
|
|
198
307
|
writeSampleConfig(opts.agentId, opts.apiUrl, opts.config);
|
|
199
|
-
|
|
200
|
-
console.log(
|
|
201
|
-
console.log(`
|
|
308
|
+
printDone(`Created ${pc.cyan(opts.config)}`);
|
|
309
|
+
console.log();
|
|
310
|
+
console.log(` Edit only the public strategy/venue/risk settings.`);
|
|
311
|
+
console.log(` Then run: ${pc.cyan(`exagent setup --config ${opts.config}`)}`);
|
|
312
|
+
console.log();
|
|
202
313
|
});
|
|
203
314
|
program.command("setup").description("Run first-time secure local setup for agent secrets").option("--config <path>", "Config file path", "agent-config.json").action(async (opts) => {
|
|
204
315
|
try {
|
|
205
316
|
await ensureLocalSetup(opts.config);
|
|
206
|
-
console.log("Secure local setup complete.");
|
|
207
317
|
} catch (err) {
|
|
208
|
-
|
|
318
|
+
printError(err.message);
|
|
209
319
|
process.exit(1);
|
|
210
320
|
}
|
|
211
321
|
});
|
|
212
322
|
program.command("run").description("Start the agent").option("--config <path>", "Config file path", "agent-config.json").action(async (opts) => {
|
|
213
323
|
try {
|
|
214
324
|
await ensureLocalSetup(opts.config);
|
|
325
|
+
printBanner();
|
|
215
326
|
const config = await loadConfig(opts.config, {
|
|
216
327
|
getSecretPassword: async () => promptSecretPassword()
|
|
217
328
|
});
|
|
329
|
+
printDone(`Agent ${pc.cyan(config.agentId)} starting...`);
|
|
330
|
+
console.log();
|
|
218
331
|
const runtime = new AgentRuntime(config);
|
|
219
332
|
await runtime.start();
|
|
220
333
|
await new Promise(() => {
|
|
221
334
|
});
|
|
222
335
|
} catch (err) {
|
|
223
|
-
|
|
336
|
+
printError(err.message);
|
|
224
337
|
process.exit(1);
|
|
225
338
|
}
|
|
226
339
|
});
|
|
227
340
|
program.command("templates").description("List available strategy templates").action(() => {
|
|
341
|
+
printBanner();
|
|
228
342
|
const templates = listTemplates();
|
|
229
|
-
console.log("
|
|
343
|
+
console.log(pc.bold(" Strategy Templates"));
|
|
344
|
+
console.log();
|
|
230
345
|
for (const t of templates) {
|
|
231
|
-
console.log(` ${t.id}`);
|
|
232
|
-
console.log(`
|
|
233
|
-
console.log(`
|
|
346
|
+
console.log(` ${pc.cyan(t.id)}`);
|
|
347
|
+
console.log(` ${t.name} \u2014 ${pc.dim(t.description)}`);
|
|
348
|
+
console.log(` ${pc.dim(`Risk: ${t.riskLevel} | Venues: ${t.venues.join(", ") || "any"}`)}`);
|
|
234
349
|
console.log();
|
|
235
350
|
}
|
|
236
351
|
});
|
|
237
352
|
program.command("status").description("Check agent status").option("--config <path>", "Config file path", "agent-config.json").action(async (opts) => {
|
|
238
353
|
try {
|
|
239
354
|
await ensureLocalSetup(opts.config);
|
|
355
|
+
printBanner();
|
|
240
356
|
const config = await loadConfig(opts.config, {
|
|
241
357
|
getSecretPassword: async () => promptSecretPassword()
|
|
242
358
|
});
|
|
@@ -244,13 +360,20 @@ program.command("status").description("Check agent status").option("--config <pa
|
|
|
244
360
|
headers: { Authorization: `Bearer ${config.apiToken}` }
|
|
245
361
|
});
|
|
246
362
|
if (!res.ok) {
|
|
247
|
-
|
|
363
|
+
printError(`API error: ${res.status}`);
|
|
248
364
|
process.exit(1);
|
|
249
365
|
}
|
|
250
366
|
const agent = await res.json();
|
|
251
|
-
|
|
367
|
+
const name = agent.name || config.agentId;
|
|
368
|
+
const status = agent.status || "unknown";
|
|
369
|
+
const statusColor = status === "online" ? pc.green : status === "error" ? pc.red : pc.yellow;
|
|
370
|
+
printSuccess(name, [
|
|
371
|
+
`${pc.dim("Status:")} ${statusColor(status)}`,
|
|
372
|
+
`${pc.dim("Agent:")} ${pc.cyan(config.agentId)}`,
|
|
373
|
+
`${pc.dim("API:")} ${pc.dim(config.apiUrl)}`
|
|
374
|
+
]);
|
|
252
375
|
} catch (err) {
|
|
253
|
-
|
|
376
|
+
printError(err.message);
|
|
254
377
|
process.exit(1);
|
|
255
378
|
}
|
|
256
379
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -19,7 +19,9 @@ interface RuntimeConfig {
|
|
|
19
19
|
};
|
|
20
20
|
strategy: {
|
|
21
21
|
file?: string;
|
|
22
|
+
code?: string;
|
|
22
23
|
template?: string;
|
|
24
|
+
venues?: string[];
|
|
23
25
|
prompt?: {
|
|
24
26
|
name?: string;
|
|
25
27
|
systemPrompt: string;
|
|
@@ -124,6 +126,18 @@ declare class AgentRuntime {
|
|
|
124
126
|
constructor(config: RuntimeConfig);
|
|
125
127
|
start(): Promise<void>;
|
|
126
128
|
stop(): Promise<void>;
|
|
129
|
+
private configureLLMAdapter;
|
|
130
|
+
private teardownVenues;
|
|
131
|
+
private waitForCycleCompletion;
|
|
132
|
+
private getConfiguredSpotVenue;
|
|
133
|
+
private normalizeVenueForExecution;
|
|
134
|
+
private isVenueConfigured;
|
|
135
|
+
private canExecuteVenue;
|
|
136
|
+
private getPreferredExecutionVenues;
|
|
137
|
+
private buildRuntimeVenuesFromSelection;
|
|
138
|
+
private buildStrategyFallbackPrompt;
|
|
139
|
+
private extractStrategyConfigFromAgentConfig;
|
|
140
|
+
private applyExecutionMode;
|
|
127
141
|
private initializeVenues;
|
|
128
142
|
private startTrading;
|
|
129
143
|
private stopTrading;
|
|
@@ -415,6 +429,7 @@ declare function createLLMAdapter(config: LLMConfig): LLMAdapter;
|
|
|
415
429
|
|
|
416
430
|
declare function loadStrategy(config: {
|
|
417
431
|
file?: string;
|
|
432
|
+
code?: string;
|
|
418
433
|
template?: string;
|
|
419
434
|
prompt?: {
|
|
420
435
|
name?: string;
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exagent/agent",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -18,15 +18,22 @@
|
|
|
18
18
|
"dev": "tsup src/index.ts src/cli.ts --format esm --dts --watch"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@
|
|
21
|
+
"@clack/prompts": "^1.1.0",
|
|
22
|
+
"@exagent/sdk": "^0.2.1",
|
|
22
23
|
"@polymarket/clob-client": "^4.0.0",
|
|
24
|
+
"boxen": "^8.0.1",
|
|
23
25
|
"commander": "^12.0.0",
|
|
24
26
|
"ethers": "^5.7.2",
|
|
27
|
+
"figlet": "^1.10.0",
|
|
28
|
+
"gradient-string": "^3.0.0",
|
|
29
|
+
"picocolors": "^1.1.1",
|
|
25
30
|
"viem": "^2.21.0",
|
|
26
31
|
"ws": "^8.16.0",
|
|
27
32
|
"zod": "^3.22.0"
|
|
28
33
|
},
|
|
29
34
|
"devDependencies": {
|
|
35
|
+
"@types/figlet": "^1.7.0",
|
|
36
|
+
"@types/gradient-string": "^1.1.6",
|
|
30
37
|
"@types/node": "^20.0.0",
|
|
31
38
|
"@types/ws": "^8.5.0",
|
|
32
39
|
"tsup": "^8.0.0",
|