@trading-boy/cli 1.6.1 → 1.7.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.
@@ -38623,98 +38623,6 @@ var init_ora = __esm({
38623
38623
  }
38624
38624
  });
38625
38625
 
38626
- // dist/utils.js
38627
- var utils_exports = {};
38628
- __export(utils_exports, {
38629
- colorChange: () => colorChange,
38630
- colorRiskScore: () => colorRiskScore,
38631
- colorSentiment: () => colorSentiment,
38632
- createSpinner: () => createSpinner,
38633
- formatConnectionError: () => formatConnectionError,
38634
- formatDate: () => formatDate,
38635
- formatUsd: () => formatUsd,
38636
- isInteractive: () => isInteractive2,
38637
- padRight: () => padRight,
38638
- parseIntOption: () => parseIntOption,
38639
- truncate: () => truncate
38640
- });
38641
- function isInteractive2() {
38642
- return !!process.stdout.isTTY;
38643
- }
38644
- async function createSpinner(text) {
38645
- const { default: ora2 } = await Promise.resolve().then(() => (init_ora(), ora_exports));
38646
- return ora2({ text, isSilent: !isInteractive2() });
38647
- }
38648
- function formatConnectionError(message) {
38649
- const lower = message.toLowerCase();
38650
- const isConnectionError = lower.includes("econnrefused") || lower.includes("serviceunavailable") || lower.includes("failed to connect") || lower.includes("connection refused") || lower.includes("max retries per request limit exceeded") || lower.includes("enotfound");
38651
- if (!isConnectionError)
38652
- return null;
38653
- return [
38654
- source_default.dim(" \u2192 Is the infrastructure running? Try: trading-boy infra status"),
38655
- source_default.dim(" \u2192 To start: pnpm infra:up")
38656
- ].join("\n");
38657
- }
38658
- function padRight(str2, len) {
38659
- return str2.length >= len ? str2.slice(0, len) : str2 + " ".repeat(len - str2.length);
38660
- }
38661
- function truncate(str2, maxLen) {
38662
- return str2.length > maxLen ? str2.slice(0, maxLen - 3) + "..." : str2;
38663
- }
38664
- function colorChange(value) {
38665
- const sign = value >= 0 ? "+" : "";
38666
- const str2 = `${sign}${value.toFixed(2)}%`;
38667
- return value >= 0 ? source_default.green(str2) : source_default.red(str2);
38668
- }
38669
- function colorSentiment(value) {
38670
- const sign = value >= 0 ? "+" : "";
38671
- const str2 = `${sign}${value.toFixed(2)}`;
38672
- if (value > 0.3)
38673
- return source_default.green(str2);
38674
- if (value < -0.3)
38675
- return source_default.red(str2);
38676
- return source_default.yellow(str2);
38677
- }
38678
- function colorRiskScore(score) {
38679
- const label = `${score}/100`;
38680
- if (score <= 30)
38681
- return source_default.green(label);
38682
- if (score <= 60)
38683
- return source_default.yellow(label);
38684
- return source_default.red(label);
38685
- }
38686
- function formatUsd(value) {
38687
- if (Math.abs(value) >= 1e9) {
38688
- return `$${(value / 1e9).toFixed(2)}B`;
38689
- }
38690
- if (Math.abs(value) >= 1e6) {
38691
- return `$${(value / 1e6).toFixed(2)}M`;
38692
- }
38693
- if (Math.abs(value) >= 1e3) {
38694
- return `$${(value / 1e3).toFixed(2)}K`;
38695
- }
38696
- return `$${value.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 6 })}`;
38697
- }
38698
- function formatDate(isoString) {
38699
- if (!isoString) {
38700
- return source_default.dim("no date");
38701
- }
38702
- try {
38703
- return new Date(isoString).toISOString().slice(0, 10);
38704
- } catch {
38705
- return isoString;
38706
- }
38707
- }
38708
- function parseIntOption(value) {
38709
- return parseInt(value, 10);
38710
- }
38711
- var init_utils3 = __esm({
38712
- "dist/utils.js"() {
38713
- "use strict";
38714
- init_source();
38715
- }
38716
- });
38717
-
38718
38626
  // dist/credentials.js
38719
38627
  import { homedir } from "node:os";
38720
38628
  import { join } from "node:path";
@@ -38872,6 +38780,16 @@ var init_credentials = __esm({
38872
38780
  });
38873
38781
 
38874
38782
  // dist/api-client.js
38783
+ var api_client_exports = {};
38784
+ __export(api_client_exports, {
38785
+ ApiError: () => ApiError,
38786
+ apiRequest: () => apiRequest,
38787
+ getApiBase: () => getApiBase,
38788
+ isDevMode: () => isDevMode,
38789
+ isRemoteMode: () => isRemoteMode,
38790
+ redactApiKey: () => redactApiKey,
38791
+ resolveApiKey: () => resolveApiKey
38792
+ });
38875
38793
  function isDevMode() {
38876
38794
  if (process.env.NODE_ENV !== "development")
38877
38795
  return false;
@@ -38957,8 +38875,12 @@ async function apiRequest(path5, options = {}) {
38957
38875
  let message = "Subscription inactive. Run `trading-boy billing manage` to update your billing.";
38958
38876
  try {
38959
38877
  const body = await response.clone().json();
38960
- if (typeof body.error === "string")
38961
- message = body.error;
38878
+ if (typeof body.error === "string") {
38879
+ const safePatterns = ["Plan ", "Subscription ", "plan ", "subscription ", "Trial ", "trial "];
38880
+ if (safePatterns.some((p) => body.error.startsWith(p))) {
38881
+ message = body.error;
38882
+ }
38883
+ }
38962
38884
  } catch {
38963
38885
  }
38964
38886
  throw new ApiError(message, 403);
@@ -39012,6 +38934,141 @@ var init_api_client = __esm({
39012
38934
  }
39013
38935
  });
39014
38936
 
38937
+ // dist/utils.js
38938
+ var utils_exports = {};
38939
+ __export(utils_exports, {
38940
+ colorChange: () => colorChange,
38941
+ colorRiskScore: () => colorRiskScore,
38942
+ colorSentiment: () => colorSentiment,
38943
+ createSpinner: () => createSpinner,
38944
+ ensureRemote: () => ensureRemote,
38945
+ formatConnectionError: () => formatConnectionError,
38946
+ formatDate: () => formatDate,
38947
+ formatUsd: () => formatUsd,
38948
+ handleApiError: () => handleApiError,
38949
+ isInteractive: () => isInteractive2,
38950
+ padRight: () => padRight,
38951
+ parseIntOption: () => parseIntOption,
38952
+ truncate: () => truncate
38953
+ });
38954
+ function isInteractive2() {
38955
+ return !!process.stdout.isTTY;
38956
+ }
38957
+ async function createSpinner(text) {
38958
+ const { default: ora2 } = await Promise.resolve().then(() => (init_ora(), ora_exports));
38959
+ return ora2({ text, isSilent: !isInteractive2() });
38960
+ }
38961
+ function formatConnectionError(message) {
38962
+ const lower = message.toLowerCase();
38963
+ const isConnectionError = lower.includes("econnrefused") || lower.includes("serviceunavailable") || lower.includes("failed to connect") || lower.includes("connection refused") || lower.includes("max retries per request limit exceeded") || lower.includes("enotfound");
38964
+ if (!isConnectionError)
38965
+ return null;
38966
+ return [
38967
+ source_default.dim(" \u2192 Is the infrastructure running? Try: trading-boy infra status"),
38968
+ source_default.dim(" \u2192 To start: pnpm infra:up")
38969
+ ].join("\n");
38970
+ }
38971
+ function padRight(str2, len) {
38972
+ return str2.length >= len ? str2.slice(0, len) : str2 + " ".repeat(len - str2.length);
38973
+ }
38974
+ function truncate(str2, maxLen) {
38975
+ return str2.length > maxLen ? str2.slice(0, maxLen - 3) + "..." : str2;
38976
+ }
38977
+ function colorChange(value) {
38978
+ const sign = value >= 0 ? "+" : "";
38979
+ const str2 = `${sign}${value.toFixed(2)}%`;
38980
+ return value >= 0 ? source_default.green(str2) : source_default.red(str2);
38981
+ }
38982
+ function colorSentiment(value) {
38983
+ const sign = value >= 0 ? "+" : "";
38984
+ const str2 = `${sign}${value.toFixed(2)}`;
38985
+ if (value > 0.3)
38986
+ return source_default.green(str2);
38987
+ if (value < -0.3)
38988
+ return source_default.red(str2);
38989
+ return source_default.yellow(str2);
38990
+ }
38991
+ function colorRiskScore(score) {
38992
+ const label = `${score}/100`;
38993
+ if (score <= 30)
38994
+ return source_default.green(label);
38995
+ if (score <= 60)
38996
+ return source_default.yellow(label);
38997
+ return source_default.red(label);
38998
+ }
38999
+ function formatUsd(value) {
39000
+ if (Math.abs(value) >= 1e9) {
39001
+ return `$${(value / 1e9).toFixed(2)}B`;
39002
+ }
39003
+ if (Math.abs(value) >= 1e6) {
39004
+ return `$${(value / 1e6).toFixed(2)}M`;
39005
+ }
39006
+ if (Math.abs(value) >= 1e3) {
39007
+ return `$${(value / 1e3).toFixed(2)}K`;
39008
+ }
39009
+ return `$${value.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 6 })}`;
39010
+ }
39011
+ function formatDate(isoString) {
39012
+ if (!isoString) {
39013
+ return source_default.dim("no date");
39014
+ }
39015
+ try {
39016
+ return new Date(isoString).toISOString().slice(0, 10);
39017
+ } catch {
39018
+ return isoString;
39019
+ }
39020
+ }
39021
+ function parseIntOption(value) {
39022
+ return parseInt(value, 10);
39023
+ }
39024
+ function isApiError(error49) {
39025
+ return error49 instanceof Error && error49.name === "ApiError" && "status" in error49;
39026
+ }
39027
+ function handleApiError(error49, context, logger31) {
39028
+ if (isApiError(error49)) {
39029
+ switch (error49.status) {
39030
+ case 401:
39031
+ console.error(source_default.red("Error: Not authenticated. Run `trading-boy login` or `trading-boy subscribe` to get started."));
39032
+ break;
39033
+ case 403:
39034
+ console.error(source_default.red(`Error: ${error49.message}`));
39035
+ break;
39036
+ case 404:
39037
+ console.error(source_default.red(`Error: Not found \u2014 ${error49.message}`));
39038
+ break;
39039
+ case 422:
39040
+ console.error(source_default.red(`Error: Validation failed \u2014 ${error49.message}`));
39041
+ break;
39042
+ case 429:
39043
+ console.error(source_default.red(`Error: Limit reached \u2014 ${error49.message}`));
39044
+ break;
39045
+ default:
39046
+ console.error(source_default.red(`Error: ${error49.message}`));
39047
+ }
39048
+ } else {
39049
+ const message = error49 instanceof Error ? error49.message : String(error49);
39050
+ logger31.error({ error: message }, context);
39051
+ console.error(source_default.red(`Error: ${message}`));
39052
+ }
39053
+ process.exitCode = isApiError(error49) ? 2 : 1;
39054
+ }
39055
+ async function ensureRemote() {
39056
+ const { isRemoteMode: isRemoteMode2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
39057
+ if (!await isRemoteMode2()) {
39058
+ console.error(source_default.yellow("This command requires a remote API connection."));
39059
+ console.error(source_default.dim(" Run: trading-boy login or trading-boy subscribe"));
39060
+ process.exitCode = 1;
39061
+ return false;
39062
+ }
39063
+ return true;
39064
+ }
39065
+ var init_utils3 = __esm({
39066
+ "dist/utils.js"() {
39067
+ "use strict";
39068
+ init_source();
39069
+ }
39070
+ });
39071
+
39015
39072
  // ../../node_modules/.pnpm/@inquirer+core@10.3.2_@types+node@25.2.3/node_modules/@inquirer/core/dist/esm/lib/key.js
39016
39073
  var isUpKey, isDownKey, isSpaceKey, isBackspaceKey, isTabKey, isNumberKey, isEnterKey;
39017
39074
  var init_key = __esm({
@@ -53236,12 +53293,8 @@ function contextToQueryResult(pkg) {
53236
53293
  function registerQueryCommand(program2) {
53237
53294
  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) => {
53238
53295
  try {
53239
- if (!await isRemoteMode()) {
53240
- console.error(source_default.yellow("This command requires a remote API connection."));
53241
- console.error(source_default.dim(" Run: trading-boy login"));
53242
- process.exitCode = 1;
53296
+ if (!await ensureRemote())
53243
53297
  return;
53244
- }
53245
53298
  const pkg = await apiRequest(`/api/v1/tokens/${encodeURIComponent(symbol2.toUpperCase())}/context`);
53246
53299
  const result = contextToQueryResult(pkg);
53247
53300
  if (result.token.name === null && result.token.chains.length === 0 && result.price.price === null) {
@@ -53541,12 +53594,8 @@ function buildHistoryQueryParams(options) {
53541
53594
  function registerContextCommand(program2) {
53542
53595
  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) => {
53543
53596
  try {
53544
- if (!await isRemoteMode()) {
53545
- console.error(source_default.yellow("This command requires a remote API connection."));
53546
- console.error(source_default.dim(" Run: trading-boy login"));
53547
- process.exitCode = 1;
53597
+ if (!await ensureRemote())
53548
53598
  return;
53549
- }
53550
53599
  if (options.from && !options.to || !options.from && options.to) {
53551
53600
  console.error(source_default.red("Error: Both --from and --to must be provided for range queries."));
53552
53601
  process.exitCode = 1;
@@ -53866,12 +53915,8 @@ function registerRiskCommand(program2) {
53866
53915
  process.exitCode = 1;
53867
53916
  return;
53868
53917
  }
53869
- if (!await isRemoteMode()) {
53870
- console.error(source_default.yellow("This command requires a remote API connection."));
53871
- console.error(source_default.dim(" Run: trading-boy login or trading-boy subscribe"));
53872
- process.exitCode = 1;
53918
+ if (!await ensureRemote())
53873
53919
  return;
53874
- }
53875
53920
  try {
53876
53921
  const apiResult = await apiRequest(`/api/v1/protocols/${encodeURIComponent(protocol.toLowerCase())}/risk`);
53877
53922
  const result = {
@@ -56734,6 +56779,7 @@ function registerLogoutCommand(program2) {
56734
56779
  init_source();
56735
56780
  init_dist2();
56736
56781
  init_credentials();
56782
+ init_api_client();
56737
56783
  var logger21 = createLogger("cli-whoami");
56738
56784
  async function executeWhoami() {
56739
56785
  const envKey = process.env.TRADING_BOY_API_KEY;
@@ -56788,6 +56834,7 @@ function formatWhoamiOutput(result) {
56788
56834
  const date5 = new Date(result.storedAt).toLocaleString();
56789
56835
  lines.push(` ${source_default.gray("Since:")} ${date5}`);
56790
56836
  }
56837
+ lines.push(` ${source_default.gray("API:")} ${getApiBase()}`);
56791
56838
  lines.push("");
56792
56839
  return lines.join("\n");
56793
56840
  }
@@ -56808,7 +56855,7 @@ function registerWhoamiCommand(program2) {
56808
56855
  }
56809
56856
  }
56810
56857
  if (options.format === "json") {
56811
- console.log(JSON.stringify(result, null, 2));
56858
+ console.log(JSON.stringify({ ...result, apiUrl: getApiBase() }, null, 2));
56812
56859
  } else {
56813
56860
  console.log(formatWhoamiOutput(result));
56814
56861
  }
@@ -56827,6 +56874,10 @@ init_dist2();
56827
56874
  init_api_client();
56828
56875
  init_utils3();
56829
56876
  var logger22 = createLogger("cli-billing");
56877
+ var ALLOWED_PORTAL_DOMAINS = /* @__PURE__ */ new Set([
56878
+ "billing.stripe.com",
56879
+ "checkout.stripe.com"
56880
+ ]);
56830
56881
  function formatStatusLabel(status) {
56831
56882
  switch (status) {
56832
56883
  case "active":
@@ -56892,6 +56943,9 @@ function registerBillingCommand(program2) {
56892
56943
  if (portalUrl.protocol !== "https:") {
56893
56944
  throw new Error(`Refusing to open non-HTTPS portal URL: ${result.url}`);
56894
56945
  }
56946
+ if (!ALLOWED_PORTAL_DOMAINS.has(portalUrl.hostname)) {
56947
+ throw new Error(`Refusing to open URL with untrusted domain: ${portalUrl.hostname}`);
56948
+ }
56895
56949
  const { default: open2 } = await Promise.resolve().then(() => (init_open(), open_exports));
56896
56950
  await open2(result.url);
56897
56951
  console.log("");
@@ -56915,6 +56969,17 @@ function registerBillingCommand(program2) {
56915
56969
  console.log(formatBillingStatus(result));
56916
56970
  }
56917
56971
  } catch (error49) {
56972
+ if (error49 instanceof ApiError && error49.status === 404) {
56973
+ if (options.format === "json") {
56974
+ console.log(JSON.stringify({ plan: "starter", status: "free" }, null, 2));
56975
+ } else {
56976
+ console.log("");
56977
+ console.log(source_default.dim(" You're on the free Starter plan."));
56978
+ console.log(source_default.dim(" Run: trading-boy subscribe to upgrade."));
56979
+ console.log("");
56980
+ }
56981
+ return;
56982
+ }
56918
56983
  const message = error49 instanceof Error ? error49.message : String(error49);
56919
56984
  logger22.error({ error: message }, "Billing status failed");
56920
56985
  console.error(source_default.red(` Error: ${message}`));
@@ -57180,7 +57245,7 @@ async function pollForApiKey(token, onTick) {
57180
57245
  if (result.status === "expired" || result.status === "not_found") {
57181
57246
  return {
57182
57247
  success: false,
57183
- error: "Provisioning token expired. If you completed payment, try `trading-boy login` with your API key from the confirmation email."
57248
+ error: "Provisioning token expired. Your API key will be emailed to you shortly. Then run: `trading-boy login`"
57184
57249
  };
57185
57250
  }
57186
57251
  if (result.status === "already_retrieved") {
@@ -57197,7 +57262,7 @@ async function pollForApiKey(token, onTick) {
57197
57262
  }
57198
57263
  return {
57199
57264
  success: false,
57200
- error: "Timed out waiting for payment confirmation. If you completed payment, try `trading-boy login` with your API key from the confirmation email."
57265
+ error: "Timed out waiting for payment confirmation. Your API key will be emailed to you shortly. Then run: `trading-boy login`"
57201
57266
  };
57202
57267
  }
57203
57268
  async function saveApiKey(apiKey, metadata) {
@@ -57536,6 +57601,7 @@ function formatConnectionError2(message) {
57536
57601
  // dist/commands/edge-cmd.js
57537
57602
  init_source();
57538
57603
  init_api_client();
57604
+ init_utils3();
57539
57605
  function formatEdgeOutput(data) {
57540
57606
  const lines = [];
57541
57607
  lines.push("");
@@ -57665,11 +57731,8 @@ function formatPct2(val) {
57665
57731
  function registerEdgeCommand(program2) {
57666
57732
  program2.command("edge <traderId>").description("Analyze trade quality \u2014 edge ratio and PnL attribution").option("--attribution", "Show PnL attribution breakdown instead of edge ratio").option("--token <symbol>", "Filter by token symbol").option("--limit <n>", "Number of trades to analyze (1-500)", "100").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (traderId, options) => {
57667
57733
  try {
57668
- if (!await isRemoteMode()) {
57669
- console.error(source_default.red("Error: edge command requires remote mode. Run `trading-boy login` first."));
57670
- process.exitCode = 1;
57734
+ if (!await ensureRemote())
57671
57735
  return;
57672
- }
57673
57736
  const query = new URLSearchParams();
57674
57737
  if (options.token)
57675
57738
  query.set("tokenSymbol", options.token.toUpperCase());
@@ -57702,6 +57765,7 @@ function registerEdgeCommand(program2) {
57702
57765
  // dist/commands/edge-guard-cmd.js
57703
57766
  init_source();
57704
57767
  init_api_client();
57768
+ init_utils3();
57705
57769
  function formatAssessmentOutput(data) {
57706
57770
  const lines = [];
57707
57771
  const a = data.assessment;
@@ -57810,11 +57874,8 @@ function registerEdgeGuardCommand(program2) {
57810
57874
  });
57811
57875
  cmd.command("check <traderId>").description("Assess negative edge status for a trader").option("--friction", "Check entry friction level only").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (traderId, options) => {
57812
57876
  try {
57813
- if (!await isRemoteMode()) {
57814
- console.error(source_default.red("Error: edge-guard command requires remote mode. Run `trading-boy login` first."));
57815
- process.exitCode = 1;
57877
+ if (!await ensureRemote())
57816
57878
  return;
57817
- }
57818
57879
  if (options.friction) {
57819
57880
  const data = await apiRequest(`/api/v1/traders/${encodeURIComponent(traderId)}/edge-guard/friction`);
57820
57881
  if (options.format === "json") {
@@ -57838,11 +57899,8 @@ function registerEdgeGuardCommand(program2) {
57838
57899
  });
57839
57900
  cmd.command("acknowledge <traderId>").description("Acknowledge negative edge warning to proceed with trade").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (traderId, ackOptions) => {
57840
57901
  try {
57841
- if (!await isRemoteMode()) {
57842
- console.error(source_default.red("Error: edge-guard command requires remote mode. Run `trading-boy login` first."));
57843
- process.exitCode = 1;
57902
+ if (!await ensureRemote())
57844
57903
  return;
57845
- }
57846
57904
  const data = await apiRequest(`/api/v1/traders/${encodeURIComponent(traderId)}/edge-guard/acknowledge`, { method: "POST" });
57847
57905
  if (ackOptions.format === "json") {
57848
57906
  console.log(JSON.stringify(data, null, 2));
@@ -57863,6 +57921,7 @@ function registerEdgeGuardCommand(program2) {
57863
57921
  init_source();
57864
57922
  init_dist2();
57865
57923
  init_api_client();
57924
+ init_utils3();
57866
57925
  var logger24 = createLogger("cli-coaching");
57867
57926
  var BYOK_HINT = `LLM API key required. Run: ${source_default.white("trading-boy config set-llm-key <key>")}`;
57868
57927
  function isByokError(error49) {
@@ -57876,15 +57935,6 @@ function isByokError(error49) {
57876
57935
  }
57877
57936
  return false;
57878
57937
  }
57879
- async function requireRemote() {
57880
- if (!await isRemoteMode()) {
57881
- console.error(source_default.yellow("Coaching requires a remote API connection."));
57882
- console.error(source_default.dim(" Run: trading-boy login"));
57883
- process.exitCode = 1;
57884
- return false;
57885
- }
57886
- return true;
57887
- }
57888
57938
  function formatIntervention(response) {
57889
57939
  const lines = [];
57890
57940
  lines.push("");
@@ -57920,7 +57970,7 @@ function formatIntervention(response) {
57920
57970
  function registerCoachingCommand(program2) {
57921
57971
  const coaching = program2.command("coaching").description("Trading coach \u2014 pre/post-trade interventions (requires BYOK LLM key)");
57922
57972
  coaching.command("pre-trade <traderId>").description("Get pre-trade coaching intervention check").option("--tilt-score <n>", "Tilt score (0-100)", parseFloat).option("--flags <flags>", "Active behavioral flags (comma-separated)").option("--token <symbol>", "Token being traded").option("--direction <dir>", "Trade direction (LONG or SHORT)").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (traderId, options) => {
57923
- if (!await requireRemote())
57973
+ if (!await ensureRemote())
57924
57974
  return;
57925
57975
  const extraction = {
57926
57976
  decisionType: "TRADE_ENTRY",
@@ -57973,7 +58023,7 @@ function registerCoachingCommand(program2) {
57973
58023
  }
57974
58024
  });
57975
58025
  coaching.command("post-trade <traderId>").description("Get post-trade coaching session reflection").option("--tilt-score <n>", "Tilt score (0-100)", parseFloat).option("--flags <flags>", "Active behavioral flags (comma-separated)").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (traderId, options) => {
57976
- if (!await requireRemote())
58026
+ if (!await ensureRemote())
57977
58027
  return;
57978
58028
  const body = {
57979
58029
  tiltScore: options.tiltScore ?? 0,
@@ -58014,7 +58064,7 @@ function registerCoachingCommand(program2) {
58014
58064
  }
58015
58065
  });
58016
58066
  coaching.command("acknowledge <traderId>").description("Acknowledge a coaching intervention").requiredOption("--reason <reason>", "Reason for acknowledgment").action(async (traderId, options) => {
58017
- if (!await requireRemote())
58067
+ if (!await ensureRemote())
58018
58068
  return;
58019
58069
  try {
58020
58070
  const result = await apiRequest(`/api/v1/traders/${encodeURIComponent(traderId)}/coaching/acknowledge`, { method: "POST", body: { reason: options.reason } });
@@ -58030,7 +58080,7 @@ function registerCoachingCommand(program2) {
58030
58080
  }
58031
58081
  });
58032
58082
  coaching.command("reset <traderId>").description("Reset coaching session state").action(async (traderId) => {
58033
- if (!await requireRemote())
58083
+ if (!await ensureRemote())
58034
58084
  return;
58035
58085
  try {
58036
58086
  const result = await apiRequest(`/api/v1/traders/${encodeURIComponent(traderId)}/coaching/reset-session`, { method: "POST", body: {} });
@@ -58050,6 +58100,7 @@ function registerCoachingCommand(program2) {
58050
58100
  init_source();
58051
58101
  init_dist2();
58052
58102
  init_api_client();
58103
+ init_utils3();
58053
58104
  var logger25 = createLogger("cli-thesis");
58054
58105
  var BYOK_HINT2 = `LLM API key required. Run: ${source_default.white("trading-boy config set-llm-key <key>")}`;
58055
58106
  function formatThesisOutput(response) {
@@ -58109,12 +58160,8 @@ function formatThesisOutput(response) {
58109
58160
  }
58110
58161
  function registerThesisCommand(program2) {
58111
58162
  program2.command("thesis <decisionId>").description("Extract structured thesis from trade decision text (requires BYOK LLM key)").requiredOption("--text <thesis>", "Thesis text to extract from").requiredOption("--trader <traderId>", "Trader ID").option("--token <symbol>", "Token symbol").option("--direction <dir>", "Trade direction (LONG or SHORT)").option("--entry-price <n>", "Entry price", parseFloat).addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (decisionId, options) => {
58112
- if (!await isRemoteMode()) {
58113
- console.error(source_default.yellow("Thesis extraction requires a remote API connection."));
58114
- console.error(source_default.dim(" Run: trading-boy login"));
58115
- process.exitCode = 1;
58163
+ if (!await ensureRemote())
58116
58164
  return;
58117
- }
58118
58165
  if (options.direction && !["LONG", "SHORT"].includes(options.direction.toUpperCase())) {
58119
58166
  console.error(source_default.red("Error: --direction must be LONG or SHORT"));
58120
58167
  process.exitCode = 1;
@@ -58264,44 +58311,10 @@ function formatHistoryList(response) {
58264
58311
  lines.push("");
58265
58312
  return lines.join("\n");
58266
58313
  }
58267
- async function requireRemote2() {
58268
- if (!await isRemoteMode()) {
58269
- console.error(source_default.yellow("Strategy commands require a remote API connection."));
58270
- console.error(source_default.dim(" Run: trading-boy login"));
58271
- process.exitCode = 1;
58272
- return false;
58273
- }
58274
- return true;
58275
- }
58276
- function handleApiError(error49, context) {
58277
- if (error49 instanceof ApiError) {
58278
- switch (error49.status) {
58279
- case 401:
58280
- console.error(source_default.red("Error: API key invalid or expired. Run `trading-boy login` to re-authenticate."));
58281
- break;
58282
- case 403:
58283
- console.error(source_default.red("Error: Subscription inactive. Run `trading-boy billing manage` to update your billing."));
58284
- break;
58285
- case 404:
58286
- console.error(source_default.red(`Error: Not found \u2014 ${error49.message}`));
58287
- break;
58288
- case 422:
58289
- console.error(source_default.red(`Error: Validation failed \u2014 ${error49.message}`));
58290
- break;
58291
- default:
58292
- console.error(source_default.red(`Error: ${error49.message}`));
58293
- }
58294
- } else {
58295
- const message = error49 instanceof Error ? error49.message : String(error49);
58296
- logger26.error({ error: message }, context);
58297
- console.error(source_default.red(`Error: ${message}`));
58298
- }
58299
- process.exitCode = error49 instanceof ApiError ? 2 : 1;
58300
- }
58301
58314
  function registerStrategyCommand(program2) {
58302
58315
  const strategy = program2.command("strategy").description("Manage agent strategies (token watch-lists, setup types, signal weights)");
58303
58316
  strategy.command("create").description("Create a new agent strategy").requiredOption("--name <name>", "Strategy name").requiredOption("--trader-id <id>", "Trader ID").requiredOption("--agent-id <id>", "Agent ID").requiredOption("--tokens <tokens>", "Comma-separated token symbols").option("--setups <types>", "Comma-separated setup types").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (options) => {
58304
- if (!await requireRemote2())
58317
+ if (!await ensureRemote())
58305
58318
  return;
58306
58319
  const tokens = options.tokens.split(",").map((t) => t.trim().toUpperCase()).filter(Boolean);
58307
58320
  const setupTypes = options.setups ? options.setups.split(",").map((s) => s.trim().toUpperCase()).filter(Boolean) : ["BREAKOUT"];
@@ -58337,11 +58350,11 @@ function registerStrategyCommand(program2) {
58337
58350
  console.log(formatStrategyDetail(result));
58338
58351
  }
58339
58352
  } catch (error49) {
58340
- handleApiError(error49, "Strategy create failed");
58353
+ handleApiError(error49, "Strategy create failed", logger26);
58341
58354
  }
58342
58355
  });
58343
58356
  strategy.command("list").description("List strategies for a trader").requiredOption("--trader-id <id>", "Trader ID").option("--agent-id <id>", "Filter by agent ID").option("--limit <n>", "Maximum results", "20").option("--offset <n>", "Pagination offset", "0").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (options) => {
58344
- if (!await requireRemote2())
58357
+ if (!await ensureRemote())
58345
58358
  return;
58346
58359
  const params = new URLSearchParams();
58347
58360
  params.set("traderId", options.traderId);
@@ -58357,11 +58370,11 @@ function registerStrategyCommand(program2) {
58357
58370
  console.log(formatStrategyList(result));
58358
58371
  }
58359
58372
  } catch (error49) {
58360
- handleApiError(error49, "Strategy list failed");
58373
+ handleApiError(error49, "Strategy list failed", logger26);
58361
58374
  }
58362
58375
  });
58363
58376
  strategy.command("show <id>").description("Show full details for a strategy").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (id, options) => {
58364
- if (!await requireRemote2())
58377
+ if (!await ensureRemote())
58365
58378
  return;
58366
58379
  try {
58367
58380
  const result = await apiRequest(`/api/v1/strategies/${encodeURIComponent(id)}`);
@@ -58371,11 +58384,11 @@ function registerStrategyCommand(program2) {
58371
58384
  console.log(formatStrategyDetail(result));
58372
58385
  }
58373
58386
  } catch (error49) {
58374
- handleApiError(error49, "Strategy show failed");
58387
+ handleApiError(error49, "Strategy show failed", logger26);
58375
58388
  }
58376
58389
  });
58377
58390
  strategy.command("update <id>").description("Update a strategy").option("--name <name>", "New strategy name").option("--tokens <tokens>", "New comma-separated token symbols (replaces existing)").option("--setups <types>", "New comma-separated setup types (replaces existing)").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (id, options) => {
58378
- if (!await requireRemote2())
58391
+ if (!await ensureRemote())
58379
58392
  return;
58380
58393
  const body = {};
58381
58394
  if (options.name) {
@@ -58400,11 +58413,11 @@ function registerStrategyCommand(program2) {
58400
58413
  console.log(formatStrategyDetail(result));
58401
58414
  }
58402
58415
  } catch (error49) {
58403
- handleApiError(error49, "Strategy update failed");
58416
+ handleApiError(error49, "Strategy update failed", logger26);
58404
58417
  }
58405
58418
  });
58406
58419
  strategy.command("history <id>").description("Show version history for a strategy").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (id, options) => {
58407
- if (!await requireRemote2())
58420
+ if (!await ensureRemote())
58408
58421
  return;
58409
58422
  try {
58410
58423
  const result = await apiRequest(`/api/v1/strategies/${encodeURIComponent(id)}/history`);
@@ -58414,11 +58427,11 @@ function registerStrategyCommand(program2) {
58414
58427
  console.log(formatHistoryList(result));
58415
58428
  }
58416
58429
  } catch (error49) {
58417
- handleApiError(error49, "Strategy history failed");
58430
+ handleApiError(error49, "Strategy history failed", logger26);
58418
58431
  }
58419
58432
  });
58420
58433
  strategy.command("export").description("Export a strategy in json, elizaos, or freqtrade format").requiredOption("--id <id>", "Strategy ID to export").addOption(new Option("--format <format>", "Export format").choices(["json", "elizaos", "freqtrade"]).default("json")).option("--output <file>", "Write output to a file instead of stdout").action(async (options) => {
58421
- if (!await requireRemote2())
58434
+ if (!await ensureRemote())
58422
58435
  return;
58423
58436
  try {
58424
58437
  const result = await apiRequest(`/api/v1/strategies/${encodeURIComponent(options.id)}/export?format=${encodeURIComponent(options.format)}`);
@@ -58440,7 +58453,7 @@ function registerStrategyCommand(program2) {
58440
58453
  console.log(output);
58441
58454
  }
58442
58455
  } catch (error49) {
58443
- handleApiError(error49, "Strategy export failed");
58456
+ handleApiError(error49, "Strategy export failed", logger26);
58444
58457
  }
58445
58458
  });
58446
58459
  }
@@ -58529,39 +58542,10 @@ async function pollReplayJob(jobId) {
58529
58542
  }
58530
58543
  throw new Error(`Replay job ${jobId} did not complete within ${POLL_TIMEOUT_MS2 / 1e3}s timeout.`);
58531
58544
  }
58532
- function handleApiError2(error49, context) {
58533
- if (error49 instanceof ApiError) {
58534
- switch (error49.status) {
58535
- case 401:
58536
- console.error(source_default.red("Error: API key invalid or expired. Run `trading-boy login` to re-authenticate."));
58537
- break;
58538
- case 403:
58539
- console.error(source_default.red("Error: Subscription inactive. Run `trading-boy billing manage` to update your billing."));
58540
- break;
58541
- case 404:
58542
- console.error(source_default.red(`Error: Not found \u2014 ${error49.message}`));
58543
- break;
58544
- case 422:
58545
- console.error(source_default.red(`Error: Validation failed \u2014 ${error49.message}`));
58546
- break;
58547
- default:
58548
- console.error(source_default.red(`Error: ${error49.message}`));
58549
- }
58550
- } else {
58551
- const message = error49 instanceof Error ? error49.message : String(error49);
58552
- logger27.error({ error: message }, context);
58553
- console.error(source_default.red(`Error: ${message}`));
58554
- }
58555
- process.exitCode = error49 instanceof ApiError ? 2 : 1;
58556
- }
58557
58545
  function registerReplayCommand(program2) {
58558
58546
  program2.command("replay").description("Run strategy replay (backtest) over a historical period").requiredOption("--strategy <id>", "Strategy ID").requiredOption("--token <symbol>", "Token symbol").requiredOption("--from <date>", "Start date (ISO-8601, e.g. 2025-01-01)").requiredOption("--to <date>", "End date (ISO-8601, e.g. 2025-03-01)").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (options) => {
58559
- if (!await isRemoteMode()) {
58560
- console.error(source_default.yellow("Replay requires a remote API connection."));
58561
- console.error(source_default.dim(" Run: trading-boy login"));
58562
- process.exitCode = 1;
58547
+ if (!await ensureRemote())
58563
58548
  return;
58564
- }
58565
58549
  const fromDate = new Date(options.from);
58566
58550
  const toDate = new Date(options.to);
58567
58551
  if (isNaN(fromDate.getTime())) {
@@ -58596,7 +58580,7 @@ function registerReplayCommand(program2) {
58596
58580
  submitSpinner.succeed(source_default.dim(`Job submitted: ${jobId}`));
58597
58581
  } catch (error49) {
58598
58582
  submitSpinner.fail("Failed to submit replay job");
58599
- handleApiError2(error49, "Replay submit failed");
58583
+ handleApiError(error49, "Replay submit failed", logger27);
58600
58584
  return;
58601
58585
  }
58602
58586
  const pollSpinner = (await createSpinner2("Running replay\u2026")).start();
@@ -58606,7 +58590,7 @@ function registerReplayCommand(program2) {
58606
58590
  pollSpinner.stop();
58607
58591
  } catch (error49) {
58608
58592
  pollSpinner.fail("Replay timed out or encountered an error");
58609
- handleApiError2(error49, "Replay polling failed");
58593
+ handleApiError(error49, "Replay polling failed", logger27);
58610
58594
  return;
58611
58595
  }
58612
58596
  if (!finalStatus.result) {
@@ -58629,6 +58613,7 @@ function registerReplayCommand(program2) {
58629
58613
  // dist/commands/benchmark-cmd.js
58630
58614
  init_source();
58631
58615
  init_api_client();
58616
+ init_utils3();
58632
58617
  function formatMetric(value, decimals = 2) {
58633
58618
  if (value === null)
58634
58619
  return source_default.dim("\u2014");
@@ -58713,11 +58698,8 @@ function registerBenchmarkCommand(program2) {
58713
58698
  "timeWeightedReturn"
58714
58699
  ]).default("sharpeRatio")).addOption(new Option("--entity-type <type>", "Entity type").choices(["trader", "agent", "strategy"]).default("trader")).option("--setup <setupType>", "Filter by setup type").option("--regime <regime>", "Filter by market regime").option("--token <symbol>", "Filter by token symbol").option("--entity <entityId>", "Show single entity benchmark").option("--limit <n>", "Number of entries", "20").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (options) => {
58715
58700
  try {
58716
- if (!await isRemoteMode()) {
58717
- console.error(source_default.red("Error: benchmark command requires remote mode. Run `trading-boy login` first."));
58718
- process.exitCode = 1;
58701
+ if (!await ensureRemote())
58719
58702
  return;
58720
- }
58721
58703
  if (options.entity) {
58722
58704
  const query = new URLSearchParams();
58723
58705
  query.set("window", options.window);
@@ -58760,11 +58742,8 @@ function registerBenchmarkCommand(program2) {
58760
58742
  });
58761
58743
  benchCmd.command("recompute").description("Trigger on-demand benchmark recompute").action(async () => {
58762
58744
  try {
58763
- if (!await isRemoteMode()) {
58764
- console.error(source_default.red("Error: benchmark recompute requires remote mode. Run `trading-boy login` first."));
58765
- process.exitCode = 1;
58745
+ if (!await ensureRemote())
58766
58746
  return;
58767
- }
58768
58747
  console.log(source_default.dim("Recomputing benchmarks..."));
58769
58748
  const data = await apiRequest("/api/v1/benchmark/recompute", { method: "POST", body: {} });
58770
58749
  console.log(source_default.green(`
@@ -58829,12 +58808,8 @@ function registerSuggestionsCommand(program2) {
58829
58808
  cmd.help();
58830
58809
  });
58831
58810
  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) => {
58832
- if (!await isRemoteMode()) {
58833
- console.error(source_default.yellow("This command requires a remote API connection."));
58834
- console.error(source_default.dim(" Run: trading-boy login or trading-boy subscribe"));
58835
- process.exitCode = 1;
58811
+ if (!await ensureRemote())
58836
58812
  return;
58837
- }
58838
58813
  try {
58839
58814
  const params = new URLSearchParams();
58840
58815
  params.set("status", options.status);
@@ -58856,12 +58831,8 @@ function registerSuggestionsCommand(program2) {
58856
58831
  }
58857
58832
  });
58858
58833
  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) => {
58859
- if (!await isRemoteMode()) {
58860
- console.error(source_default.yellow("This command requires a remote API connection."));
58861
- console.error(source_default.dim(" Run: trading-boy login or trading-boy subscribe"));
58862
- process.exitCode = 1;
58834
+ if (!await ensureRemote())
58863
58835
  return;
58864
- }
58865
58836
  try {
58866
58837
  const data = await apiRequest(`/api/v1/suggestions/${encodeURIComponent(id)}/approve`, { method: "POST" });
58867
58838
  if (options.format === "json") {
@@ -58879,12 +58850,8 @@ function registerSuggestionsCommand(program2) {
58879
58850
  }
58880
58851
  });
58881
58852
  cmd.command("reject <id>").description("Reject a suggestion").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (id, options) => {
58882
- if (!await isRemoteMode()) {
58883
- console.error(source_default.yellow("This command requires a remote API connection."));
58884
- console.error(source_default.dim(" Run: trading-boy login or trading-boy subscribe"));
58885
- process.exitCode = 1;
58853
+ if (!await ensureRemote())
58886
58854
  return;
58887
- }
58888
58855
  try {
58889
58856
  const data = await apiRequest(`/api/v1/suggestions/${encodeURIComponent(id)}/reject`, { method: "POST" });
58890
58857
  if (options.format === "json") {
@@ -58909,40 +58876,6 @@ init_dist2();
58909
58876
  init_api_client();
58910
58877
  init_utils3();
58911
58878
  var logger29 = createLogger("cli-cron");
58912
- function handleApiError3(error49, context) {
58913
- if (error49 instanceof ApiError) {
58914
- switch (error49.status) {
58915
- case 401:
58916
- console.error(source_default.red("Error: API key invalid or expired. Run `trading-boy login` to re-authenticate."));
58917
- break;
58918
- case 403:
58919
- console.error(source_default.red("Error: Subscription inactive. Run `trading-boy billing manage` to update your billing."));
58920
- break;
58921
- case 404:
58922
- console.error(source_default.red(`Error: Not found \u2014 ${error49.message}`));
58923
- break;
58924
- case 429:
58925
- console.error(source_default.red(`Error: Limit reached \u2014 ${error49.message}`));
58926
- break;
58927
- default:
58928
- console.error(source_default.red(`Error: ${error49.message}`));
58929
- }
58930
- } else {
58931
- const message = error49 instanceof Error ? error49.message : String(error49);
58932
- logger29.error({ error: message }, context);
58933
- console.error(source_default.red(`Error: ${message}`));
58934
- }
58935
- process.exitCode = error49 instanceof ApiError ? 2 : 1;
58936
- }
58937
- async function ensureRemote() {
58938
- if (!await isRemoteMode()) {
58939
- console.error(source_default.yellow("Cron commands require a remote API connection."));
58940
- console.error(source_default.dim(" Run: trading-boy login"));
58941
- process.exitCode = 1;
58942
- return false;
58943
- }
58944
- return true;
58945
- }
58946
58879
  function formatShortDate4(isoString) {
58947
58880
  if (!isoString)
58948
58881
  return source_default.dim("\u2014");
@@ -59014,7 +58947,7 @@ function registerCronCommand(program2) {
59014
58947
  console.log("");
59015
58948
  }
59016
58949
  } catch (error49) {
59017
- handleApiError3(error49, "Cron create failed");
58950
+ handleApiError(error49, "Cron create failed", logger29);
59018
58951
  }
59019
58952
  });
59020
58953
  cron.command("list").description("List cron jobs").option("--status <status>", "Filter by status: active, paused").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (options) => {
@@ -59041,7 +58974,7 @@ function registerCronCommand(program2) {
59041
58974
  console.log(source_default.dim(` ${result.count} job(s)`));
59042
58975
  console.log("");
59043
58976
  } catch (error49) {
59044
- handleApiError3(error49, "Cron list failed");
58977
+ handleApiError(error49, "Cron list failed", logger29);
59045
58978
  }
59046
58979
  });
59047
58980
  cron.command("show <jobId>").description("Show details of a cron job").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (jobId, options) => {
@@ -59069,7 +59002,7 @@ function registerCronCommand(program2) {
59069
59002
  console.log(` ${source_default.gray("Created:")} ${formatShortDate4(job.createdAt)}`);
59070
59003
  console.log("");
59071
59004
  } catch (error49) {
59072
- handleApiError3(error49, "Cron show failed");
59005
+ handleApiError(error49, "Cron show failed", logger29);
59073
59006
  }
59074
59007
  });
59075
59008
  cron.command("pause <jobId>").description("Pause a cron job").action(async (jobId) => {
@@ -59082,7 +59015,7 @@ function registerCronCommand(program2) {
59082
59015
  });
59083
59016
  console.log(source_default.green(` Job ${jobId} paused`));
59084
59017
  } catch (error49) {
59085
- handleApiError3(error49, "Cron pause failed");
59018
+ handleApiError(error49, "Cron pause failed", logger29);
59086
59019
  }
59087
59020
  });
59088
59021
  cron.command("resume <jobId>").description("Resume a paused cron job").action(async (jobId) => {
@@ -59095,7 +59028,7 @@ function registerCronCommand(program2) {
59095
59028
  });
59096
59029
  console.log(source_default.green(` Job ${jobId} resumed`));
59097
59030
  } catch (error49) {
59098
- handleApiError3(error49, "Cron resume failed");
59031
+ handleApiError(error49, "Cron resume failed", logger29);
59099
59032
  }
59100
59033
  });
59101
59034
  cron.command("delete <jobId>").description("Delete a cron job").action(async (jobId) => {
@@ -59107,7 +59040,7 @@ function registerCronCommand(program2) {
59107
59040
  });
59108
59041
  console.log(source_default.green(` Job ${jobId} deleted`));
59109
59042
  } catch (error49) {
59110
- handleApiError3(error49, "Cron delete failed");
59043
+ handleApiError(error49, "Cron delete failed", logger29);
59111
59044
  }
59112
59045
  });
59113
59046
  cron.command("run <jobId>").description("Trigger immediate execution of a cron job").action(async (jobId) => {
@@ -59119,7 +59052,7 @@ function registerCronCommand(program2) {
59119
59052
  });
59120
59053
  console.log(source_default.green(` Job ${jobId} execution triggered`));
59121
59054
  } catch (error49) {
59122
- handleApiError3(error49, "Cron run failed");
59055
+ handleApiError(error49, "Cron run failed", logger29);
59123
59056
  }
59124
59057
  });
59125
59058
  cron.command("history <jobId>").description("View execution history for a cron job").option("--limit <n>", "Number of runs to show", "20").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (jobId, options) => {
@@ -59147,7 +59080,7 @@ function registerCronCommand(program2) {
59147
59080
  console.log(source_default.dim(` ${result.count} run(s)`));
59148
59081
  console.log("");
59149
59082
  } catch (error49) {
59150
- handleApiError3(error49, "Cron history failed");
59083
+ handleApiError(error49, "Cron history failed", logger29);
59151
59084
  }
59152
59085
  });
59153
59086
  }
@@ -59160,40 +59093,6 @@ init_dist2();
59160
59093
  init_api_client();
59161
59094
  init_utils3();
59162
59095
  var logger30 = createLogger("cli-agent");
59163
- function handleApiError4(error49, context) {
59164
- if (error49 instanceof ApiError) {
59165
- switch (error49.status) {
59166
- case 401:
59167
- console.error(source_default.red("Error: API key invalid or expired. Run `trading-boy login` to re-authenticate."));
59168
- break;
59169
- case 403:
59170
- console.error(source_default.red(`Error: ${error49.message}`));
59171
- break;
59172
- case 404:
59173
- console.error(source_default.red(`Error: Not found \u2014 ${error49.message}`));
59174
- break;
59175
- case 429:
59176
- console.error(source_default.red(`Error: Limit reached \u2014 ${error49.message}`));
59177
- break;
59178
- default:
59179
- console.error(source_default.red(`Error: ${error49.message}`));
59180
- }
59181
- } else {
59182
- const message = error49 instanceof Error ? error49.message : String(error49);
59183
- logger30.error({ error: message }, context);
59184
- console.error(source_default.red(`Error: ${message}`));
59185
- }
59186
- process.exitCode = error49 instanceof ApiError ? 2 : 1;
59187
- }
59188
- async function ensureRemote2() {
59189
- if (!await isRemoteMode()) {
59190
- console.error(source_default.yellow("Agent commands require a remote API connection."));
59191
- console.error(source_default.dim(" Run: trading-boy login"));
59192
- process.exitCode = 1;
59193
- return false;
59194
- }
59195
- return true;
59196
- }
59197
59096
  function formatShortDate5(isoString) {
59198
59097
  if (!isoString)
59199
59098
  return source_default.dim("\u2014");
@@ -59257,7 +59156,7 @@ var MIN_SCAN_INTERVAL_MS = 6e4;
59257
59156
  function registerAgentCommand(program2) {
59258
59157
  const agent = program2.command("agent").description("Manage autonomous trading agents");
59259
59158
  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) => {
59260
- if (!await ensureRemote2())
59159
+ if (!await ensureRemote())
59261
59160
  return;
59262
59161
  if (!options.traderId) {
59263
59162
  console.error(source_default.red("Error: --trader-id is required."));
@@ -59355,11 +59254,11 @@ function registerAgentCommand(program2) {
59355
59254
  console.log("");
59356
59255
  }
59357
59256
  } catch (error49) {
59358
- handleApiError4(error49, "Agent create failed");
59257
+ handleApiError(error49, "Agent create failed", logger30);
59359
59258
  }
59360
59259
  });
59361
59260
  agent.command("list").description("List agents").option("--status <status>", "Filter by status: active, paused").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (options) => {
59362
- if (!await ensureRemote2())
59261
+ if (!await ensureRemote())
59363
59262
  return;
59364
59263
  try {
59365
59264
  const query = options.status ? `?status=${options.status}` : "";
@@ -59388,11 +59287,11 @@ function registerAgentCommand(program2) {
59388
59287
  console.log(source_default.dim(` ${result.count} agent(s)`));
59389
59288
  console.log("");
59390
59289
  } catch (error49) {
59391
- handleApiError4(error49, "Agent list failed");
59290
+ handleApiError(error49, "Agent list failed", logger30);
59392
59291
  }
59393
59292
  });
59394
59293
  agent.command("show <agentId>").description("Show agent details and live state").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (agentId, options) => {
59395
- if (!await ensureRemote2())
59294
+ if (!await ensureRemote())
59396
59295
  return;
59397
59296
  try {
59398
59297
  const result = await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}`);
@@ -59461,47 +59360,59 @@ function registerAgentCommand(program2) {
59461
59360
  }
59462
59361
  console.log("");
59463
59362
  } catch (error49) {
59464
- handleApiError4(error49, "Agent show failed");
59363
+ handleApiError(error49, "Agent show failed", logger30);
59465
59364
  }
59466
59365
  });
59467
- agent.command("pause <agentId>").description("Pause an agent").action(async (agentId) => {
59468
- if (!await ensureRemote2())
59366
+ agent.command("pause <agentId>").description("Pause an agent").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (agentId, options) => {
59367
+ if (!await ensureRemote())
59469
59368
  return;
59470
59369
  try {
59471
59370
  await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/pause`, {
59472
59371
  method: "POST"
59473
59372
  });
59474
- console.log(source_default.green(` Agent ${agentId} paused`));
59373
+ if (options.format === "json") {
59374
+ console.log(JSON.stringify({ agentId, status: "paused" }, null, 2));
59375
+ } else {
59376
+ console.log(source_default.green(` Agent ${agentId} paused`));
59377
+ }
59475
59378
  } catch (error49) {
59476
- handleApiError4(error49, "Agent pause failed");
59379
+ handleApiError(error49, "Agent pause failed", logger30);
59477
59380
  }
59478
59381
  });
59479
- agent.command("resume <agentId>").description("Resume a paused agent").action(async (agentId) => {
59480
- if (!await ensureRemote2())
59382
+ agent.command("resume <agentId>").description("Resume a paused agent").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (agentId, options) => {
59383
+ if (!await ensureRemote())
59481
59384
  return;
59482
59385
  try {
59483
59386
  await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/resume`, {
59484
59387
  method: "POST"
59485
59388
  });
59486
- console.log(source_default.green(` Agent ${agentId} resumed`));
59389
+ if (options.format === "json") {
59390
+ console.log(JSON.stringify({ agentId, status: "active" }, null, 2));
59391
+ } else {
59392
+ console.log(source_default.green(` Agent ${agentId} resumed`));
59393
+ }
59487
59394
  } catch (error49) {
59488
- handleApiError4(error49, "Agent resume failed");
59395
+ handleApiError(error49, "Agent resume failed", logger30);
59489
59396
  }
59490
59397
  });
59491
- agent.command("delete <agentId>").description("Delete an agent").action(async (agentId) => {
59492
- if (!await ensureRemote2())
59398
+ agent.command("delete <agentId>").description("Delete an agent").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (agentId, options) => {
59399
+ if (!await ensureRemote())
59493
59400
  return;
59494
59401
  try {
59495
59402
  await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}`, {
59496
59403
  method: "DELETE"
59497
59404
  });
59498
- console.log(source_default.green(` Agent ${agentId} deleted`));
59405
+ if (options.format === "json") {
59406
+ console.log(JSON.stringify({ agentId, status: "deleted" }, null, 2));
59407
+ } else {
59408
+ console.log(source_default.green(` Agent ${agentId} deleted`));
59409
+ }
59499
59410
  } catch (error49) {
59500
- handleApiError4(error49, "Agent delete failed");
59411
+ handleApiError(error49, "Agent delete failed", logger30);
59501
59412
  }
59502
59413
  });
59503
59414
  agent.command("exit <agentId>").description("Exit/close an open position for an agent").requiredOption("--symbol <symbol>", "Token symbol to exit (e.g. xyz:NATGAS)").option("--reason <text>", "Reason for exit").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (agentId, options) => {
59504
- if (!await ensureRemote2())
59415
+ if (!await ensureRemote())
59505
59416
  return;
59506
59417
  try {
59507
59418
  const body = {};
@@ -59526,11 +59437,11 @@ function registerAgentCommand(program2) {
59526
59437
  console.log("");
59527
59438
  }
59528
59439
  } catch (error49) {
59529
- handleApiError4(error49, "Position exit failed");
59440
+ handleApiError(error49, "Position exit failed", logger30);
59530
59441
  }
59531
59442
  });
59532
- agent.command("update <agentId>").description("Update agent config").option("--name <name>", "Agent name").option("--autonomy <level>", "Autonomy level").option("--scan-interval <ms>", "Scan interval in ms").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").option("--max-daily-loss <usd>", "Max daily loss in USD").option("--max-position-size <pct>", "Max position size as decimal").option("--min-confidence <n>", "Min confidence threshold").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"])).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").action(async (agentId, options) => {
59533
- if (!await ensureRemote2())
59443
+ agent.command("update <agentId>").description("Update agent config").option("--name <name>", "Agent name").option("--autonomy <level>", "Autonomy level").option("--scan-interval <ms>", "Scan interval in ms").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").option("--max-daily-loss <usd>", "Max daily loss in USD").option("--max-position-size <pct>", "Max position size as decimal").option("--min-confidence <n>", "Min confidence threshold").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"])).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 (agentId, options) => {
59444
+ if (!await ensureRemote())
59534
59445
  return;
59535
59446
  const body = {};
59536
59447
  if (options.name)
@@ -59599,13 +59510,17 @@ function registerAgentCommand(program2) {
59599
59510
  return;
59600
59511
  }
59601
59512
  try {
59602
- await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}`, {
59513
+ const result = await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}`, {
59603
59514
  method: "PATCH",
59604
59515
  body
59605
59516
  });
59606
- console.log(source_default.green(` Agent ${agentId} updated`));
59517
+ if (options.format === "json") {
59518
+ console.log(JSON.stringify(result, null, 2));
59519
+ } else {
59520
+ console.log(source_default.green(` Agent ${agentId} updated`));
59521
+ }
59607
59522
  } catch (error49) {
59608
- handleApiError4(error49, "Agent update failed");
59523
+ handleApiError(error49, "Agent update failed", logger30);
59609
59524
  }
59610
59525
  });
59611
59526
  }