@trading-boy/cli 1.6.1 → 1.8.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/dist/api-client.js +7 -2
- package/dist/cli.bundle.js +264 -345
- package/dist/commands/agent-cmd.js +48 -54
- package/dist/commands/benchmark-cmd.js +4 -9
- package/dist/commands/billing.js +21 -1
- package/dist/commands/coaching-cmd.js +6 -15
- package/dist/commands/context.js +3 -7
- package/dist/commands/cron-cmd.js +10 -46
- package/dist/commands/edge-cmd.js +3 -5
- package/dist/commands/edge-guard-cmd.js +4 -9
- package/dist/commands/query.js +3 -7
- package/dist/commands/replay-cmd.js +5 -36
- package/dist/commands/risk.js +3 -7
- package/dist/commands/strategy-cmd.js +14 -51
- package/dist/commands/subscribe.js +2 -2
- package/dist/commands/suggestions-cmd.js +5 -17
- package/dist/commands/thesis-cmd.js +3 -6
- package/dist/commands/whoami.js +3 -1
- package/dist/utils.d.ts +12 -0
- package/dist/utils.js +51 -0
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Option } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { createLogger } from '@trading-boy/core';
|
|
4
|
-
import {
|
|
5
|
-
import { padRight } from '../utils.js';
|
|
4
|
+
import { apiRequest } from '../api-client.js';
|
|
5
|
+
import { padRight, handleApiError, ensureRemote } from '../utils.js';
|
|
6
6
|
// ─── Logger ───
|
|
7
7
|
const logger = createLogger('cli-replay');
|
|
8
8
|
// ─── Helpers ───
|
|
@@ -96,33 +96,6 @@ async function pollReplayJob(jobId) {
|
|
|
96
96
|
}
|
|
97
97
|
throw new Error(`Replay job ${jobId} did not complete within ${POLL_TIMEOUT_MS / 1000}s timeout.`);
|
|
98
98
|
}
|
|
99
|
-
// ─── Error Handler ───
|
|
100
|
-
function handleApiError(error, context) {
|
|
101
|
-
if (error instanceof ApiError) {
|
|
102
|
-
switch (error.status) {
|
|
103
|
-
case 401:
|
|
104
|
-
console.error(chalk.red('Error: API key invalid or expired. Run `trading-boy login` to re-authenticate.'));
|
|
105
|
-
break;
|
|
106
|
-
case 403:
|
|
107
|
-
console.error(chalk.red('Error: Subscription inactive. Run `trading-boy billing manage` to update your billing.'));
|
|
108
|
-
break;
|
|
109
|
-
case 404:
|
|
110
|
-
console.error(chalk.red(`Error: Not found — ${error.message}`));
|
|
111
|
-
break;
|
|
112
|
-
case 422:
|
|
113
|
-
console.error(chalk.red(`Error: Validation failed — ${error.message}`));
|
|
114
|
-
break;
|
|
115
|
-
default:
|
|
116
|
-
console.error(chalk.red(`Error: ${error.message}`));
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
121
|
-
logger.error({ error: message }, context);
|
|
122
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
123
|
-
}
|
|
124
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
125
|
-
}
|
|
126
99
|
// ─── Command Registration ───
|
|
127
100
|
export function registerReplayCommand(program) {
|
|
128
101
|
program
|
|
@@ -134,12 +107,8 @@ export function registerReplayCommand(program) {
|
|
|
134
107
|
.requiredOption('--to <date>', 'End date (ISO-8601, e.g. 2025-03-01)')
|
|
135
108
|
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
136
109
|
.action(async (options) => {
|
|
137
|
-
if (!(await
|
|
138
|
-
console.error(chalk.yellow('Replay requires a remote API connection.'));
|
|
139
|
-
console.error(chalk.dim(' Run: trading-boy login'));
|
|
140
|
-
process.exitCode = 1;
|
|
110
|
+
if (!(await ensureRemote()))
|
|
141
111
|
return;
|
|
142
|
-
}
|
|
143
112
|
// Validate dates
|
|
144
113
|
const fromDate = new Date(options.from);
|
|
145
114
|
const toDate = new Date(options.to);
|
|
@@ -178,7 +147,7 @@ export function registerReplayCommand(program) {
|
|
|
178
147
|
}
|
|
179
148
|
catch (error) {
|
|
180
149
|
submitSpinner.fail('Failed to submit replay job');
|
|
181
|
-
handleApiError(error, 'Replay submit failed');
|
|
150
|
+
handleApiError(error, 'Replay submit failed', logger);
|
|
182
151
|
return;
|
|
183
152
|
}
|
|
184
153
|
// ─── Poll until complete ───
|
|
@@ -190,7 +159,7 @@ export function registerReplayCommand(program) {
|
|
|
190
159
|
}
|
|
191
160
|
catch (error) {
|
|
192
161
|
pollSpinner.fail('Replay timed out or encountered an error');
|
|
193
|
-
handleApiError(error, 'Replay polling failed');
|
|
162
|
+
handleApiError(error, 'Replay polling failed', logger);
|
|
194
163
|
return;
|
|
195
164
|
}
|
|
196
165
|
// ─── Display results ───
|
package/dist/commands/risk.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Option } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { createLogger } from '@trading-boy/core';
|
|
4
|
-
import { formatConnectionError, padRight } from '../utils.js';
|
|
5
|
-
import {
|
|
4
|
+
import { formatConnectionError, padRight, ensureRemote } from '../utils.js';
|
|
5
|
+
import { apiRequest, ApiError } from '../api-client.js';
|
|
6
6
|
// ─── Logger ───
|
|
7
7
|
const logger = createLogger('cli-risk');
|
|
8
8
|
// ─── Formatters ───
|
|
@@ -127,12 +127,8 @@ export function registerRiskCommand(program) {
|
|
|
127
127
|
process.exitCode = 1;
|
|
128
128
|
return;
|
|
129
129
|
}
|
|
130
|
-
if (!(await
|
|
131
|
-
console.error(chalk.yellow('This command requires a remote API connection.'));
|
|
132
|
-
console.error(chalk.dim(' Run: trading-boy login or trading-boy subscribe'));
|
|
133
|
-
process.exitCode = 1;
|
|
130
|
+
if (!(await ensureRemote()))
|
|
134
131
|
return;
|
|
135
|
-
}
|
|
136
132
|
try {
|
|
137
133
|
const apiResult = await apiRequest(`/api/v1/protocols/${encodeURIComponent(protocol.toLowerCase())}/risk`);
|
|
138
134
|
const result = {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Option } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { createLogger } from '@trading-boy/core';
|
|
4
|
-
import {
|
|
5
|
-
import { padRight } from '../utils.js';
|
|
4
|
+
import { apiRequest } from '../api-client.js';
|
|
5
|
+
import { padRight, handleApiError, ensureRemote } from '../utils.js';
|
|
6
6
|
// ─── Logger ───
|
|
7
7
|
const logger = createLogger('cli-strategy');
|
|
8
8
|
// ─── Helpers ───
|
|
@@ -127,43 +127,6 @@ export function formatHistoryList(response) {
|
|
|
127
127
|
lines.push('');
|
|
128
128
|
return lines.join('\n');
|
|
129
129
|
}
|
|
130
|
-
// ─── Remote Mode Guard ───
|
|
131
|
-
async function requireRemote() {
|
|
132
|
-
if (!(await isRemoteMode())) {
|
|
133
|
-
console.error(chalk.yellow('Strategy commands require a remote API connection.'));
|
|
134
|
-
console.error(chalk.dim(' Run: trading-boy login'));
|
|
135
|
-
process.exitCode = 1;
|
|
136
|
-
return false;
|
|
137
|
-
}
|
|
138
|
-
return true;
|
|
139
|
-
}
|
|
140
|
-
// ─── Error Handler ───
|
|
141
|
-
function handleApiError(error, context) {
|
|
142
|
-
if (error instanceof ApiError) {
|
|
143
|
-
switch (error.status) {
|
|
144
|
-
case 401:
|
|
145
|
-
console.error(chalk.red('Error: API key invalid or expired. Run `trading-boy login` to re-authenticate.'));
|
|
146
|
-
break;
|
|
147
|
-
case 403:
|
|
148
|
-
console.error(chalk.red('Error: Subscription inactive. Run `trading-boy billing manage` to update your billing.'));
|
|
149
|
-
break;
|
|
150
|
-
case 404:
|
|
151
|
-
console.error(chalk.red(`Error: Not found — ${error.message}`));
|
|
152
|
-
break;
|
|
153
|
-
case 422:
|
|
154
|
-
console.error(chalk.red(`Error: Validation failed — ${error.message}`));
|
|
155
|
-
break;
|
|
156
|
-
default:
|
|
157
|
-
console.error(chalk.red(`Error: ${error.message}`));
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
162
|
-
logger.error({ error: message }, context);
|
|
163
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
164
|
-
}
|
|
165
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
166
|
-
}
|
|
167
130
|
// ─── Command Registration ───
|
|
168
131
|
export function registerStrategyCommand(program) {
|
|
169
132
|
const strategy = program
|
|
@@ -180,7 +143,7 @@ export function registerStrategyCommand(program) {
|
|
|
180
143
|
.option('--setups <types>', 'Comma-separated setup types')
|
|
181
144
|
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
182
145
|
.action(async (options) => {
|
|
183
|
-
if (!(await
|
|
146
|
+
if (!(await ensureRemote()))
|
|
184
147
|
return;
|
|
185
148
|
const tokens = options.tokens.split(',').map((t) => t.trim().toUpperCase()).filter(Boolean);
|
|
186
149
|
const setupTypes = options.setups
|
|
@@ -220,7 +183,7 @@ export function registerStrategyCommand(program) {
|
|
|
220
183
|
}
|
|
221
184
|
}
|
|
222
185
|
catch (error) {
|
|
223
|
-
handleApiError(error, 'Strategy create failed');
|
|
186
|
+
handleApiError(error, 'Strategy create failed', logger);
|
|
224
187
|
}
|
|
225
188
|
});
|
|
226
189
|
// ─── strategy list ───
|
|
@@ -233,7 +196,7 @@ export function registerStrategyCommand(program) {
|
|
|
233
196
|
.option('--offset <n>', 'Pagination offset', '0')
|
|
234
197
|
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
235
198
|
.action(async (options) => {
|
|
236
|
-
if (!(await
|
|
199
|
+
if (!(await ensureRemote()))
|
|
237
200
|
return;
|
|
238
201
|
const params = new URLSearchParams();
|
|
239
202
|
params.set('traderId', options.traderId);
|
|
@@ -251,7 +214,7 @@ export function registerStrategyCommand(program) {
|
|
|
251
214
|
}
|
|
252
215
|
}
|
|
253
216
|
catch (error) {
|
|
254
|
-
handleApiError(error, 'Strategy list failed');
|
|
217
|
+
handleApiError(error, 'Strategy list failed', logger);
|
|
255
218
|
}
|
|
256
219
|
});
|
|
257
220
|
// ─── strategy show <id> ───
|
|
@@ -260,7 +223,7 @@ export function registerStrategyCommand(program) {
|
|
|
260
223
|
.description('Show full details for a strategy')
|
|
261
224
|
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
262
225
|
.action(async (id, options) => {
|
|
263
|
-
if (!(await
|
|
226
|
+
if (!(await ensureRemote()))
|
|
264
227
|
return;
|
|
265
228
|
try {
|
|
266
229
|
const result = await apiRequest(`/api/v1/strategies/${encodeURIComponent(id)}`);
|
|
@@ -272,7 +235,7 @@ export function registerStrategyCommand(program) {
|
|
|
272
235
|
}
|
|
273
236
|
}
|
|
274
237
|
catch (error) {
|
|
275
|
-
handleApiError(error, 'Strategy show failed');
|
|
238
|
+
handleApiError(error, 'Strategy show failed', logger);
|
|
276
239
|
}
|
|
277
240
|
});
|
|
278
241
|
// ─── strategy update <id> ───
|
|
@@ -284,7 +247,7 @@ export function registerStrategyCommand(program) {
|
|
|
284
247
|
.option('--setups <types>', 'New comma-separated setup types (replaces existing)')
|
|
285
248
|
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
286
249
|
.action(async (id, options) => {
|
|
287
|
-
if (!(await
|
|
250
|
+
if (!(await ensureRemote()))
|
|
288
251
|
return;
|
|
289
252
|
const body = {};
|
|
290
253
|
if (options.name) {
|
|
@@ -311,7 +274,7 @@ export function registerStrategyCommand(program) {
|
|
|
311
274
|
}
|
|
312
275
|
}
|
|
313
276
|
catch (error) {
|
|
314
|
-
handleApiError(error, 'Strategy update failed');
|
|
277
|
+
handleApiError(error, 'Strategy update failed', logger);
|
|
315
278
|
}
|
|
316
279
|
});
|
|
317
280
|
// ─── strategy history <id> ───
|
|
@@ -320,7 +283,7 @@ export function registerStrategyCommand(program) {
|
|
|
320
283
|
.description('Show version history for a strategy')
|
|
321
284
|
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
322
285
|
.action(async (id, options) => {
|
|
323
|
-
if (!(await
|
|
286
|
+
if (!(await ensureRemote()))
|
|
324
287
|
return;
|
|
325
288
|
try {
|
|
326
289
|
const result = await apiRequest(`/api/v1/strategies/${encodeURIComponent(id)}/history`);
|
|
@@ -332,7 +295,7 @@ export function registerStrategyCommand(program) {
|
|
|
332
295
|
}
|
|
333
296
|
}
|
|
334
297
|
catch (error) {
|
|
335
|
-
handleApiError(error, 'Strategy history failed');
|
|
298
|
+
handleApiError(error, 'Strategy history failed', logger);
|
|
336
299
|
}
|
|
337
300
|
});
|
|
338
301
|
// ─── strategy export ───
|
|
@@ -345,7 +308,7 @@ export function registerStrategyCommand(program) {
|
|
|
345
308
|
.default('json'))
|
|
346
309
|
.option('--output <file>', 'Write output to a file instead of stdout')
|
|
347
310
|
.action(async (options) => {
|
|
348
|
-
if (!(await
|
|
311
|
+
if (!(await ensureRemote()))
|
|
349
312
|
return;
|
|
350
313
|
try {
|
|
351
314
|
const result = await apiRequest(`/api/v1/strategies/${encodeURIComponent(options.id)}/export?format=${encodeURIComponent(options.format)}`);
|
|
@@ -370,7 +333,7 @@ export function registerStrategyCommand(program) {
|
|
|
370
333
|
}
|
|
371
334
|
}
|
|
372
335
|
catch (error) {
|
|
373
|
-
handleApiError(error, 'Strategy export failed');
|
|
336
|
+
handleApiError(error, 'Strategy export failed', logger);
|
|
374
337
|
}
|
|
375
338
|
});
|
|
376
339
|
}
|
|
@@ -156,7 +156,7 @@ export async function pollForApiKey(token, onTick) {
|
|
|
156
156
|
if (result.status === 'expired' || result.status === 'not_found') {
|
|
157
157
|
return {
|
|
158
158
|
success: false,
|
|
159
|
-
error: 'Provisioning token expired.
|
|
159
|
+
error: 'Provisioning token expired. Your API key will be emailed to you shortly. Then run: `trading-boy login`',
|
|
160
160
|
};
|
|
161
161
|
}
|
|
162
162
|
if (result.status === 'already_retrieved') {
|
|
@@ -175,7 +175,7 @@ export async function pollForApiKey(token, onTick) {
|
|
|
175
175
|
}
|
|
176
176
|
return {
|
|
177
177
|
success: false,
|
|
178
|
-
error: 'Timed out waiting for payment confirmation.
|
|
178
|
+
error: 'Timed out waiting for payment confirmation. Your API key will be emailed to you shortly. Then run: `trading-boy login`',
|
|
179
179
|
};
|
|
180
180
|
}
|
|
181
181
|
// ─── Credential Storage ───
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Option } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { createLogger } from '@trading-boy/core';
|
|
4
|
-
import {
|
|
5
|
-
import { padRight, truncate, formatDate } from '../utils.js';
|
|
4
|
+
import { apiRequest, ApiError } from '../api-client.js';
|
|
5
|
+
import { padRight, truncate, formatDate, ensureRemote } from '../utils.js';
|
|
6
6
|
// ─── Logger ───
|
|
7
7
|
const logger = createLogger('cli-suggestions');
|
|
8
8
|
// ─── Formatters ───
|
|
@@ -72,12 +72,8 @@ export function registerSuggestionsCommand(program) {
|
|
|
72
72
|
.option('--offset <n>', 'Pagination offset', parseInt, 0)
|
|
73
73
|
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
74
74
|
.action(async (options) => {
|
|
75
|
-
if (!(await
|
|
76
|
-
console.error(chalk.yellow('This command requires a remote API connection.'));
|
|
77
|
-
console.error(chalk.dim(' Run: trading-boy login or trading-boy subscribe'));
|
|
78
|
-
process.exitCode = 1;
|
|
75
|
+
if (!(await ensureRemote()))
|
|
79
76
|
return;
|
|
80
|
-
}
|
|
81
77
|
try {
|
|
82
78
|
const params = new URLSearchParams();
|
|
83
79
|
params.set('status', options.status);
|
|
@@ -106,12 +102,8 @@ export function registerSuggestionsCommand(program) {
|
|
|
106
102
|
.description('Approve a suggestion and auto-apply to strategy')
|
|
107
103
|
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
108
104
|
.action(async (id, options) => {
|
|
109
|
-
if (!(await
|
|
110
|
-
console.error(chalk.yellow('This command requires a remote API connection.'));
|
|
111
|
-
console.error(chalk.dim(' Run: trading-boy login or trading-boy subscribe'));
|
|
112
|
-
process.exitCode = 1;
|
|
105
|
+
if (!(await ensureRemote()))
|
|
113
106
|
return;
|
|
114
|
-
}
|
|
115
107
|
try {
|
|
116
108
|
const data = await apiRequest(`/api/v1/suggestions/${encodeURIComponent(id)}/approve`, { method: 'POST' });
|
|
117
109
|
if (options.format === 'json') {
|
|
@@ -134,12 +126,8 @@ export function registerSuggestionsCommand(program) {
|
|
|
134
126
|
.description('Reject a suggestion')
|
|
135
127
|
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
136
128
|
.action(async (id, options) => {
|
|
137
|
-
if (!(await
|
|
138
|
-
console.error(chalk.yellow('This command requires a remote API connection.'));
|
|
139
|
-
console.error(chalk.dim(' Run: trading-boy login or trading-boy subscribe'));
|
|
140
|
-
process.exitCode = 1;
|
|
129
|
+
if (!(await ensureRemote()))
|
|
141
130
|
return;
|
|
142
|
-
}
|
|
143
131
|
try {
|
|
144
132
|
const data = await apiRequest(`/api/v1/suggestions/${encodeURIComponent(id)}/reject`, { method: 'POST' });
|
|
145
133
|
if (options.format === 'json') {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Option } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { createLogger } from '@trading-boy/core';
|
|
4
|
-
import {
|
|
4
|
+
import { apiRequest, ApiError } from '../api-client.js';
|
|
5
|
+
import { ensureRemote } from '../utils.js';
|
|
5
6
|
// ─── Logger ───
|
|
6
7
|
const logger = createLogger('cli-thesis');
|
|
7
8
|
// ─── BYOK Error Hint ───
|
|
@@ -74,12 +75,8 @@ export function registerThesisCommand(program) {
|
|
|
74
75
|
.option('--entry-price <n>', 'Entry price', parseFloat)
|
|
75
76
|
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
76
77
|
.action(async (decisionId, options) => {
|
|
77
|
-
if (!(await
|
|
78
|
-
console.error(chalk.yellow('Thesis extraction requires a remote API connection.'));
|
|
79
|
-
console.error(chalk.dim(' Run: trading-boy login'));
|
|
80
|
-
process.exitCode = 1;
|
|
78
|
+
if (!(await ensureRemote()))
|
|
81
79
|
return;
|
|
82
|
-
}
|
|
83
80
|
// Validate direction if provided
|
|
84
81
|
if (options.direction && !['LONG', 'SHORT'].includes(options.direction.toUpperCase())) {
|
|
85
82
|
console.error(chalk.red('Error: --direction must be LONG or SHORT'));
|
package/dist/commands/whoami.js
CHANGED
|
@@ -2,6 +2,7 @@ import { Option } from 'commander';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { createLogger } from '@trading-boy/core';
|
|
4
4
|
import { loadCredentials, redactApiKey } from '../credentials.js';
|
|
5
|
+
import { getApiBase } from '../api-client.js';
|
|
5
6
|
const logger = createLogger('cli-whoami');
|
|
6
7
|
// ─── Whoami Logic ───
|
|
7
8
|
export async function executeWhoami() {
|
|
@@ -59,6 +60,7 @@ export function formatWhoamiOutput(result) {
|
|
|
59
60
|
const date = new Date(result.storedAt).toLocaleString();
|
|
60
61
|
lines.push(` ${chalk.gray('Since:')} ${date}`);
|
|
61
62
|
}
|
|
63
|
+
lines.push(` ${chalk.gray('API:')} ${getApiBase()}`);
|
|
62
64
|
lines.push('');
|
|
63
65
|
return lines.join('\n');
|
|
64
66
|
}
|
|
@@ -86,7 +88,7 @@ export function registerWhoamiCommand(program) {
|
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
90
|
if (options.format === 'json') {
|
|
89
|
-
console.log(JSON.stringify(result, null, 2));
|
|
91
|
+
console.log(JSON.stringify({ ...result, apiUrl: getApiBase() }, null, 2));
|
|
90
92
|
}
|
|
91
93
|
else {
|
|
92
94
|
console.log(formatWhoamiOutput(result));
|
package/dist/utils.d.ts
CHANGED
|
@@ -48,4 +48,16 @@ export declare function formatDate(isoString: string | null): string;
|
|
|
48
48
|
* Parse a string option as an integer.
|
|
49
49
|
*/
|
|
50
50
|
export declare function parseIntOption(value: string): number;
|
|
51
|
+
/**
|
|
52
|
+
* Centralized API error handler for CLI commands.
|
|
53
|
+
* Prints user-friendly error messages and sets appropriate exit codes.
|
|
54
|
+
*/
|
|
55
|
+
export declare function handleApiError(error: unknown, context: string, logger: {
|
|
56
|
+
error: (obj: object, msg: string) => void;
|
|
57
|
+
}): void;
|
|
58
|
+
/**
|
|
59
|
+
* Guard that checks if the CLI is in remote API mode.
|
|
60
|
+
* Returns false (and prints guidance) if not authenticated.
|
|
61
|
+
*/
|
|
62
|
+
export declare function ensureRemote(): Promise<boolean>;
|
|
51
63
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist/utils.js
CHANGED
|
@@ -115,4 +115,55 @@ export function formatDate(isoString) {
|
|
|
115
115
|
export function parseIntOption(value) {
|
|
116
116
|
return parseInt(value, 10);
|
|
117
117
|
}
|
|
118
|
+
function isApiError(error) {
|
|
119
|
+
return error instanceof Error && error.name === 'ApiError' && 'status' in error;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Centralized API error handler for CLI commands.
|
|
123
|
+
* Prints user-friendly error messages and sets appropriate exit codes.
|
|
124
|
+
*/
|
|
125
|
+
export function handleApiError(error, context, logger) {
|
|
126
|
+
if (isApiError(error)) {
|
|
127
|
+
switch (error.status) {
|
|
128
|
+
case 401:
|
|
129
|
+
console.error(chalk.red('Error: Not authenticated. Run `trading-boy login` or `trading-boy subscribe` to get started.'));
|
|
130
|
+
break;
|
|
131
|
+
case 403:
|
|
132
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
133
|
+
break;
|
|
134
|
+
case 404:
|
|
135
|
+
console.error(chalk.red(`Error: Not found — ${error.message}`));
|
|
136
|
+
break;
|
|
137
|
+
case 422:
|
|
138
|
+
console.error(chalk.red(`Error: Validation failed — ${error.message}`));
|
|
139
|
+
break;
|
|
140
|
+
case 429:
|
|
141
|
+
console.error(chalk.red(`Error: Limit reached — ${error.message}`));
|
|
142
|
+
break;
|
|
143
|
+
default:
|
|
144
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
149
|
+
logger.error({ error: message }, context);
|
|
150
|
+
console.error(chalk.red(`Error: ${message}`));
|
|
151
|
+
}
|
|
152
|
+
process.exitCode = isApiError(error) ? 2 : 1;
|
|
153
|
+
}
|
|
154
|
+
// ─── Shared Remote Guard ───
|
|
155
|
+
/**
|
|
156
|
+
* Guard that checks if the CLI is in remote API mode.
|
|
157
|
+
* Returns false (and prints guidance) if not authenticated.
|
|
158
|
+
*/
|
|
159
|
+
export async function ensureRemote() {
|
|
160
|
+
const { isRemoteMode } = await import('./api-client.js');
|
|
161
|
+
if (!(await isRemoteMode())) {
|
|
162
|
+
console.error(chalk.yellow('This command requires a remote API connection.'));
|
|
163
|
+
console.error(chalk.dim(' Run: trading-boy login or trading-boy subscribe'));
|
|
164
|
+
process.exitCode = 1;
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
118
169
|
//# sourceMappingURL=utils.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trading-boy/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Trading Boy CLI — crypto context intelligence for traders and AI agents. Query real-time prices, funding rates, whale activity, and DeFi risk for 100+ Solana tokens and 229 Hyperliquid perpetuals.",
|
|
5
5
|
"homepage": "https://cabal.ventures",
|
|
6
6
|
"repository": {
|