@exagent/agent 0.3.6 → 0.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/chunk-7UGLJO6W.js +6392 -0
  2. package/dist/chunk-EHAOPCTJ.js +6406 -0
  3. package/dist/chunk-FGMXTW5I.js +6540 -0
  4. package/dist/chunk-GYYW4EKM.js +6756 -0
  5. package/dist/chunk-IVA2SCSN.js +6756 -0
  6. package/dist/chunk-JHXCSGPC.js +6352 -0
  7. package/dist/chunk-V6O4UXVN.js +6345 -0
  8. package/dist/chunk-WTECTX2Z.js +6345 -0
  9. package/dist/cli.js +2 -2
  10. package/dist/index.d.ts +24 -2
  11. package/dist/index.js +1 -1
  12. package/package.json +12 -9
  13. package/src/bridge/across.ts +0 -240
  14. package/src/bridge/bridge-manager.ts +0 -87
  15. package/src/bridge/index.ts +0 -9
  16. package/src/bridge/types.ts +0 -77
  17. package/src/chains.ts +0 -105
  18. package/src/cli.ts +0 -250
  19. package/src/config.ts +0 -502
  20. package/src/diagnostics.ts +0 -335
  21. package/src/index.ts +0 -98
  22. package/src/llm/anthropic.ts +0 -63
  23. package/src/llm/base.ts +0 -264
  24. package/src/llm/deepseek.ts +0 -48
  25. package/src/llm/google.ts +0 -63
  26. package/src/llm/groq.ts +0 -48
  27. package/src/llm/index.ts +0 -42
  28. package/src/llm/mistral.ts +0 -48
  29. package/src/llm/ollama.ts +0 -52
  30. package/src/llm/openai.ts +0 -94
  31. package/src/llm/together.ts +0 -48
  32. package/src/llm-providers.ts +0 -8
  33. package/src/logger.ts +0 -137
  34. package/src/paper/executor.ts +0 -201
  35. package/src/paper/index.ts +0 -1
  36. package/src/perp/client.ts +0 -200
  37. package/src/perp/index.ts +0 -12
  38. package/src/perp/msgpack.ts +0 -272
  39. package/src/perp/orders.ts +0 -234
  40. package/src/perp/positions.ts +0 -126
  41. package/src/perp/signer.ts +0 -277
  42. package/src/perp/types.ts +0 -192
  43. package/src/perp/websocket.ts +0 -274
  44. package/src/position-tracker.ts +0 -243
  45. package/src/prediction/client.ts +0 -288
  46. package/src/prediction/index.ts +0 -3
  47. package/src/prediction/order-manager.ts +0 -297
  48. package/src/prediction/types.ts +0 -151
  49. package/src/relay.ts +0 -254
  50. package/src/runtime.ts +0 -1755
  51. package/src/scrub-secrets.ts +0 -39
  52. package/src/setup.ts +0 -392
  53. package/src/signal.ts +0 -212
  54. package/src/spot/aerodrome.ts +0 -158
  55. package/src/spot/client.ts +0 -138
  56. package/src/spot/index.ts +0 -11
  57. package/src/spot/swap-manager.ts +0 -219
  58. package/src/spot/types.ts +0 -203
  59. package/src/spot/uniswap.ts +0 -150
  60. package/src/store.ts +0 -50
  61. package/src/strategy/index.ts +0 -2
  62. package/src/strategy/loader.ts +0 -265
  63. package/src/strategy/templates.ts +0 -74
  64. package/src/trading/index.ts +0 -2
  65. package/src/trading/market.ts +0 -120
  66. package/src/trading/risk.ts +0 -107
  67. package/src/ui.ts +0 -75
  68. package/test/strategy-loader.test.ts +0 -150
  69. package/tsconfig.json +0 -8
package/src/config.ts DELETED
@@ -1,502 +0,0 @@
1
- import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'node:crypto';
2
- import { chmodSync, existsSync, readFileSync, writeFileSync } from 'node:fs';
3
- import { homedir } from 'node:os';
4
- import { dirname, resolve } from 'node:path';
5
- import { z } from 'zod';
6
- import { LLM_PROVIDER_IDS, providerRequiresApiKey } from '@exagent/sdk';
7
- import type { LLMProvider } from '@exagent/sdk';
8
-
9
- export interface RuntimeConfig {
10
- agentId: string;
11
- apiUrl: string;
12
- apiToken: string;
13
- wallet?: {
14
- privateKey: string;
15
- };
16
- llm: {
17
- provider: LLMProvider;
18
- model?: string;
19
- apiKey?: string;
20
- endpoint?: string;
21
- temperature?: number;
22
- maxTokens?: number;
23
- };
24
- strategy: {
25
- file?: string;
26
- code?: string;
27
- template?: string;
28
- venues?: string[];
29
- prompt?: {
30
- name?: string;
31
- systemPrompt: string;
32
- venues?: string[];
33
- };
34
- };
35
- trading: {
36
- mode: 'live' | 'paper';
37
- timeHorizon: 'intraday' | 'swing' | 'position';
38
- maxPositionSizeBps: number;
39
- maxDailyLossBps: number;
40
- maxConcurrentPositions: number;
41
- tradingIntervalMs: number;
42
- maxSlippageBps: number;
43
- minTradeValueUSD: number;
44
- initialCapitalUSD?: number;
45
- };
46
- venues?: {
47
- hyperliquid_perp?: {
48
- enabled: boolean;
49
- apiUrl: string;
50
- wsUrl: string;
51
- maxLeverage: number;
52
- maxNotionalUSD: number;
53
- allowedInstruments?: string[];
54
- };
55
- polymarket?: {
56
- enabled: boolean;
57
- clobApiUrl: string;
58
- gammaApiUrl: string;
59
- maxNotionalUSD: number;
60
- maxTotalExposureUSD: number;
61
- allowedCategories?: string[];
62
- };
63
- spot?: {
64
- enabled: boolean;
65
- chains: string[];
66
- defaultChain: string;
67
- maxSlippageBps: number;
68
- maxSwapValueUSD: number;
69
- };
70
- bridge?: {
71
- enabled: boolean;
72
- defaultBridge: string;
73
- maxBridgeValueUSD: number;
74
- fillTimeoutMs: number;
75
- pollIntervalMs: number;
76
- };
77
- };
78
- relay: {
79
- url: string;
80
- heartbeatIntervalMs: number;
81
- reconnectMaxAttempts: number;
82
- };
83
- llmBudget?: {
84
- maxDailyTokens?: number;
85
- };
86
- rpcOverrides?: Record<string, string>;
87
- logging?: {
88
- level?: 'debug' | 'info' | 'warn' | 'error';
89
- json?: boolean;
90
- };
91
- }
92
-
93
- export interface LocalSecretPayload {
94
- apiToken: string;
95
- walletPrivateKey?: string;
96
- llmApiKey?: string;
97
- }
98
-
99
- export interface SecureStoreFile {
100
- version: 1;
101
- algorithm: 'aes-256-gcm';
102
- kdf: {
103
- name: 'scrypt';
104
- salt: string;
105
- keyLength: 32;
106
- cost: number;
107
- blockSize: number;
108
- parallelization: number;
109
- };
110
- iv: string;
111
- ciphertext: string;
112
- authTag: string;
113
- }
114
-
115
- export interface LoadConfigOptions {
116
- getSecretPassword?: () => Promise<string>;
117
- }
118
-
119
- const providerEnum = z.enum(LLM_PROVIDER_IDS);
120
-
121
- const runtimeSchema = z.object({
122
- agentId: z.string(),
123
- apiUrl: z.string().url(),
124
- apiToken: z.string().min(1),
125
- wallet: z.object({
126
- privateKey: z.string().regex(/^0x[a-fA-F0-9]{64}$/),
127
- }).optional(),
128
- llm: z.object({
129
- provider: providerEnum,
130
- model: z.string().optional(),
131
- apiKey: z.string().optional(),
132
- endpoint: z.string().optional(),
133
- temperature: z.number().min(0).max(2).optional(),
134
- maxTokens: z.number().optional(),
135
- }),
136
- strategy: z.object({
137
- file: z.string().optional(),
138
- code: z.string().optional(),
139
- template: z.string().optional(),
140
- venues: z.array(z.string()).optional(),
141
- prompt: z.object({
142
- name: z.string().optional(),
143
- systemPrompt: z.string().min(1),
144
- venues: z.array(z.string()).optional(),
145
- }).optional(),
146
- }),
147
- trading: z.object({
148
- mode: z.enum(['live', 'paper']).default('paper'),
149
- timeHorizon: z.enum(['intraday', 'swing', 'position']).default('swing'),
150
- maxPositionSizeBps: z.number().min(100).max(10000).default(2000),
151
- maxDailyLossBps: z.number().min(0).max(10000).default(500),
152
- maxConcurrentPositions: z.number().min(1).max(100).default(5),
153
- tradingIntervalMs: z.number().min(1000).default(60000),
154
- maxSlippageBps: z.number().min(10).max(1000).default(100),
155
- minTradeValueUSD: z.number().min(0).default(10),
156
- initialCapitalUSD: z.number().optional(),
157
- }),
158
- venues: z.object({
159
- hyperliquid_perp: z.object({
160
- enabled: z.boolean().default(false),
161
- apiUrl: z.string().default('https://api.hyperliquid.xyz'),
162
- wsUrl: z.string().default('wss://api.hyperliquid.xyz/ws'),
163
- maxLeverage: z.number().min(1).max(50).default(10),
164
- maxNotionalUSD: z.number().default(50_000),
165
- allowedInstruments: z.array(z.string()).optional(),
166
- }).optional(),
167
- polymarket: z.object({
168
- enabled: z.boolean().default(false),
169
- clobApiUrl: z.string().default('https://clob.polymarket.com'),
170
- gammaApiUrl: z.string().default('https://gamma-api.polymarket.com'),
171
- maxNotionalUSD: z.number().default(1_000),
172
- maxTotalExposureUSD: z.number().default(5_000),
173
- allowedCategories: z.array(z.string()).optional(),
174
- }).optional(),
175
- spot: z.object({
176
- enabled: z.boolean().default(false),
177
- chains: z.array(z.string()).default(['base']),
178
- defaultChain: z.string().default('base'),
179
- maxSlippageBps: z.number().min(1).max(1000).default(50),
180
- maxSwapValueUSD: z.number().default(10_000),
181
- }).optional(),
182
- bridge: z.object({
183
- enabled: z.boolean().default(false),
184
- defaultBridge: z.string().default('across'),
185
- maxBridgeValueUSD: z.number().default(10_000),
186
- fillTimeoutMs: z.number().default(300_000),
187
- pollIntervalMs: z.number().default(2_000),
188
- }).optional(),
189
- }).optional(),
190
- relay: z.object({
191
- url: z.string(),
192
- heartbeatIntervalMs: z.number().default(30000),
193
- reconnectMaxAttempts: z.number().default(50),
194
- }),
195
- llmBudget: z.object({
196
- maxDailyTokens: z.number().optional(),
197
- }).optional(),
198
- rpcOverrides: z.record(z.string()).optional(),
199
- logging: z.object({
200
- level: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
201
- json: z.boolean().default(true),
202
- }).optional(),
203
- });
204
-
205
- const configFileSchema = z.object({
206
- agentId: z.string(),
207
- apiUrl: z.string().url(),
208
- apiToken: z.string().min(1).optional(),
209
- wallet: z.object({
210
- privateKey: z.string().regex(/^0x[a-fA-F0-9]{64}$/),
211
- }).optional(),
212
- llm: z.object({
213
- provider: providerEnum.optional(),
214
- model: z.string().optional(),
215
- apiKey: z.string().optional(),
216
- endpoint: z.string().optional(),
217
- temperature: z.number().min(0).max(2).optional(),
218
- maxTokens: z.number().optional(),
219
- }).default({}),
220
- strategy: z.object({
221
- file: z.string().optional(),
222
- code: z.string().optional(),
223
- template: z.string().optional(),
224
- venues: z.array(z.string()).optional(),
225
- prompt: z.object({
226
- name: z.string().optional(),
227
- systemPrompt: z.string().min(1),
228
- venues: z.array(z.string()).optional(),
229
- }).optional(),
230
- }),
231
- trading: runtimeSchema.shape.trading,
232
- venues: runtimeSchema.shape.venues,
233
- relay: runtimeSchema.shape.relay,
234
- llmBudget: runtimeSchema.shape.llmBudget,
235
- rpcOverrides: runtimeSchema.shape.rpcOverrides,
236
- logging: runtimeSchema.shape.logging,
237
- secrets: z.object({
238
- bootstrapToken: z.string().optional(),
239
- bootstrapExpiresAt: z.string().optional(),
240
- secureStorePath: z.string().optional(),
241
- }).optional(),
242
- });
243
-
244
- const secureStoreSchema = z.object({
245
- version: z.literal(1),
246
- algorithm: z.literal('aes-256-gcm'),
247
- kdf: z.object({
248
- name: z.literal('scrypt'),
249
- salt: z.string(),
250
- keyLength: z.literal(32),
251
- cost: z.number().int().positive(),
252
- blockSize: z.number().int().positive(),
253
- parallelization: z.number().int().positive(),
254
- }),
255
- iv: z.string(),
256
- ciphertext: z.string(),
257
- authTag: z.string(),
258
- });
259
-
260
- export type RuntimeConfigFile = z.infer<typeof configFileSchema>;
261
-
262
- const DEFAULT_SCRYPT_COST = 16384;
263
- const DEFAULT_SCRYPT_BLOCK_SIZE = 8;
264
- const DEFAULT_SCRYPT_PARALLELIZATION = 1;
265
-
266
- function expandHomeDir(path: string): string {
267
- if (!path.startsWith('~/')) return path;
268
- return resolve(homedir(), path.slice(2));
269
- }
270
-
271
- export function getDefaultSecureStorePath(agentId: string): string {
272
- return resolve(homedir(), '.exagent', 'agents', agentId, 'secrets.json');
273
- }
274
-
275
- export function readConfigFile(path: string = 'agent-config.json'): RuntimeConfigFile {
276
- if (!existsSync(path)) {
277
- throw new Error(`Config file not found: ${path}. Run 'exagent init' first.`);
278
- }
279
-
280
- let parsed: unknown;
281
- try {
282
- parsed = JSON.parse(readFileSync(path, 'utf-8'));
283
- } catch {
284
- throw new Error(`Invalid JSON in ${path}`);
285
- }
286
-
287
- const result = configFileSchema.safeParse(parsed);
288
- if (!result.success) {
289
- const issues = result.error.issues.map((issue) => ` ${issue.path.join('.')}: ${issue.message}`).join('\n');
290
- throw new Error(`Invalid config file:\n${issues}`);
291
- }
292
-
293
- return result.data;
294
- }
295
-
296
- export function writeConfigFile(path: string, config: RuntimeConfigFile): void {
297
- writeFileSync(path, JSON.stringify(config, null, 2));
298
- }
299
-
300
- export function encryptSecretPayload(payload: LocalSecretPayload, password: string): SecureStoreFile {
301
- const salt = randomBytes(16);
302
- const iv = randomBytes(12);
303
- const key = scryptSync(password, salt, 32, {
304
- N: DEFAULT_SCRYPT_COST,
305
- r: DEFAULT_SCRYPT_BLOCK_SIZE,
306
- p: DEFAULT_SCRYPT_PARALLELIZATION,
307
- });
308
-
309
- const cipher = createCipheriv('aes-256-gcm', key, iv);
310
- const plaintext = Buffer.from(JSON.stringify(payload), 'utf8');
311
- const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
312
-
313
- return {
314
- version: 1,
315
- algorithm: 'aes-256-gcm',
316
- kdf: {
317
- name: 'scrypt',
318
- salt: salt.toString('hex'),
319
- keyLength: 32,
320
- cost: DEFAULT_SCRYPT_COST,
321
- blockSize: DEFAULT_SCRYPT_BLOCK_SIZE,
322
- parallelization: DEFAULT_SCRYPT_PARALLELIZATION,
323
- },
324
- iv: iv.toString('hex'),
325
- ciphertext: ciphertext.toString('hex'),
326
- authTag: cipher.getAuthTag().toString('hex'),
327
- };
328
- }
329
-
330
- export function decryptSecretPayload(path: string, password: string): LocalSecretPayload {
331
- const secureStorePath = expandHomeDir(path);
332
- if (!existsSync(secureStorePath)) {
333
- throw new Error(`Encrypted secret store not found: ${secureStorePath}`);
334
- }
335
-
336
- let parsed: unknown;
337
- try {
338
- parsed = JSON.parse(readFileSync(secureStorePath, 'utf-8'));
339
- } catch {
340
- throw new Error(`Invalid JSON in encrypted secret store: ${secureStorePath}`);
341
- }
342
-
343
- const result = secureStoreSchema.safeParse(parsed);
344
- if (!result.success) {
345
- const issues = result.error.issues.map((issue) => ` ${issue.path.join('.')}: ${issue.message}`).join('\n');
346
- throw new Error(`Invalid encrypted secret store:\n${issues}`);
347
- }
348
-
349
- const store = result.data;
350
- const key = scryptSync(password, Buffer.from(store.kdf.salt, 'hex'), store.kdf.keyLength, {
351
- N: store.kdf.cost,
352
- r: store.kdf.blockSize,
353
- p: store.kdf.parallelization,
354
- });
355
-
356
- try {
357
- const decipher = createDecipheriv(store.algorithm, key, Buffer.from(store.iv, 'hex'));
358
- decipher.setAuthTag(Buffer.from(store.authTag, 'hex'));
359
- const plaintext = Buffer.concat([
360
- decipher.update(Buffer.from(store.ciphertext, 'hex')),
361
- decipher.final(),
362
- ]);
363
- return JSON.parse(plaintext.toString('utf8')) as LocalSecretPayload;
364
- } catch {
365
- throw new Error('Unable to decrypt local secret store. Check your password.');
366
- }
367
- }
368
-
369
- export async function loadConfig(path: string = 'agent-config.json', options: LoadConfigOptions = {}): Promise<RuntimeConfig> {
370
- const parsed = readConfigFile(path);
371
- const config = structuredClone(parsed) as RuntimeConfigFile & Record<string, unknown>;
372
- const llm = { ...(config.llm || {}) } as Record<string, unknown>;
373
-
374
- if (process.env.EXAGENT_LLM_PROVIDER) llm.provider = process.env.EXAGENT_LLM_PROVIDER;
375
- if (process.env.EXAGENT_LLM_MODEL) llm.model = process.env.EXAGENT_LLM_MODEL;
376
- if (process.env.EXAGENT_LLM_API_KEY) llm.apiKey = process.env.EXAGENT_LLM_API_KEY;
377
- if (process.env.EXAGENT_API_URL) config.apiUrl = process.env.EXAGENT_API_URL;
378
- if (process.env.EXAGENT_API_TOKEN) config.apiToken = process.env.EXAGENT_API_TOKEN;
379
- if (process.env.EXAGENT_WALLET_PRIVATE_KEY) {
380
- config.wallet = { privateKey: process.env.EXAGENT_WALLET_PRIVATE_KEY };
381
- }
382
-
383
- const llmNeedsApiKey = providerRequiresApiKey(String(llm.provider || ''));
384
-
385
- if ((!config.apiToken || (llmNeedsApiKey && !llm.apiKey) || !config.wallet) && parsed.secrets?.secureStorePath) {
386
- const password = process.env.EXAGENT_SECRET_PASSWORD || await options.getSecretPassword?.();
387
- if (!password) {
388
- throw new Error('Encrypted secret store found, but no password was provided.');
389
- }
390
-
391
- const secrets = decryptSecretPayload(parsed.secrets.secureStorePath, password);
392
- if (!config.apiToken) config.apiToken = secrets.apiToken;
393
- if (!config.wallet && secrets.walletPrivateKey) config.wallet = { privateKey: secrets.walletPrivateKey };
394
- if (!llm.apiKey && secrets.llmApiKey) llm.apiKey = secrets.llmApiKey;
395
- }
396
-
397
- if ((!config.apiToken || (llmNeedsApiKey && !llm.apiKey) || !config.wallet) && parsed.secrets?.bootstrapToken && !parsed.secrets?.secureStorePath) {
398
- throw new Error(`Config ${path} still requires first-time secure setup. Run 'exagent setup --config ${path}' or start the agent interactively.`);
399
- }
400
-
401
- config.llm = llm;
402
-
403
- const rpcOverrides: Record<string, string> = (config.rpcOverrides as Record<string, string>) || {};
404
- if (process.env.EXAGENT_RPC_BASE) rpcOverrides.base = process.env.EXAGENT_RPC_BASE;
405
- if (process.env.EXAGENT_RPC_ARBITRUM) rpcOverrides.arbitrum = process.env.EXAGENT_RPC_ARBITRUM;
406
- if (process.env.EXAGENT_RPC_POLYGON) rpcOverrides.polygon = process.env.EXAGENT_RPC_POLYGON;
407
- if (process.env.EXAGENT_RPC_ETHEREUM) rpcOverrides.ethereum = process.env.EXAGENT_RPC_ETHEREUM;
408
- if (Object.keys(rpcOverrides).length > 0) {
409
- config.rpcOverrides = rpcOverrides;
410
- }
411
-
412
- const result = runtimeSchema.safeParse(config);
413
- if (!result.success) {
414
- const issues = result.error.issues.map((issue) => ` ${issue.path.join('.')}: ${issue.message}`).join('\n');
415
- throw new Error(`Invalid config:\n${issues}`);
416
- }
417
-
418
- return result.data;
419
- }
420
-
421
- export function updateSecureStore(
422
- path: string,
423
- password: string,
424
- updates: Partial<LocalSecretPayload>,
425
- ): void {
426
- const secrets = decryptSecretPayload(path, password);
427
- const updated = { ...secrets, ...updates };
428
- const encrypted = encryptSecretPayload(updated, password);
429
- const secureStorePath = expandHomeDir(path);
430
- writeFileSync(secureStorePath, JSON.stringify(encrypted, null, 2), { mode: 0o600 });
431
- try {
432
- chmodSync(secureStorePath, 0o600);
433
- } catch {
434
- // Best effort
435
- }
436
- }
437
-
438
- export function generateSampleConfig(agentId: string, apiUrl: string): string {
439
- const config: RuntimeConfigFile = {
440
- agentId,
441
- apiUrl,
442
- llm: {},
443
- strategy: {
444
- template: 'momentum',
445
- },
446
- trading: {
447
- mode: 'paper',
448
- timeHorizon: 'swing',
449
- maxPositionSizeBps: 2000,
450
- maxDailyLossBps: 500,
451
- maxConcurrentPositions: 5,
452
- tradingIntervalMs: 60000,
453
- maxSlippageBps: 100,
454
- minTradeValueUSD: 10,
455
- initialCapitalUSD: 10000,
456
- },
457
- venues: {
458
- hyperliquid_perp: {
459
- enabled: false,
460
- apiUrl: 'https://api.hyperliquid.xyz',
461
- wsUrl: 'wss://api.hyperliquid.xyz/ws',
462
- maxLeverage: 10,
463
- maxNotionalUSD: 50000,
464
- },
465
- polymarket: {
466
- enabled: false,
467
- clobApiUrl: 'https://clob.polymarket.com',
468
- gammaApiUrl: 'https://gamma-api.polymarket.com',
469
- maxNotionalUSD: 1000,
470
- maxTotalExposureUSD: 5000,
471
- },
472
- spot: {
473
- enabled: false,
474
- chains: ['base'],
475
- defaultChain: 'base',
476
- maxSlippageBps: 50,
477
- maxSwapValueUSD: 10000,
478
- },
479
- bridge: {
480
- enabled: false,
481
- defaultBridge: 'across',
482
- maxBridgeValueUSD: 10000,
483
- fillTimeoutMs: 300000,
484
- pollIntervalMs: 2000,
485
- },
486
- },
487
- relay: {
488
- url: apiUrl.replace(/^http/, 'ws') + '/ws/agent',
489
- heartbeatIntervalMs: 30000,
490
- reconnectMaxAttempts: 50,
491
- },
492
- secrets: {
493
- secureStorePath: getDefaultSecureStorePath(agentId),
494
- },
495
- };
496
-
497
- return JSON.stringify(config, null, 2);
498
- }
499
-
500
- export function writeSampleConfig(agentId: string, apiUrl: string, path: string = 'agent-config.json'): void {
501
- writeFileSync(path, generateSampleConfig(agentId, apiUrl));
502
- }