@trading-boy/cli 1.12.0 → 2.0.1

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