@trading-boy/cli 1.12.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.
- package/README.md +50 -22
- package/dist/api-client.d.ts +4 -7
- package/dist/api-client.js +8 -13
- package/dist/cli.bundle.js +1896 -33657
- package/dist/credentials.js +1 -1
- package/dist/index.d.ts +0 -28
- package/dist/index.js +0 -24
- package/dist/logger.d.ts +8 -0
- package/dist/logger.js +12 -0
- package/dist/utils.js +3 -3
- package/package.json +20 -5
- package/dist/cli.d.ts +0 -5
- package/dist/cli.js +0 -157
- package/dist/commands/agent-cmd.d.ts +0 -9
- package/dist/commands/agent-cmd.js +0 -567
- package/dist/commands/audit.d.ts +0 -18
- package/dist/commands/audit.js +0 -73
- package/dist/commands/behavioral.d.ts +0 -73
- package/dist/commands/behavioral.js +0 -349
- package/dist/commands/benchmark-cmd.d.ts +0 -3
- package/dist/commands/benchmark-cmd.js +0 -191
- package/dist/commands/billing.d.ts +0 -12
- package/dist/commands/billing.js +0 -142
- package/dist/commands/catalysts.d.ts +0 -17
- package/dist/commands/catalysts.js +0 -151
- package/dist/commands/coaching-cmd.d.ts +0 -16
- package/dist/commands/coaching-cmd.js +0 -222
- package/dist/commands/config-cmd.d.ts +0 -30
- package/dist/commands/config-cmd.js +0 -515
- package/dist/commands/connect-chatgpt.d.ts +0 -5
- package/dist/commands/connect-chatgpt.js +0 -293
- package/dist/commands/connect-claude.d.ts +0 -5
- package/dist/commands/connect-claude.js +0 -280
- package/dist/commands/context.d.ts +0 -41
- package/dist/commands/context.js +0 -405
- package/dist/commands/cron-cmd.d.ts +0 -3
- package/dist/commands/cron-cmd.js +0 -305
- package/dist/commands/decisions.d.ts +0 -57
- package/dist/commands/decisions.js +0 -364
- package/dist/commands/edge-cmd.d.ts +0 -78
- package/dist/commands/edge-cmd.js +0 -183
- package/dist/commands/edge-guard-cmd.d.ts +0 -36
- package/dist/commands/edge-guard-cmd.js +0 -169
- package/dist/commands/events.d.ts +0 -3
- package/dist/commands/events.js +0 -117
- package/dist/commands/infra.d.ts +0 -24
- package/dist/commands/infra.js +0 -137
- package/dist/commands/journal.d.ts +0 -3
- package/dist/commands/journal.js +0 -302
- package/dist/commands/login.d.ts +0 -18
- package/dist/commands/login.js +0 -127
- package/dist/commands/logout.d.ts +0 -8
- package/dist/commands/logout.js +0 -108
- package/dist/commands/narratives.d.ts +0 -3
- package/dist/commands/narratives.js +0 -259
- package/dist/commands/onboarding.d.ts +0 -7
- package/dist/commands/onboarding.js +0 -281
- package/dist/commands/query.d.ts +0 -32
- package/dist/commands/query.js +0 -135
- package/dist/commands/replay-cmd.d.ts +0 -43
- package/dist/commands/replay-cmd.js +0 -184
- package/dist/commands/review.d.ts +0 -3
- package/dist/commands/review.js +0 -443
- package/dist/commands/risk.d.ts +0 -47
- package/dist/commands/risk.js +0 -158
- package/dist/commands/social.d.ts +0 -43
- package/dist/commands/social.js +0 -318
- package/dist/commands/soul-wizard.d.ts +0 -29
- package/dist/commands/soul-wizard.js +0 -155
- package/dist/commands/strategy-cmd.d.ts +0 -44
- package/dist/commands/strategy-cmd.js +0 -335
- package/dist/commands/subscribe.d.ts +0 -78
- package/dist/commands/subscribe.js +0 -552
- package/dist/commands/suggestions-cmd.d.ts +0 -24
- package/dist/commands/suggestions-cmd.js +0 -148
- package/dist/commands/thesis-cmd.d.ts +0 -3
- package/dist/commands/thesis-cmd.js +0 -129
- package/dist/commands/trader.d.ts +0 -30
- package/dist/commands/trader.js +0 -971
- package/dist/commands/watch.d.ts +0 -16
- package/dist/commands/watch.js +0 -104
- package/dist/commands/whoami.d.ts +0 -14
- 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
|
package/dist/commands/audit.d.ts
DELETED
|
@@ -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
|
package/dist/commands/audit.js
DELETED
|
@@ -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
|