@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
package/dist/commands/trader.js
DELETED
|
@@ -1,971 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { Option } from 'commander';
|
|
3
|
-
import chalk from 'chalk';
|
|
4
|
-
import { createLogger } from '@trading-boy/core';
|
|
5
|
-
import { padRight } from '../utils.js';
|
|
6
|
-
import { apiRequest, ApiError } from '../api-client.js';
|
|
7
|
-
import { confirm } from '@inquirer/prompts';
|
|
8
|
-
// ─── Logger ───
|
|
9
|
-
const logger = createLogger('cli-trader');
|
|
10
|
-
// ─── Formatters ───
|
|
11
|
-
/**
|
|
12
|
-
* Format a single trader record for display.
|
|
13
|
-
*/
|
|
14
|
-
export function formatTraderOutput(trader, wallets) {
|
|
15
|
-
const lines = [];
|
|
16
|
-
lines.push('');
|
|
17
|
-
lines.push(chalk.bold.cyan(' Trader Profile'));
|
|
18
|
-
lines.push(chalk.gray(' ' + '\u2500'.repeat(50)));
|
|
19
|
-
lines.push('');
|
|
20
|
-
lines.push(` ${chalk.gray('Name:')} ${chalk.white(trader.name)}`);
|
|
21
|
-
lines.push(` ${chalk.gray('ID:')} ${chalk.dim(trader.id)}`);
|
|
22
|
-
lines.push(` ${chalk.gray('Max Drawdown:')} ${chalk.yellow(String(trader.riskToleranceMax ?? trader.maxDrawdownPct ?? 'n/a') + '%')}`);
|
|
23
|
-
const walletAddrs = trader.walletAddresses ?? [];
|
|
24
|
-
lines.push(` ${chalk.gray('Wallets:')} ${chalk.white(String(walletAddrs.length))}`);
|
|
25
|
-
if (walletAddrs.length > 0) {
|
|
26
|
-
for (const addr of walletAddrs) {
|
|
27
|
-
lines.push(` ${chalk.dim('\u2022')} ${chalk.white(addr)}`);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
if (wallets && wallets.length > 0) {
|
|
31
|
-
lines.push('');
|
|
32
|
-
lines.push(` ${chalk.gray('Wallet Details:')}`);
|
|
33
|
-
for (const w of wallets) {
|
|
34
|
-
lines.push(` ${chalk.dim('\u2022')} ${chalk.white(w.address)} ${chalk.dim(`(${w.chain})`)} ${chalk.dim(`since ${w.since}`)}`);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
lines.push('');
|
|
38
|
-
lines.push(` ${chalk.gray('Created:')} ${chalk.dim(trader.createdAt)}`);
|
|
39
|
-
lines.push(` ${chalk.gray('Updated:')} ${chalk.dim(trader.updatedAt)}`);
|
|
40
|
-
lines.push('');
|
|
41
|
-
return lines.join('\n');
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Format a list of traders for display.
|
|
45
|
-
*/
|
|
46
|
-
export function formatTraderListOutput(traders) {
|
|
47
|
-
const lines = [];
|
|
48
|
-
lines.push('');
|
|
49
|
-
lines.push(chalk.bold.cyan(' Registered Traders'));
|
|
50
|
-
lines.push(chalk.gray(' ' + '\u2500'.repeat(70)));
|
|
51
|
-
lines.push('');
|
|
52
|
-
if (traders.length === 0) {
|
|
53
|
-
lines.push(` ${chalk.dim('No traders registered.')}`);
|
|
54
|
-
lines.push('');
|
|
55
|
-
lines.push(` ${chalk.dim('Run')} ${chalk.white('trading-boy trader register --name "YourName"')} ${chalk.dim('to get started.')}`);
|
|
56
|
-
lines.push('');
|
|
57
|
-
return lines.join('\n');
|
|
58
|
-
}
|
|
59
|
-
// Header
|
|
60
|
-
lines.push(' ' +
|
|
61
|
-
padRight('Name', 20) +
|
|
62
|
-
padRight('Max DD', 10) +
|
|
63
|
-
padRight('Wallets', 10) +
|
|
64
|
-
'ID');
|
|
65
|
-
lines.push(' ' + '\u2500'.repeat(70));
|
|
66
|
-
for (const t of traders) {
|
|
67
|
-
lines.push(' ' +
|
|
68
|
-
padRight(String(t.name ?? t.id ?? ''), 20) +
|
|
69
|
-
padRight(String(t.riskToleranceMax ?? 0) + '%', 10) +
|
|
70
|
-
padRight(String(t.walletAddresses?.length ?? 0), 10) +
|
|
71
|
-
chalk.dim(t.id));
|
|
72
|
-
}
|
|
73
|
-
lines.push('');
|
|
74
|
-
lines.push(` ${chalk.gray(`Total: ${traders.length} trader(s)`)}`);
|
|
75
|
-
lines.push('');
|
|
76
|
-
return lines.join('\n');
|
|
77
|
-
}
|
|
78
|
-
// ─── Command Registration ───
|
|
79
|
-
export function registerTraderCommand(program) {
|
|
80
|
-
const trader = program
|
|
81
|
-
.command('trader')
|
|
82
|
-
.description('Manage trader profiles');
|
|
83
|
-
// ─── register ───
|
|
84
|
-
trader
|
|
85
|
-
.command('register')
|
|
86
|
-
.description('Register a new trader profile')
|
|
87
|
-
.requiredOption('--name <name>', 'Trader display name')
|
|
88
|
-
.option('--wallet <address>', 'Initial Solana wallet address')
|
|
89
|
-
.option('--max-drawdown <pct>', 'Max acceptable drawdown percentage', parseFloatOption)
|
|
90
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
91
|
-
.action(async (options) => {
|
|
92
|
-
try {
|
|
93
|
-
const result = await apiRequest('/api/v1/traders', {
|
|
94
|
-
method: 'POST',
|
|
95
|
-
body: {
|
|
96
|
-
name: options.name,
|
|
97
|
-
wallet: options.wallet,
|
|
98
|
-
maxDrawdown: options.maxDrawdown,
|
|
99
|
-
},
|
|
100
|
-
});
|
|
101
|
-
const traderId = result.name ?? result.id;
|
|
102
|
-
if (options.format === 'json') {
|
|
103
|
-
console.log(JSON.stringify({
|
|
104
|
-
...result,
|
|
105
|
-
nextSteps: [
|
|
106
|
-
`trading-boy trader soul ${traderId} --file soul.md`,
|
|
107
|
-
`trading-boy trader purpose ${traderId} --file purpose.md`,
|
|
108
|
-
`trading-boy trader identity ${traderId}`,
|
|
109
|
-
],
|
|
110
|
-
}, null, 2));
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
console.log(formatTraderOutput(result));
|
|
114
|
-
console.log(chalk.green(' Trader registered successfully.'));
|
|
115
|
-
if (result.alias) {
|
|
116
|
-
console.log(` ${chalk.gray('Leaderboard alias:')} ${chalk.white(result.alias)} ${chalk.dim('(change with: trading-boy trader set-alias <name> <alias>)')}`);
|
|
117
|
-
}
|
|
118
|
-
console.log('');
|
|
119
|
-
console.log(chalk.bold(' Next: Set up your trading identity'));
|
|
120
|
-
console.log(chalk.dim(' SOUL and PURPOSE documents personalize your context — bias warnings,'));
|
|
121
|
-
console.log(chalk.dim(' scope checks, and tailored synthesis based on who you are as a trader.'));
|
|
122
|
-
console.log('');
|
|
123
|
-
console.log(` ${chalk.gray('1.')} trading-boy trader soul ${chalk.white(traderId)} --file soul.md`);
|
|
124
|
-
console.log(` ${chalk.gray('2.')} trading-boy trader purpose ${chalk.white(traderId)} --file purpose.md`);
|
|
125
|
-
console.log(` ${chalk.gray('3.')} trading-boy trader identity ${chalk.white(traderId)}`);
|
|
126
|
-
console.log('');
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
|
|
131
|
-
logger.error({ error: message }, 'Failed to register trader');
|
|
132
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
133
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
// ─── show ───
|
|
137
|
-
trader
|
|
138
|
-
.command('show')
|
|
139
|
-
.description('Show a trader profile by name or ID')
|
|
140
|
-
.argument('<name-or-id>', 'Trader name or ID')
|
|
141
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
142
|
-
.action(async (nameOrId, options) => {
|
|
143
|
-
try {
|
|
144
|
-
const result = await apiRequest(`/api/v1/traders/${encodeURIComponent(nameOrId)}`);
|
|
145
|
-
if (options.format === 'json') {
|
|
146
|
-
console.log(JSON.stringify(result, null, 2));
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
-
const { wallets, ...traderData } = result;
|
|
150
|
-
console.log(formatTraderOutput(traderData, wallets));
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
catch (error) {
|
|
154
|
-
if (error instanceof ApiError && error.status === 404) {
|
|
155
|
-
console.error(chalk.yellow(`Trader not found: "${nameOrId}"`));
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
159
|
-
logger.error({ error: message }, 'Failed to show trader');
|
|
160
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
161
|
-
}
|
|
162
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
// ─── list ───
|
|
166
|
-
trader
|
|
167
|
-
.command('list')
|
|
168
|
-
.description('List all registered traders')
|
|
169
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
170
|
-
.action(async (options) => {
|
|
171
|
-
try {
|
|
172
|
-
const result = await apiRequest('/api/v1/traders');
|
|
173
|
-
if (options.format === 'json') {
|
|
174
|
-
console.log(JSON.stringify(result, null, 2));
|
|
175
|
-
}
|
|
176
|
-
else {
|
|
177
|
-
console.log(formatTraderListOutput(result.traders));
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
catch (error) {
|
|
181
|
-
const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
|
|
182
|
-
logger.error({ error: message }, 'Failed to list traders');
|
|
183
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
184
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
// ─── update ───
|
|
188
|
-
trader
|
|
189
|
-
.command('update')
|
|
190
|
-
.description('Update a trader profile')
|
|
191
|
-
.argument('<name-or-id>', 'Trader name or ID')
|
|
192
|
-
.option('--name <new-name>', 'New display name')
|
|
193
|
-
.option('--max-drawdown <pct>', 'New max acceptable drawdown percentage', parseFloatOption)
|
|
194
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
195
|
-
.action(async (nameOrId, options) => {
|
|
196
|
-
if (options.name === undefined && options.maxDrawdown === undefined) {
|
|
197
|
-
console.error(chalk.yellow('No updates provided. Use --name or --max-drawdown.'));
|
|
198
|
-
process.exitCode = 1;
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
try {
|
|
202
|
-
const result = await apiRequest(`/api/v1/traders/${encodeURIComponent(nameOrId)}`, {
|
|
203
|
-
method: 'PATCH',
|
|
204
|
-
body: {
|
|
205
|
-
name: options.name,
|
|
206
|
-
maxDrawdown: options.maxDrawdown,
|
|
207
|
-
},
|
|
208
|
-
});
|
|
209
|
-
if (options.format === 'json') {
|
|
210
|
-
console.log(JSON.stringify(result, null, 2));
|
|
211
|
-
}
|
|
212
|
-
else {
|
|
213
|
-
console.log(formatTraderOutput(result));
|
|
214
|
-
console.log(chalk.green(' Trader updated successfully.'));
|
|
215
|
-
console.log('');
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
catch (error) {
|
|
219
|
-
if (error instanceof ApiError && error.status === 404) {
|
|
220
|
-
console.error(chalk.yellow(`Trader not found: "${nameOrId}"`));
|
|
221
|
-
}
|
|
222
|
-
else {
|
|
223
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
224
|
-
logger.error({ error: message }, 'Failed to update trader');
|
|
225
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
226
|
-
}
|
|
227
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
// ─── set-alias ───
|
|
231
|
-
const ALIAS_PATTERN = /^[a-zA-Z0-9_-]{3,30}$/;
|
|
232
|
-
trader
|
|
233
|
-
.command('set-alias')
|
|
234
|
-
.description('Set a public leaderboard alias')
|
|
235
|
-
.argument('<name-or-id>', 'Trader name or ID')
|
|
236
|
-
.argument('<alias>', 'Leaderboard alias (3-30 chars, alphanumeric/underscores/hyphens)')
|
|
237
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
238
|
-
.action(async (nameOrId, alias, options) => {
|
|
239
|
-
const jsonMode = options.format === 'json';
|
|
240
|
-
if (!ALIAS_PATTERN.test(alias)) {
|
|
241
|
-
const msg = 'Invalid alias. Must be 3-30 characters, alphanumeric with underscores and hyphens.';
|
|
242
|
-
if (jsonMode) {
|
|
243
|
-
console.error(JSON.stringify({ error: msg }));
|
|
244
|
-
}
|
|
245
|
-
else {
|
|
246
|
-
console.error(chalk.red(` ${msg}`));
|
|
247
|
-
}
|
|
248
|
-
process.exitCode = 1;
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
try {
|
|
252
|
-
await apiRequest(`/api/v1/traders/${encodeURIComponent(nameOrId)}/alias`, { method: 'PUT', body: { alias } });
|
|
253
|
-
if (jsonMode) {
|
|
254
|
-
console.log(JSON.stringify({ alias, updated: true }));
|
|
255
|
-
}
|
|
256
|
-
else {
|
|
257
|
-
console.log('');
|
|
258
|
-
console.log(chalk.green(` Alias updated to: ${chalk.white(alias)}`));
|
|
259
|
-
console.log('');
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
catch (error) {
|
|
263
|
-
if (error instanceof ApiError && error.status === 409) {
|
|
264
|
-
const msg = 'Alias already taken. Try a different name.';
|
|
265
|
-
if (jsonMode) {
|
|
266
|
-
console.error(JSON.stringify({ error: msg }));
|
|
267
|
-
}
|
|
268
|
-
else {
|
|
269
|
-
console.error(chalk.yellow(` ${msg}`));
|
|
270
|
-
}
|
|
271
|
-
process.exitCode = 2;
|
|
272
|
-
}
|
|
273
|
-
else if (error instanceof ApiError && error.status === 404) {
|
|
274
|
-
const msg = `Trader not found: "${nameOrId}"`;
|
|
275
|
-
if (jsonMode) {
|
|
276
|
-
console.error(JSON.stringify({ error: msg }));
|
|
277
|
-
}
|
|
278
|
-
else {
|
|
279
|
-
console.error(chalk.yellow(msg));
|
|
280
|
-
}
|
|
281
|
-
process.exitCode = 2;
|
|
282
|
-
}
|
|
283
|
-
else {
|
|
284
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
285
|
-
logger.error({ error: message }, 'Failed to set alias');
|
|
286
|
-
if (jsonMode) {
|
|
287
|
-
console.error(JSON.stringify({ error: message }));
|
|
288
|
-
}
|
|
289
|
-
else {
|
|
290
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
291
|
-
}
|
|
292
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
});
|
|
296
|
-
// ─── preferences ───
|
|
297
|
-
trader
|
|
298
|
-
.command('preferences')
|
|
299
|
-
.description('Update notification preferences')
|
|
300
|
-
.option('--summary-time <hour>', 'Hour (0-23 UTC) to receive daily email summary', parseInt)
|
|
301
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
302
|
-
.action(async (options) => {
|
|
303
|
-
if (options.summaryTime === undefined) {
|
|
304
|
-
console.error(chalk.yellow('No updates provided. Use --summary-time <hour>.'));
|
|
305
|
-
console.error(chalk.dim(' Example: trading-boy trader preferences --summary-time 14'));
|
|
306
|
-
process.exitCode = 1;
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
if (isNaN(options.summaryTime) || options.summaryTime < 0 || options.summaryTime > 23) {
|
|
310
|
-
console.error(chalk.red('Error: --summary-time must be 0-23 (UTC hour)'));
|
|
311
|
-
process.exitCode = 1;
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
try {
|
|
315
|
-
const result = await apiRequest('/api/v1/preferences/email', {
|
|
316
|
-
method: 'PATCH',
|
|
317
|
-
body: { summaryHourUtc: options.summaryTime },
|
|
318
|
-
});
|
|
319
|
-
if (options.format === 'json') {
|
|
320
|
-
console.log(JSON.stringify(result, null, 2));
|
|
321
|
-
}
|
|
322
|
-
else {
|
|
323
|
-
console.log('');
|
|
324
|
-
console.log(chalk.green(' Email preferences updated.'));
|
|
325
|
-
console.log(` ${chalk.gray('Email:')} ${chalk.white(result.email)}`);
|
|
326
|
-
console.log(` ${chalk.gray('Summary time:')} ${chalk.white(String(result.summaryHourUtc).padStart(2, '0') + ':00 UTC')}`);
|
|
327
|
-
console.log('');
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
catch (error) {
|
|
331
|
-
const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
|
|
332
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
333
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
334
|
-
}
|
|
335
|
-
});
|
|
336
|
-
// ─── link-wallet ───
|
|
337
|
-
trader
|
|
338
|
-
.command('link-wallet')
|
|
339
|
-
.description('Link a wallet address to a trader')
|
|
340
|
-
.argument('<name-or-id>', 'Trader name or ID')
|
|
341
|
-
.argument('<wallet-address>', 'Solana wallet address')
|
|
342
|
-
.option('--chain <chain>', 'Blockchain name', 'solana')
|
|
343
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
344
|
-
.action(async (nameOrId, walletAddress, options) => {
|
|
345
|
-
try {
|
|
346
|
-
await apiRequest(`/api/v1/traders/${encodeURIComponent(nameOrId)}/wallets`, {
|
|
347
|
-
method: 'POST',
|
|
348
|
-
body: {
|
|
349
|
-
address: walletAddress,
|
|
350
|
-
chain: options.chain,
|
|
351
|
-
},
|
|
352
|
-
});
|
|
353
|
-
if (options.format === 'json') {
|
|
354
|
-
console.log(JSON.stringify({ linked: true, address: walletAddress, chain: options.chain }, null, 2));
|
|
355
|
-
}
|
|
356
|
-
else {
|
|
357
|
-
console.log('');
|
|
358
|
-
console.log(chalk.green(` Wallet linked: ${walletAddress}`));
|
|
359
|
-
console.log('');
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
catch (error) {
|
|
363
|
-
if (error instanceof ApiError && error.status === 404) {
|
|
364
|
-
console.error(chalk.yellow(`Trader not found: "${nameOrId}"`));
|
|
365
|
-
}
|
|
366
|
-
else {
|
|
367
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
368
|
-
logger.error({ error: message }, 'Failed to link wallet');
|
|
369
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
370
|
-
}
|
|
371
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
372
|
-
}
|
|
373
|
-
});
|
|
374
|
-
// ─── soul ───
|
|
375
|
-
trader
|
|
376
|
-
.command('soul')
|
|
377
|
-
.description('Show or upload a SOUL document')
|
|
378
|
-
.argument('<name-or-id>', 'Trader name or ID')
|
|
379
|
-
.option('--file <path>', 'Path to SOUL document file to upload')
|
|
380
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
381
|
-
.action(async (nameOrId, options) => {
|
|
382
|
-
try {
|
|
383
|
-
if (options.file) {
|
|
384
|
-
// Upload SOUL document
|
|
385
|
-
const document = readFileSync(options.file, 'utf-8');
|
|
386
|
-
const result = await apiRequest(`/api/v1/traders/${encodeURIComponent(nameOrId)}/soul`, { method: 'PUT', body: { document } });
|
|
387
|
-
if (options.format === 'json') {
|
|
388
|
-
console.log(JSON.stringify(result, null, 2));
|
|
389
|
-
}
|
|
390
|
-
else {
|
|
391
|
-
console.log('');
|
|
392
|
-
console.log(chalk.green(' SOUL document uploaded successfully.'));
|
|
393
|
-
const parsed = result.parsed;
|
|
394
|
-
if (parsed) {
|
|
395
|
-
if (parsed.tradingPersonality)
|
|
396
|
-
console.log(` ${chalk.gray('Personality:')} ${chalk.white(String(parsed.tradingPersonality))}`);
|
|
397
|
-
if (parsed.riskTolerance)
|
|
398
|
-
console.log(` ${chalk.gray('Risk:')} ${chalk.white(String(parsed.riskTolerance))}`);
|
|
399
|
-
const biases = parsed.biases;
|
|
400
|
-
if (biases && biases.length > 0) {
|
|
401
|
-
console.log(` ${chalk.gray('Biases:')} ${chalk.yellow(biases.map(b => b.canonicalName).join(', '))}`);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
console.log('');
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
else {
|
|
408
|
-
// Show current SOUL document
|
|
409
|
-
const result = await apiRequest(`/api/v1/traders/${encodeURIComponent(nameOrId)}/soul`);
|
|
410
|
-
if (options.format === 'json') {
|
|
411
|
-
console.log(JSON.stringify(result, null, 2));
|
|
412
|
-
}
|
|
413
|
-
else {
|
|
414
|
-
console.log('');
|
|
415
|
-
console.log(chalk.bold.cyan(' SOUL Document'));
|
|
416
|
-
console.log(chalk.gray(' ' + '\u2500'.repeat(50)));
|
|
417
|
-
console.log('');
|
|
418
|
-
console.log(result.rawDocument.split('\n').map(l => ' ' + l).join('\n'));
|
|
419
|
-
console.log('');
|
|
420
|
-
console.log(` ${chalk.gray('Updated:')} ${chalk.dim(result.updatedAt)}`);
|
|
421
|
-
console.log('');
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
catch (error) {
|
|
426
|
-
if (error instanceof ApiError && error.status === 404) {
|
|
427
|
-
if (options.format === 'json') {
|
|
428
|
-
console.log(JSON.stringify({ soul: null, message: options.file ? `Trader not found: "${nameOrId}"` : 'No SOUL document found' }));
|
|
429
|
-
}
|
|
430
|
-
else {
|
|
431
|
-
console.error(chalk.yellow(options.file ? `Trader not found: "${nameOrId}"` : 'No SOUL document found.'));
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
else {
|
|
435
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
436
|
-
if (options.format === 'json') {
|
|
437
|
-
console.log(JSON.stringify({ error: message }));
|
|
438
|
-
}
|
|
439
|
-
else {
|
|
440
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
444
|
-
}
|
|
445
|
-
});
|
|
446
|
-
// ─── soul-wizard ───
|
|
447
|
-
trader
|
|
448
|
-
.command('soul-wizard')
|
|
449
|
-
.description('Interactive wizard to create your trading identity')
|
|
450
|
-
.argument('<name>', 'Trader name')
|
|
451
|
-
.action(async (name) => {
|
|
452
|
-
try {
|
|
453
|
-
const { runSoulWizardAndUpload } = await import('./soul-wizard.js');
|
|
454
|
-
console.log('');
|
|
455
|
-
console.log(chalk.bold.cyan(' SOUL Wizard'));
|
|
456
|
-
console.log(chalk.gray(' ' + '\u2500'.repeat(50)));
|
|
457
|
-
console.log(chalk.dim(' Answer a few questions to generate your trading identity.'));
|
|
458
|
-
console.log('');
|
|
459
|
-
await runSoulWizardAndUpload(name);
|
|
460
|
-
}
|
|
461
|
-
catch (error) {
|
|
462
|
-
if (error instanceof Error && error.message.includes('User force closed')) {
|
|
463
|
-
console.log('');
|
|
464
|
-
return;
|
|
465
|
-
}
|
|
466
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
467
|
-
logger.error({ error: message }, 'Soul wizard failed');
|
|
468
|
-
console.error(chalk.red(` Error: ${message}`));
|
|
469
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
470
|
-
}
|
|
471
|
-
});
|
|
472
|
-
// ─── purpose ───
|
|
473
|
-
trader
|
|
474
|
-
.command('purpose')
|
|
475
|
-
.description('Show or upload a PURPOSE document')
|
|
476
|
-
.argument('<name-or-id>', 'Trader name or ID')
|
|
477
|
-
.option('--file <path>', 'Path to PURPOSE document file to upload')
|
|
478
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
479
|
-
.action(async (nameOrId, options) => {
|
|
480
|
-
try {
|
|
481
|
-
if (options.file) {
|
|
482
|
-
const document = readFileSync(options.file, 'utf-8');
|
|
483
|
-
const result = await apiRequest(`/api/v1/traders/${encodeURIComponent(nameOrId)}/purpose`, { method: 'PUT', body: { document } });
|
|
484
|
-
if (options.format === 'json') {
|
|
485
|
-
console.log(JSON.stringify(result, null, 2));
|
|
486
|
-
}
|
|
487
|
-
else {
|
|
488
|
-
console.log('');
|
|
489
|
-
console.log(chalk.green(' PURPOSE document uploaded successfully.'));
|
|
490
|
-
const parsed = result.parsed;
|
|
491
|
-
if (parsed) {
|
|
492
|
-
if (parsed.mission)
|
|
493
|
-
console.log(` ${chalk.gray('Mission:')} ${chalk.white(String(parsed.mission).slice(0, 80))}`);
|
|
494
|
-
const scope = parsed.assetScope;
|
|
495
|
-
if (scope && scope.length > 0) {
|
|
496
|
-
console.log(` ${chalk.gray('Scope:')} ${chalk.white(scope.join(', '))}`);
|
|
497
|
-
}
|
|
498
|
-
if (parsed.timeHorizon)
|
|
499
|
-
console.log(` ${chalk.gray('Horizon:')} ${chalk.white(String(parsed.timeHorizon))}`);
|
|
500
|
-
}
|
|
501
|
-
console.log('');
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
else {
|
|
505
|
-
const result = await apiRequest(`/api/v1/traders/${encodeURIComponent(nameOrId)}/purpose`);
|
|
506
|
-
if (options.format === 'json') {
|
|
507
|
-
console.log(JSON.stringify(result, null, 2));
|
|
508
|
-
}
|
|
509
|
-
else {
|
|
510
|
-
console.log('');
|
|
511
|
-
console.log(chalk.bold.cyan(' PURPOSE Document'));
|
|
512
|
-
console.log(chalk.gray(' ' + '\u2500'.repeat(50)));
|
|
513
|
-
console.log('');
|
|
514
|
-
console.log(result.rawDocument.split('\n').map(l => ' ' + l).join('\n'));
|
|
515
|
-
console.log('');
|
|
516
|
-
console.log(` ${chalk.gray('Updated:')} ${chalk.dim(result.updatedAt)}`);
|
|
517
|
-
console.log('');
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
catch (error) {
|
|
522
|
-
if (error instanceof ApiError && error.status === 404) {
|
|
523
|
-
if (options.format === 'json') {
|
|
524
|
-
console.log(JSON.stringify({ purpose: null, message: options.file ? `Trader not found: "${nameOrId}"` : 'No PURPOSE document found' }));
|
|
525
|
-
}
|
|
526
|
-
else {
|
|
527
|
-
console.error(chalk.yellow(options.file ? `Trader not found: "${nameOrId}"` : 'No PURPOSE document found.'));
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
else {
|
|
531
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
532
|
-
if (options.format === 'json') {
|
|
533
|
-
console.log(JSON.stringify({ error: message }));
|
|
534
|
-
}
|
|
535
|
-
else {
|
|
536
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
540
|
-
}
|
|
541
|
-
});
|
|
542
|
-
// ─── identity ───
|
|
543
|
-
trader
|
|
544
|
-
.command('identity')
|
|
545
|
-
.description('Show combined identity summary with bias-signal matrix')
|
|
546
|
-
.argument('<name-or-id>', 'Trader name or ID')
|
|
547
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
548
|
-
.action(async (nameOrId, options) => {
|
|
549
|
-
try {
|
|
550
|
-
const traderResult = await apiRequest(`/api/v1/traders/${encodeURIComponent(nameOrId)}`);
|
|
551
|
-
// Fetch both documents (they may not exist)
|
|
552
|
-
let soulDoc = null;
|
|
553
|
-
let purposeDoc = null;
|
|
554
|
-
try {
|
|
555
|
-
soulDoc = await apiRequest(`/api/v1/traders/${encodeURIComponent(traderResult.id)}/soul`);
|
|
556
|
-
}
|
|
557
|
-
catch { /* no soul doc */ }
|
|
558
|
-
try {
|
|
559
|
-
purposeDoc = await apiRequest(`/api/v1/traders/${encodeURIComponent(traderResult.id)}/purpose`);
|
|
560
|
-
}
|
|
561
|
-
catch { /* no purpose doc */ }
|
|
562
|
-
const combined = {
|
|
563
|
-
trader: { id: traderResult.id, name: traderResult.name },
|
|
564
|
-
soul: soulDoc?.parsed ?? null,
|
|
565
|
-
purpose: purposeDoc?.parsed ?? null,
|
|
566
|
-
hasSoul: soulDoc !== null,
|
|
567
|
-
hasPurpose: purposeDoc !== null,
|
|
568
|
-
};
|
|
569
|
-
if (options.format === 'json') {
|
|
570
|
-
console.log(JSON.stringify(combined, null, 2));
|
|
571
|
-
}
|
|
572
|
-
else {
|
|
573
|
-
console.log('');
|
|
574
|
-
console.log(chalk.bold.cyan(` Identity: ${traderResult.name}`));
|
|
575
|
-
console.log(chalk.gray(' ' + '\u2500'.repeat(50)));
|
|
576
|
-
console.log('');
|
|
577
|
-
if (soulDoc?.parsed) {
|
|
578
|
-
const p = soulDoc.parsed;
|
|
579
|
-
console.log(` ${chalk.bold('SOUL')}`);
|
|
580
|
-
if (p.tradingPersonality)
|
|
581
|
-
console.log(` ${chalk.gray('Personality:')} ${chalk.white(String(p.tradingPersonality))}`);
|
|
582
|
-
if (p.riskTolerance)
|
|
583
|
-
console.log(` ${chalk.gray('Risk:')} ${chalk.white(String(p.riskTolerance))}`);
|
|
584
|
-
if (p.decisionStyle)
|
|
585
|
-
console.log(` ${chalk.gray('Decisions:')} ${chalk.white(String(p.decisionStyle))}`);
|
|
586
|
-
const biases = p.biases;
|
|
587
|
-
if (biases && biases.length > 0) {
|
|
588
|
-
console.log(` ${chalk.gray('Biases:')}`);
|
|
589
|
-
for (const b of biases) {
|
|
590
|
-
const color = b.severity === 'high' ? chalk.red : b.severity === 'medium' ? chalk.yellow : chalk.dim;
|
|
591
|
-
console.log(` ${chalk.dim('\u2022')} ${color(b.canonicalName)} ${chalk.dim(`(${b.severity})`)}`);
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
else {
|
|
596
|
-
console.log(` ${chalk.dim('No SOUL document. Upload with: trader soul <id> --file <path>')}`);
|
|
597
|
-
}
|
|
598
|
-
console.log('');
|
|
599
|
-
if (purposeDoc?.parsed) {
|
|
600
|
-
const p = purposeDoc.parsed;
|
|
601
|
-
console.log(` ${chalk.bold('PURPOSE')}`);
|
|
602
|
-
if (p.mission)
|
|
603
|
-
console.log(` ${chalk.gray('Mission:')} ${chalk.white(String(p.mission).slice(0, 100))}`);
|
|
604
|
-
const scope = p.assetScope;
|
|
605
|
-
if (scope && scope.length > 0)
|
|
606
|
-
console.log(` ${chalk.gray('Scope:')} ${chalk.white(scope.join(', '))}`);
|
|
607
|
-
const outOfScope = p.outOfScope;
|
|
608
|
-
if (outOfScope && outOfScope.length > 0)
|
|
609
|
-
console.log(` ${chalk.gray('Out of scope:')} ${chalk.dim(outOfScope.join(', '))}`);
|
|
610
|
-
if (p.timeHorizon)
|
|
611
|
-
console.log(` ${chalk.gray('Horizon:')} ${chalk.white(String(p.timeHorizon))}`);
|
|
612
|
-
const goals = p.goals;
|
|
613
|
-
if (goals && goals.length > 0) {
|
|
614
|
-
console.log(` ${chalk.gray('Goals:')}`);
|
|
615
|
-
for (const g of goals)
|
|
616
|
-
console.log(` ${chalk.dim('\u2022')} ${chalk.white(g)}`);
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
else {
|
|
620
|
-
console.log(` ${chalk.dim('No PURPOSE document. Upload with: trader purpose <id> --file <path>')}`);
|
|
621
|
-
}
|
|
622
|
-
console.log('');
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
catch (error) {
|
|
626
|
-
if (error instanceof ApiError && error.status === 404) {
|
|
627
|
-
console.error(chalk.yellow(`Trader not found: "${nameOrId}"`));
|
|
628
|
-
}
|
|
629
|
-
else {
|
|
630
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
631
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
632
|
-
}
|
|
633
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
634
|
-
}
|
|
635
|
-
});
|
|
636
|
-
// ─── history ───
|
|
637
|
-
trader
|
|
638
|
-
.command('history')
|
|
639
|
-
.description('Show document revision history')
|
|
640
|
-
.argument('<name-or-id>', 'Trader name or ID')
|
|
641
|
-
.option('--type <type>', 'Filter by document type (SOUL or PURPOSE)')
|
|
642
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
643
|
-
.action(async (nameOrId, options) => {
|
|
644
|
-
try {
|
|
645
|
-
let url = `/api/v1/traders/${encodeURIComponent(nameOrId)}/documents/history`;
|
|
646
|
-
if (options.type)
|
|
647
|
-
url += `?type=${options.type.toUpperCase()}`;
|
|
648
|
-
const result = await apiRequest(url);
|
|
649
|
-
if (options.format === 'json') {
|
|
650
|
-
console.log(JSON.stringify(result, null, 2));
|
|
651
|
-
}
|
|
652
|
-
else {
|
|
653
|
-
console.log('');
|
|
654
|
-
console.log(chalk.bold.cyan(' Document Revision History'));
|
|
655
|
-
console.log(chalk.gray(' ' + '\u2500'.repeat(50)));
|
|
656
|
-
console.log('');
|
|
657
|
-
if (result.revisions.length === 0) {
|
|
658
|
-
console.log(` ${chalk.dim('No document revisions found.')}`);
|
|
659
|
-
}
|
|
660
|
-
else {
|
|
661
|
-
console.log(' ' + padRight('Type', 12) + padRight('Version', 10) + 'Created');
|
|
662
|
-
console.log(' ' + '\u2500'.repeat(50));
|
|
663
|
-
for (const r of result.revisions) {
|
|
664
|
-
console.log(' ' +
|
|
665
|
-
padRight(r.documentType, 12) +
|
|
666
|
-
padRight(`v${r.version}`, 10) +
|
|
667
|
-
chalk.dim(r.createdAt));
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
console.log('');
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
catch (error) {
|
|
674
|
-
if (error instanceof ApiError && error.status === 404) {
|
|
675
|
-
console.error(chalk.yellow(`Trader not found: "${nameOrId}"`));
|
|
676
|
-
}
|
|
677
|
-
else {
|
|
678
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
679
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
680
|
-
}
|
|
681
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
682
|
-
}
|
|
683
|
-
});
|
|
684
|
-
// ─── agent (subcommand group) ───
|
|
685
|
-
const AUTONOMY_LEVELS = ['OBSERVE_ONLY', 'SUGGEST', 'AUTO_WITH_APPROVAL', 'FULLY_AUTONOMOUS'];
|
|
686
|
-
const agent = trader
|
|
687
|
-
.command('agent')
|
|
688
|
-
.description('Manage autonomous trading agent');
|
|
689
|
-
// ─── agent status ───
|
|
690
|
-
agent
|
|
691
|
-
.command('status')
|
|
692
|
-
.description('Show agent runtime status')
|
|
693
|
-
.argument('<agent-id>', 'Agent ID')
|
|
694
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
695
|
-
.action(async (agentId, options) => {
|
|
696
|
-
const jsonMode = options.format === 'json';
|
|
697
|
-
try {
|
|
698
|
-
const result = await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/status`);
|
|
699
|
-
if (jsonMode) {
|
|
700
|
-
const safeOutput = {
|
|
701
|
-
agentId,
|
|
702
|
-
state: result.state,
|
|
703
|
-
autonomyLevel: result.autonomyLevel,
|
|
704
|
-
paused: result.paused,
|
|
705
|
-
killed: result.killed,
|
|
706
|
-
override: result.humanOverride,
|
|
707
|
-
openPositions: result.openPositions ?? 0,
|
|
708
|
-
dailyTradeCount: result.dailyTradeCount ?? 0,
|
|
709
|
-
dailyPnlUsd: result.dailyPnlUsd ?? null,
|
|
710
|
-
lastTransitionAt: result.lastTransitionAt ?? null,
|
|
711
|
-
healthTimestamp: result.healthTimestamp ?? null,
|
|
712
|
-
};
|
|
713
|
-
console.log(JSON.stringify(safeOutput, null, 2));
|
|
714
|
-
}
|
|
715
|
-
else {
|
|
716
|
-
console.log('');
|
|
717
|
-
console.log(chalk.bold.cyan(' Agent Status'));
|
|
718
|
-
console.log(chalk.gray(' ' + '\u2500'.repeat(50)));
|
|
719
|
-
console.log('');
|
|
720
|
-
console.log(` ${chalk.gray('Agent ID:')} ${chalk.white(agentId)}`);
|
|
721
|
-
console.log(` ${chalk.gray('State:')} ${formatAgentState(result.state)}`);
|
|
722
|
-
console.log(` ${chalk.gray('Autonomy:')} ${chalk.white(result.autonomyLevel)}`);
|
|
723
|
-
console.log(` ${chalk.gray('Paused:')} ${result.paused ? chalk.yellow('yes') : chalk.green('no')}`);
|
|
724
|
-
console.log(` ${chalk.gray('Killed:')} ${result.killed ? chalk.red('yes') : chalk.green('no')}`);
|
|
725
|
-
console.log(` ${chalk.gray('Override:')} ${result.humanOverride ? chalk.yellow('active') : chalk.dim('none')}`);
|
|
726
|
-
console.log(` ${chalk.gray('Open positions:')} ${chalk.white(String(result.openPositions ?? 0))}`);
|
|
727
|
-
console.log(` ${chalk.gray('Trades today:')} ${chalk.white(String(result.dailyTradeCount ?? 0))}`);
|
|
728
|
-
console.log(` ${chalk.gray('Daily PnL:')} ${formatPnl(result.dailyPnlUsd)}`);
|
|
729
|
-
console.log('');
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
catch (error) {
|
|
733
|
-
handleAgentError(error, jsonMode, agentId, 'get agent status');
|
|
734
|
-
}
|
|
735
|
-
});
|
|
736
|
-
// ─── agent pause ───
|
|
737
|
-
agent
|
|
738
|
-
.command('pause')
|
|
739
|
-
.description('Pause the agent (stops all trading)')
|
|
740
|
-
.argument('<agent-id>', 'Agent ID')
|
|
741
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
742
|
-
.action(async (agentId, options) => {
|
|
743
|
-
const jsonMode = options.format === 'json';
|
|
744
|
-
try {
|
|
745
|
-
await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/pause`, { method: 'POST' });
|
|
746
|
-
if (jsonMode) {
|
|
747
|
-
console.log(JSON.stringify({ agentId, paused: true }));
|
|
748
|
-
}
|
|
749
|
-
else {
|
|
750
|
-
console.log('');
|
|
751
|
-
console.log(chalk.yellow(` Agent ${agentId} paused.`));
|
|
752
|
-
console.log(chalk.dim(' Resume with: trading-boy trader agent resume ' + agentId));
|
|
753
|
-
console.log('');
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
catch (error) {
|
|
757
|
-
handleAgentError(error, jsonMode, agentId, 'pause agent');
|
|
758
|
-
}
|
|
759
|
-
});
|
|
760
|
-
// ─── agent resume ───
|
|
761
|
-
agent
|
|
762
|
-
.command('resume')
|
|
763
|
-
.description('Resume a paused agent')
|
|
764
|
-
.argument('<agent-id>', 'Agent ID')
|
|
765
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
766
|
-
.action(async (agentId, options) => {
|
|
767
|
-
const jsonMode = options.format === 'json';
|
|
768
|
-
try {
|
|
769
|
-
await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/resume`, { method: 'POST' });
|
|
770
|
-
if (jsonMode) {
|
|
771
|
-
console.log(JSON.stringify({ agentId, resumed: true }));
|
|
772
|
-
}
|
|
773
|
-
else {
|
|
774
|
-
console.log('');
|
|
775
|
-
console.log(chalk.green(` Agent ${agentId} resumed.`));
|
|
776
|
-
console.log('');
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
catch (error) {
|
|
780
|
-
handleAgentError(error, jsonMode, agentId, 'resume agent');
|
|
781
|
-
}
|
|
782
|
-
});
|
|
783
|
-
// ─── agent kill ───
|
|
784
|
-
agent
|
|
785
|
-
.command('kill')
|
|
786
|
-
.description('Kill the agent permanently (until revived)')
|
|
787
|
-
.argument('<agent-id>', 'Agent ID')
|
|
788
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
789
|
-
.addOption(new Option('--force', 'Skip confirmation prompt'))
|
|
790
|
-
.action(async (agentId, options) => {
|
|
791
|
-
const jsonMode = options.format === 'json';
|
|
792
|
-
if (jsonMode && !options.force) {
|
|
793
|
-
console.error(JSON.stringify({ error: '--force is required with --format json' }));
|
|
794
|
-
process.exitCode = 1;
|
|
795
|
-
return;
|
|
796
|
-
}
|
|
797
|
-
if (!options.force) {
|
|
798
|
-
const proceed = await confirm({
|
|
799
|
-
message: `Are you sure you want to kill agent ${agentId}? This is permanent until revived.`,
|
|
800
|
-
});
|
|
801
|
-
if (!proceed) {
|
|
802
|
-
console.log(chalk.dim(' Kill cancelled.'));
|
|
803
|
-
return;
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
try {
|
|
807
|
-
await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/kill`, { method: 'POST' });
|
|
808
|
-
if (jsonMode) {
|
|
809
|
-
console.log(JSON.stringify({ agentId, killed: true }));
|
|
810
|
-
}
|
|
811
|
-
else {
|
|
812
|
-
console.log('');
|
|
813
|
-
console.log(chalk.red(` Agent ${agentId} killed.`));
|
|
814
|
-
console.log(chalk.dim(' The agent will not trade until manually revived.'));
|
|
815
|
-
console.log('');
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
catch (error) {
|
|
819
|
-
handleAgentError(error, jsonMode, agentId, 'kill agent');
|
|
820
|
-
}
|
|
821
|
-
});
|
|
822
|
-
// ─── agent autonomy ───
|
|
823
|
-
agent
|
|
824
|
-
.command('autonomy')
|
|
825
|
-
.description('Set agent autonomy level')
|
|
826
|
-
.argument('<agent-id>', 'Agent ID')
|
|
827
|
-
.argument('<level>', `Autonomy level: ${AUTONOMY_LEVELS.join(' | ')}`)
|
|
828
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
829
|
-
.action(async (agentId, level, options) => {
|
|
830
|
-
const jsonMode = options.format === 'json';
|
|
831
|
-
const upperLevel = level.toUpperCase();
|
|
832
|
-
if (!AUTONOMY_LEVELS.includes(upperLevel)) {
|
|
833
|
-
const msg = `Invalid autonomy level. Must be one of: ${AUTONOMY_LEVELS.join(', ')}`;
|
|
834
|
-
if (jsonMode) {
|
|
835
|
-
console.error(JSON.stringify({ error: msg }));
|
|
836
|
-
}
|
|
837
|
-
else {
|
|
838
|
-
console.error(chalk.red(` ${msg}`));
|
|
839
|
-
}
|
|
840
|
-
process.exitCode = 1;
|
|
841
|
-
return;
|
|
842
|
-
}
|
|
843
|
-
try {
|
|
844
|
-
await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/autonomy`, {
|
|
845
|
-
method: 'PUT',
|
|
846
|
-
body: { level: upperLevel },
|
|
847
|
-
});
|
|
848
|
-
if (jsonMode) {
|
|
849
|
-
console.log(JSON.stringify({ agentId, autonomyLevel: upperLevel }));
|
|
850
|
-
}
|
|
851
|
-
else {
|
|
852
|
-
console.log('');
|
|
853
|
-
console.log(chalk.green(` Autonomy level set to: ${chalk.white(upperLevel)}`));
|
|
854
|
-
console.log('');
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
catch (error) {
|
|
858
|
-
handleAgentError(error, jsonMode, agentId, 'set autonomy');
|
|
859
|
-
}
|
|
860
|
-
});
|
|
861
|
-
// ─── agent override ───
|
|
862
|
-
agent
|
|
863
|
-
.command('override')
|
|
864
|
-
.description('Set human override (agent defers all decisions to human)')
|
|
865
|
-
.argument('<agent-id>', 'Agent ID')
|
|
866
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
867
|
-
.addOption(new Option('--force', 'Skip confirmation prompt'))
|
|
868
|
-
.action(async (agentId, options) => {
|
|
869
|
-
const jsonMode = options.format === 'json';
|
|
870
|
-
if (jsonMode && !options.force) {
|
|
871
|
-
console.error(JSON.stringify({ error: '--force is required with --format json' }));
|
|
872
|
-
process.exitCode = 1;
|
|
873
|
-
return;
|
|
874
|
-
}
|
|
875
|
-
if (!options.force) {
|
|
876
|
-
const proceed = await confirm({
|
|
877
|
-
message: `Are you sure you want to set human override for agent ${agentId}?`,
|
|
878
|
-
});
|
|
879
|
-
if (!proceed) {
|
|
880
|
-
console.log(chalk.dim(' Override cancelled.'));
|
|
881
|
-
return;
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
try {
|
|
885
|
-
await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/override`, { method: 'POST' });
|
|
886
|
-
if (jsonMode) {
|
|
887
|
-
console.log(JSON.stringify({ agentId, humanOverride: true }));
|
|
888
|
-
}
|
|
889
|
-
else {
|
|
890
|
-
console.log('');
|
|
891
|
-
console.log(chalk.yellow(` Human override set for agent ${agentId}.`));
|
|
892
|
-
console.log(chalk.dim(' The agent will defer all decisions to you.'));
|
|
893
|
-
console.log(chalk.dim(' Clear with: trading-boy trader agent clear-override ' + agentId));
|
|
894
|
-
console.log('');
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
catch (error) {
|
|
898
|
-
handleAgentError(error, jsonMode, agentId, 'set override');
|
|
899
|
-
}
|
|
900
|
-
});
|
|
901
|
-
// ─── agent clear-override ───
|
|
902
|
-
agent
|
|
903
|
-
.command('clear-override')
|
|
904
|
-
.description('Clear human override (return control to agent)')
|
|
905
|
-
.argument('<agent-id>', 'Agent ID')
|
|
906
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
907
|
-
.action(async (agentId, options) => {
|
|
908
|
-
const jsonMode = options.format === 'json';
|
|
909
|
-
try {
|
|
910
|
-
await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/clear-override`, { method: 'POST' });
|
|
911
|
-
if (jsonMode) {
|
|
912
|
-
console.log(JSON.stringify({ agentId, humanOverride: false }));
|
|
913
|
-
}
|
|
914
|
-
else {
|
|
915
|
-
console.log('');
|
|
916
|
-
console.log(chalk.green(` Human override cleared for agent ${agentId}.`));
|
|
917
|
-
console.log(chalk.dim(' The agent will resume autonomous decision-making.'));
|
|
918
|
-
console.log('');
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
catch (error) {
|
|
922
|
-
handleAgentError(error, jsonMode, agentId, 'clear override');
|
|
923
|
-
}
|
|
924
|
-
});
|
|
925
|
-
}
|
|
926
|
-
// ─── Agent Helpers ───
|
|
927
|
-
function formatAgentState(state) {
|
|
928
|
-
const colors = {
|
|
929
|
-
'IDLE': chalk.dim,
|
|
930
|
-
'SCANNING': chalk.cyan,
|
|
931
|
-
'ANALYZING': chalk.blue,
|
|
932
|
-
'EXECUTING': chalk.yellow,
|
|
933
|
-
'PAUSED': chalk.yellow,
|
|
934
|
-
'KILLED': chalk.red,
|
|
935
|
-
};
|
|
936
|
-
return (colors[state] ?? chalk.white)(state);
|
|
937
|
-
}
|
|
938
|
-
function formatPnl(pnl) {
|
|
939
|
-
if (pnl === undefined || pnl === null)
|
|
940
|
-
return chalk.dim('n/a');
|
|
941
|
-
const sign = pnl >= 0 ? '+' : '';
|
|
942
|
-
const color = pnl >= 0 ? chalk.green : chalk.red;
|
|
943
|
-
return color(`${sign}$${pnl.toFixed(2)}`);
|
|
944
|
-
}
|
|
945
|
-
function handleAgentError(error, jsonMode, agentId, action) {
|
|
946
|
-
if (error instanceof ApiError && error.status === 404) {
|
|
947
|
-
const msg = `Agent not found: "${agentId}"`;
|
|
948
|
-
if (jsonMode) {
|
|
949
|
-
console.error(JSON.stringify({ error: msg }));
|
|
950
|
-
}
|
|
951
|
-
else {
|
|
952
|
-
console.error(chalk.yellow(msg));
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
else {
|
|
956
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
957
|
-
logger.error({ error: message }, `Failed to ${action}`);
|
|
958
|
-
if (jsonMode) {
|
|
959
|
-
console.error(JSON.stringify({ error: message }));
|
|
960
|
-
}
|
|
961
|
-
else {
|
|
962
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
966
|
-
}
|
|
967
|
-
// ─── Helpers ───
|
|
968
|
-
function parseFloatOption(value) {
|
|
969
|
-
return parseFloat(value);
|
|
970
|
-
}
|
|
971
|
-
//# sourceMappingURL=trader.js.map
|