@exagent/agent 0.3.6 → 0.3.7

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 (68) 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-IVA2SCSN.js +6756 -0
  5. package/dist/chunk-JHXCSGPC.js +6352 -0
  6. package/dist/chunk-V6O4UXVN.js +6345 -0
  7. package/dist/chunk-WTECTX2Z.js +6345 -0
  8. package/dist/cli.js +2 -2
  9. package/dist/index.d.ts +24 -2
  10. package/dist/index.js +1 -1
  11. package/package.json +12 -9
  12. package/src/bridge/across.ts +0 -240
  13. package/src/bridge/bridge-manager.ts +0 -87
  14. package/src/bridge/index.ts +0 -9
  15. package/src/bridge/types.ts +0 -77
  16. package/src/chains.ts +0 -105
  17. package/src/cli.ts +0 -250
  18. package/src/config.ts +0 -502
  19. package/src/diagnostics.ts +0 -335
  20. package/src/index.ts +0 -98
  21. package/src/llm/anthropic.ts +0 -63
  22. package/src/llm/base.ts +0 -264
  23. package/src/llm/deepseek.ts +0 -48
  24. package/src/llm/google.ts +0 -63
  25. package/src/llm/groq.ts +0 -48
  26. package/src/llm/index.ts +0 -42
  27. package/src/llm/mistral.ts +0 -48
  28. package/src/llm/ollama.ts +0 -52
  29. package/src/llm/openai.ts +0 -94
  30. package/src/llm/together.ts +0 -48
  31. package/src/llm-providers.ts +0 -8
  32. package/src/logger.ts +0 -137
  33. package/src/paper/executor.ts +0 -201
  34. package/src/paper/index.ts +0 -1
  35. package/src/perp/client.ts +0 -200
  36. package/src/perp/index.ts +0 -12
  37. package/src/perp/msgpack.ts +0 -272
  38. package/src/perp/orders.ts +0 -234
  39. package/src/perp/positions.ts +0 -126
  40. package/src/perp/signer.ts +0 -277
  41. package/src/perp/types.ts +0 -192
  42. package/src/perp/websocket.ts +0 -274
  43. package/src/position-tracker.ts +0 -243
  44. package/src/prediction/client.ts +0 -288
  45. package/src/prediction/index.ts +0 -3
  46. package/src/prediction/order-manager.ts +0 -297
  47. package/src/prediction/types.ts +0 -151
  48. package/src/relay.ts +0 -254
  49. package/src/runtime.ts +0 -1755
  50. package/src/scrub-secrets.ts +0 -39
  51. package/src/setup.ts +0 -392
  52. package/src/signal.ts +0 -212
  53. package/src/spot/aerodrome.ts +0 -158
  54. package/src/spot/client.ts +0 -138
  55. package/src/spot/index.ts +0 -11
  56. package/src/spot/swap-manager.ts +0 -219
  57. package/src/spot/types.ts +0 -203
  58. package/src/spot/uniswap.ts +0 -150
  59. package/src/store.ts +0 -50
  60. package/src/strategy/index.ts +0 -2
  61. package/src/strategy/loader.ts +0 -265
  62. package/src/strategy/templates.ts +0 -74
  63. package/src/trading/index.ts +0 -2
  64. package/src/trading/market.ts +0 -120
  65. package/src/trading/risk.ts +0 -107
  66. package/src/ui.ts +0 -75
  67. package/test/strategy-loader.test.ts +0 -150
  68. 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
- }