@trading-boy/cli 1.5.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -51201,6 +51201,12 @@ function contextToQueryResult(pkg) {
51201
51201
  function registerQueryCommand(program2) {
51202
51202
  program2.command("query <symbol>").description("Query token info, price, funding rate, and whale activity").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (symbol2, options) => {
51203
51203
  try {
51204
+ if (!await isRemoteMode()) {
51205
+ console.error(source_default.yellow("This command requires a remote API connection."));
51206
+ console.error(source_default.dim(" Run: trading-boy login"));
51207
+ process.exitCode = 1;
51208
+ return;
51209
+ }
51204
51210
  const pkg = await apiRequest(`/api/v1/tokens/${encodeURIComponent(symbol2.toUpperCase())}/context`);
51205
51211
  const result = contextToQueryResult(pkg);
51206
51212
  if (result.token.name === null && result.token.chains.length === 0 && result.price.price === null) {
@@ -51497,6 +51503,12 @@ function buildHistoryQueryParams(options) {
51497
51503
  function registerContextCommand(program2) {
51498
51504
  program2.command("context <symbol>").description("Assemble and display a full ContextPackage for a token").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).option("--at <date>", "Fetch historical context at a specific date/time (e.g., 2026-01-15 or 2026-01-15T12:00:00Z)").option("--from <date>", "Start of time range for context history").option("--to <date>", "End of time range for context history").addOption(new Option("--interval <interval>", "Sampling interval for range queries").choices(["15m", "1h", "4h", "1d"])).option("--trader-id <id>", "Trader ID for personalized context").action(async (symbol2, options) => {
51499
51505
  try {
51506
+ if (!await isRemoteMode()) {
51507
+ console.error(source_default.yellow("This command requires a remote API connection."));
51508
+ console.error(source_default.dim(" Run: trading-boy login"));
51509
+ process.exitCode = 1;
51510
+ return;
51511
+ }
51500
51512
  if (options.from && !options.to || !options.from && options.to) {
51501
51513
  console.error(source_default.red("Error: Both --from and --to must be provided for range queries."));
51502
51514
  process.exitCode = 1;
@@ -51533,6 +51545,12 @@ function registerContextCommand(program2) {
51533
51545
  } else {
51534
51546
  const traderQs = options.traderId ? `?traderId=${encodeURIComponent(options.traderId)}` : "";
51535
51547
  const pkg = await apiRequest(`/api/v1/tokens/${encodeURIComponent(symbol2.toUpperCase())}/context${traderQs}`);
51548
+ if (pkg.token.name == null && (!Array.isArray(pkg.token.chains) || pkg.token.chains.length === 0) && pkg.market?.price == null) {
51549
+ console.error(source_default.red(`Error: Token not found: ${symbol2.toUpperCase()}`));
51550
+ console.error(source_default.dim(" Check the symbol and try again. Use a symbol like SOL, JUP, or BONK."));
51551
+ process.exitCode = 1;
51552
+ return;
51553
+ }
51536
51554
  if (options.format === "json") {
51537
51555
  console.log(JSON.stringify(pkg, null, 2));
51538
51556
  } else {
@@ -51805,6 +51823,12 @@ function registerRiskCommand(program2) {
51805
51823
  process.exitCode = 1;
51806
51824
  return;
51807
51825
  }
51826
+ if (!await isRemoteMode()) {
51827
+ console.error(source_default.yellow("This command requires a remote API connection."));
51828
+ console.error(source_default.dim(" Run: trading-boy login or trading-boy subscribe"));
51829
+ process.exitCode = 1;
51830
+ return;
51831
+ }
51808
51832
  try {
51809
51833
  const apiResult = await apiRequest(`/api/v1/protocols/${encodeURIComponent(protocol.toLowerCase())}/risk`);
51810
51834
  const result = {
@@ -51981,6 +52005,12 @@ function registerDecisionsCommand(program2) {
51981
52005
  }
51982
52006
  } catch (error49) {
51983
52007
  const message = error49 instanceof ApiError ? error49.message : error49 instanceof Error ? error49.message : String(error49);
52008
+ const connErr = formatConnectionError(message);
52009
+ if (connErr) {
52010
+ console.error(connErr);
52011
+ process.exitCode = 1;
52012
+ return;
52013
+ }
51984
52014
  logger11.error({ error: message }, "Failed to fetch decisions");
51985
52015
  console.error(source_default.red(`Error: ${message}`));
51986
52016
  process.exitCode = error49 instanceof ApiError ? 2 : 1;
@@ -52011,6 +52041,12 @@ function registerDecisionsCommand(program2) {
52011
52041
  }
52012
52042
  } catch (error49) {
52013
52043
  const message = error49 instanceof ApiError ? error49.message : error49 instanceof Error ? error49.message : String(error49);
52044
+ const connErr = formatConnectionError(message);
52045
+ if (connErr) {
52046
+ console.error(connErr);
52047
+ process.exitCode = 1;
52048
+ return;
52049
+ }
52014
52050
  logger11.error({ error: message }, "Failed to fetch stats");
52015
52051
  console.error(source_default.red(`Error: ${message}`));
52016
52052
  process.exitCode = error49 instanceof ApiError ? 2 : 1;
@@ -53741,6 +53777,12 @@ function registerJournalCommand(program2) {
53741
53777
  console.log(`Entry logged: id=${result.id} hash=${result.hash}`);
53742
53778
  } catch (error49) {
53743
53779
  const message = error49 instanceof ApiError ? error49.message : error49 instanceof Error ? error49.message : String(error49);
53780
+ const connErr = formatConnectionError(message);
53781
+ if (connErr) {
53782
+ console.error(connErr);
53783
+ process.exitCode = 1;
53784
+ return;
53785
+ }
53744
53786
  console.error(`Error: ${message}`);
53745
53787
  if (error49 instanceof ApiError && error49.body && typeof error49.body === "object") {
53746
53788
  const detail = error49.body.error ?? error49.body.code;
@@ -53773,6 +53815,12 @@ function registerJournalCommand(program2) {
53773
53815
  console.log(`Exit logged: id=${result.id} hash=${result.hash}`);
53774
53816
  } catch (error49) {
53775
53817
  const message = error49 instanceof ApiError ? error49.message : error49 instanceof Error ? error49.message : String(error49);
53818
+ const connErr = formatConnectionError(message);
53819
+ if (connErr) {
53820
+ console.error(connErr);
53821
+ process.exitCode = 1;
53822
+ return;
53823
+ }
53776
53824
  console.error(`Error: ${message}`);
53777
53825
  if (error49 instanceof ApiError && error49.body && typeof error49.body === "object") {
53778
53826
  const detail = error49.body.error ?? error49.body.code;
@@ -53817,6 +53865,12 @@ function registerJournalCommand(program2) {
53817
53865
  console.log(`Total: ${decisions.length} decision(s)`);
53818
53866
  } catch (error49) {
53819
53867
  const message = error49 instanceof ApiError ? error49.message : error49 instanceof Error ? error49.message : String(error49);
53868
+ const connErr = formatConnectionError(message);
53869
+ if (connErr) {
53870
+ console.error(connErr);
53871
+ process.exitCode = 1;
53872
+ return;
53873
+ }
53820
53874
  console.error(`Error: ${message}`);
53821
53875
  if (error49 instanceof ApiError && error49.body && typeof error49.body === "object") {
53822
53876
  const detail = error49.body.error ?? error49.body.code;
@@ -53853,6 +53907,12 @@ function registerJournalCommand(program2) {
53853
53907
  }
53854
53908
  } catch (error49) {
53855
53909
  const message = error49 instanceof ApiError ? error49.message : error49 instanceof Error ? error49.message : String(error49);
53910
+ const connErr = formatConnectionError(message);
53911
+ if (connErr) {
53912
+ console.error(connErr);
53913
+ process.exitCode = 1;
53914
+ return;
53915
+ }
53856
53916
  console.error(`Error: ${message}`);
53857
53917
  process.exitCode = error49 instanceof ApiError ? 2 : 1;
53858
53918
  }
@@ -53875,6 +53935,12 @@ function registerJournalCommand(program2) {
53875
53935
  }
53876
53936
  } catch (error49) {
53877
53937
  const message = error49 instanceof ApiError ? error49.message : error49 instanceof Error ? error49.message : String(error49);
53938
+ const connErr = formatConnectionError(message);
53939
+ if (connErr) {
53940
+ console.error(connErr);
53941
+ process.exitCode = 1;
53942
+ return;
53943
+ }
53878
53944
  console.error(`Error: ${message}`);
53879
53945
  process.exitCode = error49 instanceof ApiError ? 2 : 1;
53880
53946
  }
@@ -56648,6 +56714,12 @@ function registerSuggestionsCommand(program2) {
56648
56714
  cmd.help();
56649
56715
  });
56650
56716
  cmd.command("list").description("List strategy suggestions").option("--status <status>", "Filter by status: pending, approved, rejected", "pending").option("--strategy <id>", "Filter by strategy ID").option("--limit <n>", "Maximum results", parseInt, 20).option("--offset <n>", "Pagination offset", parseInt, 0).addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (options) => {
56717
+ if (!await isRemoteMode()) {
56718
+ console.error(source_default.yellow("This command requires a remote API connection."));
56719
+ console.error(source_default.dim(" Run: trading-boy login or trading-boy subscribe"));
56720
+ process.exitCode = 1;
56721
+ return;
56722
+ }
56651
56723
  try {
56652
56724
  const params = new URLSearchParams();
56653
56725
  params.set("status", options.status);
@@ -56669,6 +56741,12 @@ function registerSuggestionsCommand(program2) {
56669
56741
  }
56670
56742
  });
56671
56743
  cmd.command("approve <id>").description("Approve a suggestion and auto-apply to strategy").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (id, options) => {
56744
+ if (!await isRemoteMode()) {
56745
+ console.error(source_default.yellow("This command requires a remote API connection."));
56746
+ console.error(source_default.dim(" Run: trading-boy login or trading-boy subscribe"));
56747
+ process.exitCode = 1;
56748
+ return;
56749
+ }
56672
56750
  try {
56673
56751
  const data = await apiRequest(`/api/v1/suggestions/${encodeURIComponent(id)}/approve`, { method: "POST" });
56674
56752
  if (options.format === "json") {
@@ -56686,6 +56764,12 @@ function registerSuggestionsCommand(program2) {
56686
56764
  }
56687
56765
  });
56688
56766
  cmd.command("reject <id>").description("Reject a suggestion").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (id, options) => {
56767
+ if (!await isRemoteMode()) {
56768
+ console.error(source_default.yellow("This command requires a remote API connection."));
56769
+ console.error(source_default.dim(" Run: trading-boy login or trading-boy subscribe"));
56770
+ process.exitCode = 1;
56771
+ return;
56772
+ }
56689
56773
  try {
56690
56774
  const data = await apiRequest(`/api/v1/suggestions/${encodeURIComponent(id)}/reject`, { method: "POST" });
56691
56775
  if (options.format === "json") {
@@ -57053,9 +57137,21 @@ function parseHumanInterval(value) {
57053
57137
  var MIN_SCAN_INTERVAL_MS = 6e4;
57054
57138
  function registerAgentCommand(program2) {
57055
57139
  const agent = program2.command("agent").description("Manage autonomous trading agents");
57056
- agent.command("create").description("Create a new agent").requiredOption("--trader-id <traderId>", "Trader ID").requiredOption("--strategy-id <strategyId>", "Strategy ID").option("--name <name>", "Agent name").option("--autonomy <level>", "Autonomy level: OBSERVE_ONLY, SUGGEST, AUTO_WITH_APPROVAL, FULLY_AUTONOMOUS", "OBSERVE_ONLY").option("--scan-interval <ms>", "Scan interval in ms (min 60000)", "300000").option("--scan-interval-human <duration>", "Scan interval in human-readable format (e.g. 1m, 5m, 15m, 30m, 1h)").option("--watchlist <symbols>", "Comma-separated token symbols").option("--max-daily-trades <n>", "Max daily trades", "10").option("--max-daily-loss <usd>", "Max daily loss in USD", "500").option("--max-position-size <pct>", "Max position size as decimal (0.10 = 10%)", "0.10").option("--min-confidence <n>", "Min confidence threshold (0-1)", "0.60").option("--scan-model <model>", "LLM model for market scanning").option("--analyze-model <model>", "LLM model for deep analysis").option("--decide-model <model>", "LLM model for trade decisions").addOption(new Option("--asset-class <class>", "Asset class for this agent").choices(["crypto", "commodities", "mixed"]).default("crypto")).option("--soul-override <text>", "Custom soul/personality for this agent").option("--purpose-override <text>", "Custom purpose/mission for this agent").option("--soul-file <path>", "Load soul from a file").option("--purpose-file <path>", "Load purpose from a file").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (options) => {
57140
+ agent.command("create").description("Create a new agent").option("--trader-id <traderId>", "Trader ID").option("--strategy-id <strategyId>", "Strategy ID").option("--name <name>", "Agent name").option("--autonomy <level>", "Autonomy level: OBSERVE_ONLY, SUGGEST, AUTO_WITH_APPROVAL, FULLY_AUTONOMOUS", "OBSERVE_ONLY").option("--scan-interval <ms>", "Scan interval in ms (min 60000)", "300000").option("--scan-interval-human <duration>", "Scan interval in human-readable format (e.g. 1m, 5m, 15m, 30m, 1h)").option("--watchlist <symbols>", "Comma-separated token symbols").option("--max-daily-trades <n>", "Max daily trades", "10").option("--max-daily-loss <usd>", "Max daily loss in USD", "500").option("--max-position-size <pct>", "Max position size as decimal (0.10 = 10%)", "0.10").option("--min-confidence <n>", "Min confidence threshold (0-1)", "0.60").option("--scan-model <model>", "LLM model for market scanning").option("--analyze-model <model>", "LLM model for deep analysis").option("--decide-model <model>", "LLM model for trade decisions").addOption(new Option("--asset-class <class>", "Asset class for this agent").choices(["crypto", "commodities", "mixed"]).default("crypto")).option("--soul-override <text>", "Custom soul/personality for this agent").option("--purpose-override <text>", "Custom purpose/mission for this agent").option("--soul-file <path>", "Load soul from a file").option("--purpose-file <path>", "Load purpose from a file").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (options) => {
57057
57141
  if (!await ensureRemote2())
57058
57142
  return;
57143
+ if (!options.traderId) {
57144
+ console.error(source_default.red("Error: --trader-id is required."));
57145
+ console.log(source_default.dim(" Find yours with: trading-boy trader list"));
57146
+ process.exitCode = 1;
57147
+ return;
57148
+ }
57149
+ if (!options.strategyId) {
57150
+ console.error(source_default.red("Error: --strategy-id is required."));
57151
+ console.log(source_default.dim(" Find yours with: trading-boy strategy list"));
57152
+ process.exitCode = 1;
57153
+ return;
57154
+ }
57059
57155
  const body = {
57060
57156
  traderId: options.traderId,
57061
57157
  strategyId: options.strategyId
@@ -57154,7 +57250,13 @@ function registerAgentCommand(program2) {
57154
57250
  return;
57155
57251
  }
57156
57252
  if (result.agents.length === 0) {
57157
- console.log(source_default.dim(" No agents found"));
57253
+ console.log(source_default.dim(" No agents found."));
57254
+ console.log("");
57255
+ console.log(source_default.dim(" Create one with:"));
57256
+ console.log(source_default.dim(" trading-boy agent create --trader-id <id> --strategy-id <id>"));
57257
+ console.log(source_default.dim(" Find your IDs with:"));
57258
+ console.log(source_default.dim(" trading-boy trader list"));
57259
+ console.log(source_default.dim(" trading-boy strategy list"));
57158
57260
  return;
57159
57261
  }
57160
57262
  console.log("");
@@ -114,8 +114,8 @@ export function registerAgentCommand(program) {
114
114
  agent
115
115
  .command('create')
116
116
  .description('Create a new agent')
117
- .requiredOption('--trader-id <traderId>', 'Trader ID')
118
- .requiredOption('--strategy-id <strategyId>', 'Strategy ID')
117
+ .option('--trader-id <traderId>', 'Trader ID')
118
+ .option('--strategy-id <strategyId>', 'Strategy ID')
119
119
  .option('--name <name>', 'Agent name')
120
120
  .option('--autonomy <level>', 'Autonomy level: OBSERVE_ONLY, SUGGEST, AUTO_WITH_APPROVAL, FULLY_AUTONOMOUS', 'OBSERVE_ONLY')
121
121
  .option('--scan-interval <ms>', 'Scan interval in ms (min 60000)', '300000')
@@ -137,6 +137,18 @@ export function registerAgentCommand(program) {
137
137
  .action(async (options) => {
138
138
  if (!(await ensureRemote()))
139
139
  return;
140
+ if (!options.traderId) {
141
+ console.error(chalk.red('Error: --trader-id is required.'));
142
+ console.log(chalk.dim(' Find yours with: trading-boy trader list'));
143
+ process.exitCode = 1;
144
+ return;
145
+ }
146
+ if (!options.strategyId) {
147
+ console.error(chalk.red('Error: --strategy-id is required.'));
148
+ console.log(chalk.dim(' Find yours with: trading-boy strategy list'));
149
+ process.exitCode = 1;
150
+ return;
151
+ }
140
152
  const body = {
141
153
  traderId: options.traderId,
142
154
  strategyId: options.strategyId,
@@ -249,7 +261,13 @@ export function registerAgentCommand(program) {
249
261
  return;
250
262
  }
251
263
  if (result.agents.length === 0) {
252
- console.log(chalk.dim(' No agents found'));
264
+ console.log(chalk.dim(' No agents found.'));
265
+ console.log('');
266
+ console.log(chalk.dim(' Create one with:'));
267
+ console.log(chalk.dim(' trading-boy agent create --trader-id <id> --strategy-id <id>'));
268
+ console.log(chalk.dim(' Find your IDs with:'));
269
+ console.log(chalk.dim(' trading-boy trader list'));
270
+ console.log(chalk.dim(' trading-boy strategy list'));
253
271
  return;
254
272
  }
255
273
  console.log('');
@@ -2,7 +2,7 @@ import { Option } from 'commander';
2
2
  import chalk from 'chalk';
3
3
  import { createLogger } from '@trading-boy/core';
4
4
  import { formatConnectionError, colorChange, colorSentiment, colorRiskScore, formatUsd } from '../utils.js';
5
- import { apiRequest, ApiError } from '../api-client.js';
5
+ import { isRemoteMode, apiRequest, ApiError } from '../api-client.js';
6
6
  // ─── Logger ───
7
7
  const logger = createLogger('cli-context');
8
8
  // ─── Formatters ───
@@ -324,6 +324,13 @@ export function registerContextCommand(program) {
324
324
  .option('--trader-id <id>', 'Trader ID for personalized context')
325
325
  .action(async (symbol, options) => {
326
326
  try {
327
+ // ─── Auth pre-flight ───
328
+ if (!(await isRemoteMode())) {
329
+ console.error(chalk.yellow('This command requires a remote API connection.'));
330
+ console.error(chalk.dim(' Run: trading-boy login'));
331
+ process.exitCode = 1;
332
+ return;
333
+ }
327
334
  // ─── Validate range options ───
328
335
  if ((options.from && !options.to) || (!options.from && options.to)) {
329
336
  console.error(chalk.red('Error: Both --from and --to must be provided for range queries.'));
@@ -368,6 +375,13 @@ export function registerContextCommand(program) {
368
375
  // Live context
369
376
  const traderQs = options.traderId ? `?traderId=${encodeURIComponent(options.traderId)}` : '';
370
377
  const pkg = await apiRequest(`/api/v1/tokens/${encodeURIComponent(symbol.toUpperCase())}/context${traderQs}`);
378
+ // Check if the token was actually found
379
+ if (pkg.token.name == null && (!Array.isArray(pkg.token.chains) || pkg.token.chains.length === 0) && pkg.market?.price == null) {
380
+ console.error(chalk.red(`Error: Token not found: ${symbol.toUpperCase()}`));
381
+ console.error(chalk.dim(' Check the symbol and try again. Use a symbol like SOL, JUP, or BONK.'));
382
+ process.exitCode = 1;
383
+ return;
384
+ }
371
385
  if (options.format === 'json') {
372
386
  console.log(JSON.stringify(pkg, null, 2));
373
387
  }
@@ -1,7 +1,7 @@
1
1
  import { Option } from 'commander';
2
2
  import chalk from 'chalk';
3
3
  import { createLogger } from '@trading-boy/core';
4
- import { padRight, parseIntOption, formatDate } from '../utils.js';
4
+ import { padRight, parseIntOption, formatDate, formatConnectionError } from '../utils.js';
5
5
  import { apiRequest, ApiError } from '../api-client.js';
6
6
  // ─── Logger ───
7
7
  const logger = createLogger('cli-decisions');
@@ -222,6 +222,12 @@ export function registerDecisionsCommand(program) {
222
222
  }
223
223
  catch (error) {
224
224
  const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
225
+ const connErr = formatConnectionError(message);
226
+ if (connErr) {
227
+ console.error(connErr);
228
+ process.exitCode = 1;
229
+ return;
230
+ }
225
231
  logger.error({ error: message }, 'Failed to fetch decisions');
226
232
  console.error(chalk.red(`Error: ${message}`));
227
233
  process.exitCode = error instanceof ApiError ? 2 : 1;
@@ -262,6 +268,12 @@ export function registerDecisionsCommand(program) {
262
268
  }
263
269
  catch (error) {
264
270
  const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
271
+ const connErr = formatConnectionError(message);
272
+ if (connErr) {
273
+ console.error(connErr);
274
+ process.exitCode = 1;
275
+ return;
276
+ }
265
277
  logger.error({ error: message }, 'Failed to fetch stats');
266
278
  console.error(chalk.red(`Error: ${message}`));
267
279
  process.exitCode = error instanceof ApiError ? 2 : 1;
@@ -1,5 +1,5 @@
1
1
  import { Option } from 'commander';
2
- import { padRight } from '../utils.js';
2
+ import { padRight, formatConnectionError } from '../utils.js';
3
3
  import { apiRequest, ApiError } from '../api-client.js';
4
4
  import { registerReviewCommand } from './review.js';
5
5
  // ─── Default Trader ───
@@ -73,6 +73,12 @@ export function registerJournalCommand(program) {
73
73
  }
74
74
  catch (error) {
75
75
  const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
76
+ const connErr = formatConnectionError(message);
77
+ if (connErr) {
78
+ console.error(connErr);
79
+ process.exitCode = 1;
80
+ return;
81
+ }
76
82
  console.error(`Error: ${message}`);
77
83
  if (error instanceof ApiError && error.body && typeof error.body === 'object') {
78
84
  const detail = error.body.error ?? error.body.code;
@@ -118,6 +124,12 @@ export function registerJournalCommand(program) {
118
124
  }
119
125
  catch (error) {
120
126
  const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
127
+ const connErr = formatConnectionError(message);
128
+ if (connErr) {
129
+ console.error(connErr);
130
+ process.exitCode = 1;
131
+ return;
132
+ }
121
133
  console.error(`Error: ${message}`);
122
134
  if (error instanceof ApiError && error.body && typeof error.body === 'object') {
123
135
  const detail = error.body.error ?? error.body.code;
@@ -185,6 +197,12 @@ export function registerJournalCommand(program) {
185
197
  }
186
198
  catch (error) {
187
199
  const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
200
+ const connErr = formatConnectionError(message);
201
+ if (connErr) {
202
+ console.error(connErr);
203
+ process.exitCode = 1;
204
+ return;
205
+ }
188
206
  console.error(`Error: ${message}`);
189
207
  if (error instanceof ApiError && error.body && typeof error.body === 'object') {
190
208
  const detail = error.body.error ?? error.body.code;
@@ -231,6 +249,12 @@ export function registerJournalCommand(program) {
231
249
  }
232
250
  catch (error) {
233
251
  const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
252
+ const connErr = formatConnectionError(message);
253
+ if (connErr) {
254
+ console.error(connErr);
255
+ process.exitCode = 1;
256
+ return;
257
+ }
234
258
  console.error(`Error: ${message}`);
235
259
  process.exitCode = error instanceof ApiError ? 2 : 1;
236
260
  }
@@ -263,6 +287,12 @@ export function registerJournalCommand(program) {
263
287
  }
264
288
  catch (error) {
265
289
  const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
290
+ const connErr = formatConnectionError(message);
291
+ if (connErr) {
292
+ console.error(connErr);
293
+ process.exitCode = 1;
294
+ return;
295
+ }
266
296
  console.error(`Error: ${message}`);
267
297
  process.exitCode = error instanceof ApiError ? 2 : 1;
268
298
  }
@@ -2,7 +2,7 @@ import { Option } from 'commander';
2
2
  import chalk from 'chalk';
3
3
  import { createLogger } from '@trading-boy/core';
4
4
  import { formatConnectionError } from '../utils.js';
5
- import { apiRequest, ApiError } from '../api-client.js';
5
+ import { isRemoteMode, apiRequest, ApiError } from '../api-client.js';
6
6
  // ─── Logger ───
7
7
  const logger = createLogger('cli-query');
8
8
  // ─── Formatter ───
@@ -102,6 +102,13 @@ export function registerQueryCommand(program) {
102
102
  .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
103
103
  .action(async (symbol, options) => {
104
104
  try {
105
+ // ─── Auth pre-flight ───
106
+ if (!(await isRemoteMode())) {
107
+ console.error(chalk.yellow('This command requires a remote API connection.'));
108
+ console.error(chalk.dim(' Run: trading-boy login'));
109
+ process.exitCode = 1;
110
+ return;
111
+ }
105
112
  const pkg = await apiRequest(`/api/v1/tokens/${encodeURIComponent(symbol.toUpperCase())}/context`);
106
113
  const result = contextToQueryResult(pkg);
107
114
  // Check if the token was actually found
@@ -2,7 +2,7 @@ import { Option } from 'commander';
2
2
  import chalk from 'chalk';
3
3
  import { createLogger } from '@trading-boy/core';
4
4
  import { formatConnectionError, padRight } from '../utils.js';
5
- import { apiRequest, ApiError } from '../api-client.js';
5
+ import { isRemoteMode, apiRequest, ApiError } from '../api-client.js';
6
6
  // ─── Logger ───
7
7
  const logger = createLogger('cli-risk');
8
8
  // ─── Formatters ───
@@ -127,6 +127,12 @@ export function registerRiskCommand(program) {
127
127
  process.exitCode = 1;
128
128
  return;
129
129
  }
130
+ if (!(await isRemoteMode())) {
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;
134
+ return;
135
+ }
130
136
  try {
131
137
  const apiResult = await apiRequest(`/api/v1/protocols/${encodeURIComponent(protocol.toLowerCase())}/risk`);
132
138
  const result = {
@@ -1,7 +1,7 @@
1
1
  import { Option } from 'commander';
2
2
  import chalk from 'chalk';
3
3
  import { createLogger } from '@trading-boy/core';
4
- import { apiRequest, ApiError } from '../api-client.js';
4
+ import { isRemoteMode, apiRequest, ApiError } from '../api-client.js';
5
5
  import { padRight, truncate, formatDate } from '../utils.js';
6
6
  // ─── Logger ───
7
7
  const logger = createLogger('cli-suggestions');
@@ -72,6 +72,12 @@ 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 isRemoteMode())) {
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;
79
+ return;
80
+ }
75
81
  try {
76
82
  const params = new URLSearchParams();
77
83
  params.set('status', options.status);
@@ -100,6 +106,12 @@ export function registerSuggestionsCommand(program) {
100
106
  .description('Approve a suggestion and auto-apply to strategy')
101
107
  .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
102
108
  .action(async (id, options) => {
109
+ if (!(await isRemoteMode())) {
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;
113
+ return;
114
+ }
103
115
  try {
104
116
  const data = await apiRequest(`/api/v1/suggestions/${encodeURIComponent(id)}/approve`, { method: 'POST' });
105
117
  if (options.format === 'json') {
@@ -122,6 +134,12 @@ export function registerSuggestionsCommand(program) {
122
134
  .description('Reject a suggestion')
123
135
  .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
124
136
  .action(async (id, options) => {
137
+ if (!(await isRemoteMode())) {
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;
141
+ return;
142
+ }
125
143
  try {
126
144
  const data = await apiRequest(`/api/v1/suggestions/${encodeURIComponent(id)}/reject`, { method: 'POST' });
127
145
  if (options.format === 'json') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trading-boy/cli",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
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": {