@trading-boy/cli 1.11.0 → 2.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.
Files changed (83) hide show
  1. package/README.md +50 -22
  2. package/dist/api-client.d.ts +4 -7
  3. package/dist/api-client.js +8 -13
  4. package/dist/cli.bundle.js +1977 -33976
  5. package/dist/credentials.js +1 -1
  6. package/dist/index.d.ts +0 -28
  7. package/dist/index.js +0 -24
  8. package/dist/logger.d.ts +8 -0
  9. package/dist/logger.js +12 -0
  10. package/dist/utils.js +3 -3
  11. package/package.json +20 -5
  12. package/dist/cli.d.ts +0 -5
  13. package/dist/cli.js +0 -157
  14. package/dist/commands/agent-cmd.d.ts +0 -9
  15. package/dist/commands/agent-cmd.js +0 -572
  16. package/dist/commands/audit.d.ts +0 -18
  17. package/dist/commands/audit.js +0 -73
  18. package/dist/commands/behavioral.d.ts +0 -73
  19. package/dist/commands/behavioral.js +0 -349
  20. package/dist/commands/benchmark-cmd.d.ts +0 -3
  21. package/dist/commands/benchmark-cmd.js +0 -191
  22. package/dist/commands/billing.d.ts +0 -12
  23. package/dist/commands/billing.js +0 -142
  24. package/dist/commands/catalysts.d.ts +0 -17
  25. package/dist/commands/catalysts.js +0 -151
  26. package/dist/commands/coaching-cmd.d.ts +0 -16
  27. package/dist/commands/coaching-cmd.js +0 -222
  28. package/dist/commands/config-cmd.d.ts +0 -30
  29. package/dist/commands/config-cmd.js +0 -515
  30. package/dist/commands/connect-chatgpt.d.ts +0 -5
  31. package/dist/commands/connect-chatgpt.js +0 -293
  32. package/dist/commands/connect-claude.d.ts +0 -5
  33. package/dist/commands/connect-claude.js +0 -280
  34. package/dist/commands/context.d.ts +0 -41
  35. package/dist/commands/context.js +0 -405
  36. package/dist/commands/cron-cmd.d.ts +0 -3
  37. package/dist/commands/cron-cmd.js +0 -305
  38. package/dist/commands/decisions.d.ts +0 -57
  39. package/dist/commands/decisions.js +0 -364
  40. package/dist/commands/edge-cmd.d.ts +0 -78
  41. package/dist/commands/edge-cmd.js +0 -183
  42. package/dist/commands/edge-guard-cmd.d.ts +0 -36
  43. package/dist/commands/edge-guard-cmd.js +0 -169
  44. package/dist/commands/events.d.ts +0 -3
  45. package/dist/commands/events.js +0 -117
  46. package/dist/commands/infra.d.ts +0 -24
  47. package/dist/commands/infra.js +0 -137
  48. package/dist/commands/journal.d.ts +0 -3
  49. package/dist/commands/journal.js +0 -302
  50. package/dist/commands/login.d.ts +0 -18
  51. package/dist/commands/login.js +0 -127
  52. package/dist/commands/logout.d.ts +0 -8
  53. package/dist/commands/logout.js +0 -108
  54. package/dist/commands/narratives.d.ts +0 -3
  55. package/dist/commands/narratives.js +0 -259
  56. package/dist/commands/onboarding.d.ts +0 -7
  57. package/dist/commands/onboarding.js +0 -298
  58. package/dist/commands/query.d.ts +0 -32
  59. package/dist/commands/query.js +0 -135
  60. package/dist/commands/replay-cmd.d.ts +0 -43
  61. package/dist/commands/replay-cmd.js +0 -184
  62. package/dist/commands/review.d.ts +0 -3
  63. package/dist/commands/review.js +0 -443
  64. package/dist/commands/risk.d.ts +0 -47
  65. package/dist/commands/risk.js +0 -158
  66. package/dist/commands/social.d.ts +0 -43
  67. package/dist/commands/social.js +0 -318
  68. package/dist/commands/soul-wizard.d.ts +0 -29
  69. package/dist/commands/soul-wizard.js +0 -155
  70. package/dist/commands/strategy-cmd.d.ts +0 -44
  71. package/dist/commands/strategy-cmd.js +0 -340
  72. package/dist/commands/subscribe.d.ts +0 -78
  73. package/dist/commands/subscribe.js +0 -552
  74. package/dist/commands/suggestions-cmd.d.ts +0 -24
  75. package/dist/commands/suggestions-cmd.js +0 -148
  76. package/dist/commands/thesis-cmd.d.ts +0 -3
  77. package/dist/commands/thesis-cmd.js +0 -129
  78. package/dist/commands/trader.d.ts +0 -30
  79. package/dist/commands/trader.js +0 -971
  80. package/dist/commands/watch.d.ts +0 -16
  81. package/dist/commands/watch.js +0 -104
  82. package/dist/commands/whoami.d.ts +0 -14
  83. package/dist/commands/whoami.js +0 -105
@@ -1,572 +0,0 @@
1
- // ─── Agent CLI Commands ───
2
- //
3
- // tb agent create — Create a new agent
4
- // tb agent list — List agents
5
- // tb agent show — Show agent details + live state
6
- // tb agent pause — Pause an agent
7
- // tb agent resume — Resume a paused agent
8
- // tb agent delete — Delete an agent
9
- // tb agent update — Update agent config
10
- import { readFileSync, existsSync } from 'node:fs';
11
- import { resolve } from 'node:path';
12
- import { Option } from 'commander';
13
- import chalk from 'chalk';
14
- import { createLogger } from '@trading-boy/core';
15
- import { apiRequest } from '../api-client.js';
16
- import { padRight, handleApiError, ensureRemote } from '../utils.js';
17
- const logger = createLogger('cli-agent');
18
- // ─── Formatters ───
19
- function formatShortDate(isoString) {
20
- if (!isoString)
21
- return chalk.dim('—');
22
- try {
23
- return new Date(isoString).toISOString().slice(0, 19).replace('T', ' ');
24
- }
25
- catch {
26
- return isoString;
27
- }
28
- }
29
- function formatStatus(status) {
30
- switch (status) {
31
- case 'active': return chalk.green('active');
32
- case 'paused': return chalk.yellow('paused');
33
- case 'deleted': return chalk.red('deleted');
34
- default: return status;
35
- }
36
- }
37
- function formatAutonomy(level) {
38
- switch (level) {
39
- case 'OBSERVE_ONLY': return chalk.dim('observe');
40
- case 'SUGGEST': return chalk.blue('suggest');
41
- case 'AUTO_WITH_APPROVAL': return chalk.yellow('auto+approve');
42
- case 'FULLY_AUTONOMOUS': return chalk.red('autonomous');
43
- default: return level;
44
- }
45
- }
46
- function formatInterval(ms) {
47
- if (ms < 60000)
48
- return `${(ms / 1000).toFixed(0)}s`;
49
- if (ms < 3600000)
50
- return `${(ms / 60000).toFixed(0)}m`;
51
- return `${(ms / 3600000).toFixed(1)}h`;
52
- }
53
- /**
54
- * Parse a human-readable interval string (e.g. "1m", "5m", "15m", "1h") to milliseconds.
55
- * Supported units: s (seconds), m (minutes), h (hours).
56
- * Returns null if the format is invalid.
57
- */
58
- export function parseHumanInterval(value) {
59
- const match = value.trim().match(/^(\d+(?:\.\d+)?)\s*(s|m|h)$/i);
60
- if (!match)
61
- return null;
62
- const num = parseFloat(match[1]);
63
- const unit = match[2].toLowerCase();
64
- switch (unit) {
65
- case 's': return num * 1000;
66
- case 'm': return num * 60_000;
67
- case 'h': return num * 3_600_000;
68
- default: return null;
69
- }
70
- }
71
- const MIN_SCAN_INTERVAL_MS = 60_000;
72
- // ─── Command Registration ───
73
- export function registerAgentCommand(program) {
74
- const agent = program
75
- .command('agent')
76
- .description('Manage autonomous trading agents');
77
- // ── create ──────────────────────────────────────────────────────────────────
78
- agent
79
- .command('create')
80
- .description('Create a new agent')
81
- .option('--trader-id <traderId>', 'Trader ID')
82
- .option('--strategy-id <strategyId>', 'Strategy ID')
83
- .option('--name <name>', 'Agent name')
84
- .option('--autonomy <level>', 'Autonomy level: OBSERVE_ONLY, SUGGEST, AUTO_WITH_APPROVAL, FULLY_AUTONOMOUS', 'OBSERVE_ONLY')
85
- .option('--scan-interval <ms>', 'Scan interval in ms (min 60000)', '300000')
86
- .option('--scan-interval-human <duration>', 'Scan interval in human-readable format (e.g. 1m, 5m, 15m, 30m, 1h)')
87
- .option('--watchlist <symbols>', 'Comma-separated token symbols')
88
- .option('--max-daily-trades <n>', 'Max daily trades', '10')
89
- .option('--max-daily-loss <usd>', 'Max daily loss in USD', '500')
90
- .option('--max-position-size <pct>', 'Max position size as decimal (0.10 = 10%)', '0.10')
91
- .option('--min-confidence <n>', 'Min confidence threshold (0-1)', '0.60')
92
- .option('--scan-model <model>', 'LLM model for market scanning')
93
- .option('--analyze-model <model>', 'LLM model for deep analysis')
94
- .option('--decide-model <model>', 'LLM model for trade decisions')
95
- .addOption(new Option('--asset-class <class>', 'Asset class for this agent').choices(['crypto', 'commodities', 'mixed']).default('crypto'))
96
- .option('--soul-override <text>', 'Custom soul/personality for this agent')
97
- .option('--purpose-override <text>', 'Custom purpose/mission for this agent')
98
- .option('--soul-file <path>', 'Load soul from a file')
99
- .option('--purpose-file <path>', 'Load purpose from a file')
100
- .option('--exit-reasoner', 'Enable LLM-powered exit reasoning for this agent')
101
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
102
- .action(async (options) => {
103
- if (!(await ensureRemote()))
104
- return;
105
- if (!options.traderId) {
106
- console.error(chalk.red('Error: --trader-id is required.'));
107
- console.log(chalk.dim(' Find yours with: trading-boy trader list'));
108
- process.exitCode = 1;
109
- return;
110
- }
111
- if (!options.strategyId) {
112
- console.error(chalk.red('Error: --strategy-id is required.'));
113
- console.log(chalk.dim(' Find yours with: trading-boy strategy list'));
114
- process.exitCode = 1;
115
- return;
116
- }
117
- const body = {
118
- traderId: options.traderId,
119
- strategyId: options.strategyId,
120
- };
121
- if (options.name)
122
- body.name = options.name;
123
- if (options.autonomy)
124
- body.autonomyLevel = options.autonomy;
125
- // Resolve scan interval: --scan-interval-human takes precedence
126
- if (options.scanIntervalHuman) {
127
- const ms = parseHumanInterval(options.scanIntervalHuman);
128
- if (ms === null) {
129
- console.error(chalk.red(`Error: Invalid duration "${options.scanIntervalHuman}". Use format like 1m, 5m, 15m, 30m, 1h.`));
130
- process.exitCode = 1;
131
- return;
132
- }
133
- if (ms < MIN_SCAN_INTERVAL_MS) {
134
- console.error(chalk.red(`Error: Scan interval must be at least 1m (60000ms). Got ${formatInterval(ms)}.`));
135
- process.exitCode = 1;
136
- return;
137
- }
138
- body.scanIntervalMs = ms;
139
- }
140
- else if (options.scanInterval) {
141
- body.scanIntervalMs = parseInt(options.scanInterval, 10);
142
- }
143
- if (options.watchlist)
144
- body.watchlist = options.watchlist.split(',').map((s) => s.trim().toUpperCase());
145
- if (options.maxDailyTrades)
146
- body.maxDailyTrades = parseInt(options.maxDailyTrades, 10);
147
- if (options.maxDailyLoss)
148
- body.maxDailyLossUsd = parseFloat(options.maxDailyLoss);
149
- if (options.maxPositionSize)
150
- body.maxPositionSizePct = parseFloat(options.maxPositionSize);
151
- if (options.minConfidence)
152
- body.minConfidence = parseFloat(options.minConfidence);
153
- if (options.scanModel)
154
- body.scanModel = options.scanModel;
155
- if (options.analyzeModel)
156
- body.analyzeModel = options.analyzeModel;
157
- if (options.decideModel)
158
- body.decideModel = options.decideModel;
159
- if (options.assetClass)
160
- body.assetClass = options.assetClass;
161
- if (options.exitReasoner)
162
- body.exitReasonerEnabled = true;
163
- // Soul override — file takes precedence over inline text
164
- if (options.soulFile) {
165
- const path = resolve(options.soulFile);
166
- if (!existsSync(path)) {
167
- console.error(chalk.red(`Error: Soul file not found: ${path}`));
168
- process.exitCode = 1;
169
- return;
170
- }
171
- body.soulOverride = readFileSync(path, 'utf-8');
172
- }
173
- else if (options.soulOverride) {
174
- body.soulOverride = options.soulOverride;
175
- }
176
- // Purpose override — file takes precedence over inline text
177
- if (options.purposeFile) {
178
- const path = resolve(options.purposeFile);
179
- if (!existsSync(path)) {
180
- console.error(chalk.red(`Error: Purpose file not found: ${path}`));
181
- process.exitCode = 1;
182
- return;
183
- }
184
- body.purposeOverride = readFileSync(path, 'utf-8');
185
- }
186
- else if (options.purposeOverride) {
187
- body.purposeOverride = options.purposeOverride;
188
- }
189
- try {
190
- const result = await apiRequest('/api/v1/agents', {
191
- method: 'POST',
192
- body,
193
- });
194
- if (options.format === 'json') {
195
- console.log(JSON.stringify(result, null, 2));
196
- }
197
- else {
198
- console.log('');
199
- console.log(chalk.green(' Agent created'));
200
- console.log(` ${chalk.gray('ID:')} ${result.id}`);
201
- console.log(` ${chalk.gray('Name:')} ${result.name}`);
202
- console.log(` ${chalk.gray('Trader:')} ${result.traderId}`);
203
- console.log(` ${chalk.gray('Strategy:')} ${result.strategyId}`);
204
- console.log(` ${chalk.gray('Autonomy:')} ${formatAutonomy(result.autonomyLevel)}`);
205
- console.log(` ${chalk.gray('Interval:')} ${formatInterval(result.scanIntervalMs)}`);
206
- console.log(` ${chalk.gray('Next scan:')} ${formatShortDate(result.nextScanAt)}`);
207
- console.log('');
208
- }
209
- }
210
- catch (error) {
211
- handleApiError(error, 'Agent create failed', logger);
212
- }
213
- });
214
- // ── list ────────────────────────────────────────────────────────────────────
215
- agent
216
- .command('list')
217
- .description('List agents')
218
- .option('--status <status>', 'Filter by status: active, paused')
219
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
220
- .action(async (options) => {
221
- if (!(await ensureRemote()))
222
- return;
223
- try {
224
- const query = options.status ? `?status=${options.status}` : '';
225
- const result = await apiRequest(`/api/v1/agents${query}`);
226
- if (options.format === 'json') {
227
- console.log(JSON.stringify(result, null, 2));
228
- return;
229
- }
230
- if (result.agents.length === 0) {
231
- console.log(chalk.dim(' No agents found.'));
232
- console.log('');
233
- console.log(chalk.dim(' Create one with:'));
234
- console.log(chalk.dim(' trading-boy agent create --trader-id <id> --strategy-id <id>'));
235
- console.log(chalk.dim(' Find your IDs with:'));
236
- console.log(chalk.dim(' trading-boy trader list'));
237
- console.log(chalk.dim(' trading-boy strategy list'));
238
- return;
239
- }
240
- console.log('');
241
- console.log(' ' +
242
- padRight('Name', 24) +
243
- padRight('Autonomy', 14) +
244
- padRight('Interval', 10) +
245
- padRight('Status', 10) +
246
- padRight('Ticks', 7) +
247
- padRight('Errors', 8) +
248
- 'Next Scan');
249
- console.log(chalk.gray(' ' + '─'.repeat(100)));
250
- for (const a of result.agents) {
251
- console.log(' ' +
252
- padRight(a.name.slice(0, 22), 24) +
253
- padRight(formatAutonomy(a.autonomyLevel), 14) +
254
- padRight(formatInterval(a.scanIntervalMs), 10) +
255
- padRight(formatStatus(a.status), 10) +
256
- padRight(String(a.tickCount), 7) +
257
- padRight(String(a.errorCount), 8) +
258
- formatShortDate(a.nextScanAt));
259
- }
260
- console.log('');
261
- console.log(chalk.dim(` ${result.count} agent(s)`));
262
- console.log('');
263
- }
264
- catch (error) {
265
- handleApiError(error, 'Agent list failed', logger);
266
- }
267
- });
268
- // ── show ────────────────────────────────────────────────────────────────────
269
- agent
270
- .command('show <agentId>')
271
- .description('Show agent details and live state')
272
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
273
- .action(async (agentId, options) => {
274
- if (!(await ensureRemote()))
275
- return;
276
- try {
277
- const result = await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}`);
278
- if (options.format === 'json') {
279
- console.log(JSON.stringify(result, null, 2));
280
- return;
281
- }
282
- const a = result.agent;
283
- const live = result.live;
284
- console.log('');
285
- console.log(chalk.bold.cyan(` Agent — ${a.name}`));
286
- console.log(chalk.gray(' ' + '─'.repeat(50)));
287
- console.log(` ${chalk.gray('ID:')} ${a.id}`);
288
- console.log(` ${chalk.gray('Status:')} ${formatStatus(a.status)}`);
289
- console.log(` ${chalk.gray('Trader:')} ${a.traderId}`);
290
- console.log(` ${chalk.gray('Strategy:')} ${a.strategyId}`);
291
- console.log(` ${chalk.gray('Autonomy:')} ${formatAutonomy(a.autonomyLevel)}`);
292
- if (a.assetClass)
293
- console.log(` ${chalk.gray('Asset class:')} ${a.assetClass}`);
294
- if (a.scanModel)
295
- console.log(` ${chalk.gray('Scan model:')} ${a.scanModel}`);
296
- if (a.analyzeModel)
297
- console.log(` ${chalk.gray('Analyze model:')} ${a.analyzeModel}`);
298
- if (a.decideModel)
299
- console.log(` ${chalk.gray('Decide model:')} ${a.decideModel}`);
300
- console.log(` ${chalk.gray('Scan interval:')} ${formatInterval(a.scanIntervalMs)}`);
301
- console.log(` ${chalk.gray('Max daily trades:')} ${a.maxDailyTrades}`);
302
- console.log(` ${chalk.gray('Max daily loss:')} $${a.maxDailyLossUsd}`);
303
- console.log(` ${chalk.gray('Max position:')} ${(a.maxPositionSizePct * 100).toFixed(0)}%`);
304
- console.log(` ${chalk.gray('Min confidence:')} ${(a.minConfidence * 100).toFixed(0)}%`);
305
- console.log(` ${chalk.gray('Watchlist:')} ${a.watchlist.length > 0 ? a.watchlist.join(', ') : chalk.dim('(from strategy)')}`);
306
- if (a.soulOverride) {
307
- const soulPreview = a.soulOverride.length > 60 ? a.soulOverride.slice(0, 60) + '...' : a.soulOverride;
308
- console.log(` ${chalk.gray('Soul override:')} ${chalk.white(soulPreview)}`);
309
- }
310
- if (a.purposeOverride) {
311
- const purposePreview = a.purposeOverride.length > 60 ? a.purposeOverride.slice(0, 60) + '...' : a.purposeOverride;
312
- console.log(` ${chalk.gray('Purpose override:')} ${chalk.white(purposePreview)}`);
313
- }
314
- console.log(` ${chalk.gray('Tick count:')} ${a.tickCount}`);
315
- console.log(` ${chalk.gray('Error count:')} ${a.errorCount}`);
316
- if (a.lastError) {
317
- console.log(` ${chalk.gray('Last error:')} ${chalk.red(a.lastError.slice(0, 60))}`);
318
- }
319
- console.log(` ${chalk.gray('Last tick:')} ${formatShortDate(a.lastTickAt)}`);
320
- console.log(` ${chalk.gray('Next scan:')} ${formatShortDate(a.nextScanAt)}`);
321
- console.log(` ${chalk.gray('Created:')} ${formatShortDate(a.createdAt)}`);
322
- // Live state
323
- if (live.state || live.admin) {
324
- console.log('');
325
- console.log(chalk.bold(' Live State'));
326
- console.log(chalk.gray(' ' + '─'.repeat(50)));
327
- if (live.admin) {
328
- console.log(` ${chalk.gray('Paused:')} ${live.admin.paused ? chalk.yellow('yes') : chalk.dim('no')}`);
329
- console.log(` ${chalk.gray('Killed:')} ${live.admin.killed ? chalk.red('yes') : chalk.dim('no')}`);
330
- console.log(` ${chalk.gray('Override:')} ${live.admin.override ?? chalk.dim('none')}`);
331
- if (live.admin.autonomyOverride) {
332
- console.log(` ${chalk.gray('Autonomy override:')} ${formatAutonomy(live.admin.autonomyOverride)}`);
333
- }
334
- }
335
- if (live.state) {
336
- const currentState = live.state.state;
337
- if (currentState) {
338
- console.log(` ${chalk.gray('Agent state:')} ${chalk.cyan(String(currentState))}`);
339
- }
340
- }
341
- }
342
- console.log('');
343
- }
344
- catch (error) {
345
- handleApiError(error, 'Agent show failed', logger);
346
- }
347
- });
348
- // ── pause ───────────────────────────────────────────────────────────────────
349
- agent
350
- .command('pause <agentId>')
351
- .description('Pause an agent')
352
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
353
- .action(async (agentId, options) => {
354
- if (!(await ensureRemote()))
355
- return;
356
- try {
357
- await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/pause`, {
358
- method: 'POST',
359
- });
360
- if (options.format === 'json') {
361
- console.log(JSON.stringify({ agentId, status: 'paused' }, null, 2));
362
- }
363
- else {
364
- console.log(chalk.green(` Agent ${agentId} paused`));
365
- }
366
- }
367
- catch (error) {
368
- handleApiError(error, 'Agent pause failed', logger);
369
- }
370
- });
371
- // ── resume ──────────────────────────────────────────────────────────────────
372
- agent
373
- .command('resume <agentId>')
374
- .description('Resume a paused agent')
375
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
376
- .action(async (agentId, options) => {
377
- if (!(await ensureRemote()))
378
- return;
379
- try {
380
- await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/resume`, {
381
- method: 'POST',
382
- });
383
- if (options.format === 'json') {
384
- console.log(JSON.stringify({ agentId, status: 'active' }, null, 2));
385
- }
386
- else {
387
- console.log(chalk.green(` Agent ${agentId} resumed`));
388
- }
389
- }
390
- catch (error) {
391
- handleApiError(error, 'Agent resume failed', logger);
392
- }
393
- });
394
- // ── delete ──────────────────────────────────────────────────────────────────
395
- agent
396
- .command('delete <agentId>')
397
- .description('Delete an agent')
398
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
399
- .action(async (agentId, options) => {
400
- if (!(await ensureRemote()))
401
- return;
402
- try {
403
- await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}`, {
404
- method: 'DELETE',
405
- });
406
- if (options.format === 'json') {
407
- console.log(JSON.stringify({ agentId, status: 'deleted' }, null, 2));
408
- }
409
- else {
410
- console.log(chalk.green(` Agent ${agentId} deleted`));
411
- }
412
- }
413
- catch (error) {
414
- handleApiError(error, 'Agent delete failed', logger);
415
- }
416
- });
417
- // ── exit ───────────────────────────────────────────────────────────────────
418
- agent
419
- .command('exit <agentId>')
420
- .description('Exit/close an open position for an agent')
421
- .requiredOption('--symbol <symbol>', 'Token symbol to exit (e.g. xyz:NATGAS)')
422
- .option('--reason <text>', 'Reason for exit')
423
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
424
- .action(async (agentId, options) => {
425
- if (!(await ensureRemote()))
426
- return;
427
- try {
428
- const body = {};
429
- if (options.reason)
430
- body.reason = options.reason;
431
- const result = await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/positions/${encodeURIComponent(options.symbol)}/exit`, { method: 'POST', body });
432
- if (options.format === 'json') {
433
- console.log(JSON.stringify(result, null, 2));
434
- }
435
- else {
436
- const pnlColor = result.pnl >= 0 ? chalk.green : chalk.red;
437
- const pnlSign = result.pnl >= 0 ? '+' : '';
438
- console.log('');
439
- console.log(chalk.green(' Position closed'));
440
- console.log(` ${chalk.gray('Symbol:')} ${result.symbol}`);
441
- console.log(` ${chalk.gray('Side:')} ${result.side}`);
442
- console.log(` ${chalk.gray('Exit price:')} $${result.exitPrice.toLocaleString()}`);
443
- console.log(` ${chalk.gray('PnL:')} ${pnlColor(`${pnlSign}$${result.pnl.toFixed(2)} (${pnlSign}${result.pnlPct.toFixed(2)}%)`)}`);
444
- console.log(` ${chalk.gray('Closed at:')} ${formatShortDate(result.closedAt)}`);
445
- if (options.reason) {
446
- console.log(` ${chalk.gray('Reason:')} ${options.reason}`);
447
- }
448
- console.log('');
449
- }
450
- }
451
- catch (error) {
452
- handleApiError(error, 'Position exit failed', logger);
453
- }
454
- });
455
- // ── update ──────────────────────────────────────────────────────────────────
456
- agent
457
- .command('update <agentId>')
458
- .description('Update agent config')
459
- .option('--name <name>', 'Agent name')
460
- .option('--autonomy <level>', 'Autonomy level')
461
- .option('--scan-interval <ms>', 'Scan interval in ms')
462
- .option('--scan-interval-human <duration>', 'Scan interval in human-readable format (e.g. 1m, 5m, 15m, 30m, 1h)')
463
- .option('--watchlist <symbols>', 'Comma-separated token symbols')
464
- .option('--max-daily-trades <n>', 'Max daily trades')
465
- .option('--max-daily-loss <usd>', 'Max daily loss in USD')
466
- .option('--max-position-size <pct>', 'Max position size as decimal')
467
- .option('--min-confidence <n>', 'Min confidence threshold')
468
- .option('--scan-model <model>', 'LLM model for market scanning')
469
- .option('--analyze-model <model>', 'LLM model for deep analysis')
470
- .option('--decide-model <model>', 'LLM model for trade decisions')
471
- .addOption(new Option('--asset-class <class>', 'Asset class for this agent').choices(['crypto', 'commodities', 'mixed']))
472
- .option('--soul-override <text>', 'Custom soul/personality for this agent')
473
- .option('--purpose-override <text>', 'Custom purpose/mission for this agent')
474
- .option('--soul-file <path>', 'Load soul from a file')
475
- .option('--purpose-file <path>', 'Load purpose from a file')
476
- .option('--exit-reasoner', 'Enable LLM-powered exit reasoning for this agent')
477
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
478
- .action(async (agentId, options) => {
479
- if (!(await ensureRemote()))
480
- return;
481
- const body = {};
482
- if (options.name)
483
- body.name = options.name;
484
- if (options.exitReasoner)
485
- body.exitReasonerEnabled = true;
486
- if (options.autonomy)
487
- body.autonomyLevel = options.autonomy;
488
- // Resolve scan interval: --scan-interval-human takes precedence
489
- if (options.scanIntervalHuman) {
490
- const ms = parseHumanInterval(options.scanIntervalHuman);
491
- if (ms === null) {
492
- console.error(chalk.red(`Error: Invalid duration "${options.scanIntervalHuman}". Use format like 1m, 5m, 15m, 30m, 1h.`));
493
- process.exitCode = 1;
494
- return;
495
- }
496
- if (ms < MIN_SCAN_INTERVAL_MS) {
497
- console.error(chalk.red(`Error: Scan interval must be at least 1m (60000ms). Got ${formatInterval(ms)}.`));
498
- process.exitCode = 1;
499
- return;
500
- }
501
- body.scanIntervalMs = ms;
502
- }
503
- else if (options.scanInterval) {
504
- body.scanIntervalMs = parseInt(options.scanInterval, 10);
505
- }
506
- if (options.watchlist)
507
- body.watchlist = options.watchlist.split(',').map((s) => s.trim().toUpperCase());
508
- if (options.maxDailyTrades)
509
- body.maxDailyTrades = parseInt(options.maxDailyTrades, 10);
510
- if (options.maxDailyLoss)
511
- body.maxDailyLossUsd = parseFloat(options.maxDailyLoss);
512
- if (options.maxPositionSize)
513
- body.maxPositionSizePct = parseFloat(options.maxPositionSize);
514
- if (options.minConfidence)
515
- body.minConfidence = parseFloat(options.minConfidence);
516
- if (options.scanModel)
517
- body.scanModel = options.scanModel;
518
- if (options.analyzeModel)
519
- body.analyzeModel = options.analyzeModel;
520
- if (options.decideModel)
521
- body.decideModel = options.decideModel;
522
- if (options.assetClass)
523
- body.assetClass = options.assetClass;
524
- // Soul override — file takes precedence over inline text
525
- if (options.soulFile) {
526
- const path = resolve(options.soulFile);
527
- if (!existsSync(path)) {
528
- console.error(chalk.red(`Error: Soul file not found: ${path}`));
529
- process.exitCode = 1;
530
- return;
531
- }
532
- body.soulOverride = readFileSync(path, 'utf-8');
533
- }
534
- else if (options.soulOverride) {
535
- body.soulOverride = options.soulOverride;
536
- }
537
- // Purpose override — file takes precedence over inline text
538
- if (options.purposeFile) {
539
- const path = resolve(options.purposeFile);
540
- if (!existsSync(path)) {
541
- console.error(chalk.red(`Error: Purpose file not found: ${path}`));
542
- process.exitCode = 1;
543
- return;
544
- }
545
- body.purposeOverride = readFileSync(path, 'utf-8');
546
- }
547
- else if (options.purposeOverride) {
548
- body.purposeOverride = options.purposeOverride;
549
- }
550
- if (Object.keys(body).length === 0) {
551
- console.error(chalk.yellow(' No updates specified. Use --name, --autonomy, --watchlist, etc.'));
552
- process.exitCode = 1;
553
- return;
554
- }
555
- try {
556
- const result = await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}`, {
557
- method: 'PATCH',
558
- body,
559
- });
560
- if (options.format === 'json') {
561
- console.log(JSON.stringify(result, null, 2));
562
- }
563
- else {
564
- console.log(chalk.green(` Agent ${agentId} updated`));
565
- }
566
- }
567
- catch (error) {
568
- handleApiError(error, 'Agent update failed', logger);
569
- }
570
- });
571
- }
572
- //# sourceMappingURL=agent-cmd.js.map
@@ -1,18 +0,0 @@
1
- import { Command } from 'commander';
2
- export interface AuditVerifyResult {
3
- valid: boolean;
4
- chainLength: number;
5
- verifiedCount: number;
6
- breakPoint?: {
7
- index: number;
8
- decisionId: string;
9
- decisionType: string;
10
- eventTime: string;
11
- };
12
- }
13
- /**
14
- * Format audit verification output with chalk styling.
15
- */
16
- export declare function formatAuditVerifyOutput(result: AuditVerifyResult, elapsedMs: number): string;
17
- export declare function registerAuditCommand(program: Command): void;
18
- //# sourceMappingURL=audit.d.ts.map
@@ -1,73 +0,0 @@
1
- import { Option } from 'commander';
2
- import chalk from 'chalk';
3
- import { createLogger } from '@trading-boy/core';
4
- import { formatConnectionError } from '../utils.js';
5
- import { apiRequest, ApiError } from '../api-client.js';
6
- // ─── Logger ───
7
- const logger = createLogger('cli-audit');
8
- // ─── Default Trader ───
9
- const DEFAULT_TRADER_ID = 'default';
10
- // ─── Formatter ───
11
- /**
12
- * Format audit verification output with chalk styling.
13
- */
14
- export function formatAuditVerifyOutput(result, elapsedMs) {
15
- const lines = [];
16
- lines.push('');
17
- lines.push(chalk.bold.cyan(' Audit Verification'));
18
- lines.push(chalk.gray(' ' + '\u2500'.repeat(50)));
19
- lines.push('');
20
- if (result.valid) {
21
- lines.push(` ${chalk.green('\u2713')} ${chalk.green('Chain integrity verified')}`);
22
- lines.push(` ${chalk.gray('Decisions verified:')} ${chalk.white(String(result.verifiedCount))}`);
23
- lines.push(` ${chalk.gray('Chain length:')} ${chalk.white(String(result.chainLength))}`);
24
- }
25
- else {
26
- lines.push(` ${chalk.red('\u2717')} ${chalk.red(`Chain integrity BROKEN at decision ${result.breakPoint.index}`)}`);
27
- lines.push(` ${chalk.gray('Decision ID:')} ${chalk.white(result.breakPoint.decisionId)}`);
28
- lines.push(` ${chalk.gray('Type:')} ${chalk.white(result.breakPoint.decisionType)}`);
29
- lines.push(` ${chalk.gray('Event time:')} ${chalk.white(result.breakPoint.eventTime)}`);
30
- lines.push(` ${chalk.gray('Verified:')} ${chalk.yellow(`${result.verifiedCount} of ${result.chainLength}`)} decisions`);
31
- }
32
- lines.push(` ${chalk.dim(`Elapsed: ${elapsedMs.toFixed(2)}ms`)}`);
33
- lines.push('');
34
- return lines.join('\n');
35
- }
36
- // ─── Command Registration ───
37
- export function registerAuditCommand(program) {
38
- const audit = program
39
- .command('audit')
40
- .description('Audit and integrity verification commands');
41
- // ─── audit verify ───
42
- audit
43
- .command('verify')
44
- .description('Verify the integrity of the decision hash chain')
45
- .option('--trader <traderId>', 'Trader ID', DEFAULT_TRADER_ID)
46
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
47
- .action(async (options) => {
48
- try {
49
- const startTime = performance.now();
50
- const result = await apiRequest(`/api/v1/audit/verify?traderId=${encodeURIComponent(options.trader)}`);
51
- const elapsed = performance.now() - startTime;
52
- if (options.format === 'json') {
53
- console.log(JSON.stringify({ ...result, elapsedMs: Number(elapsed.toFixed(2)) }, null, 2));
54
- }
55
- else {
56
- console.log(formatAuditVerifyOutput(result, elapsed));
57
- }
58
- logger.debug({ elapsed: elapsed.toFixed(2) + 'ms' }, 'Chain verification complete');
59
- }
60
- catch (error) {
61
- const message = error instanceof Error ? error.message : String(error);
62
- logger.error({ error: message }, 'Failed to verify chain');
63
- console.error(`Error: ${message}`);
64
- if (!(error instanceof ApiError)) {
65
- const guidance = formatConnectionError(message);
66
- if (guidance)
67
- console.error(guidance);
68
- }
69
- process.exitCode = error instanceof ApiError ? 2 : 1;
70
- }
71
- });
72
- }
73
- //# sourceMappingURL=audit.js.map