@trading-boy/cli 2.1.0 → 2.1.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.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Remote trading intelligence for traders and AI agents. Real-time market data, on-chain analytics, DeFi risk scoring, and adaptive learning outcomes — all from the command line.
4
4
 
5
- **Website:** [cabal.ventures](https://cabal.ventures) | **Docs:** [cabal.ventures/docs](https://cabal.ventures/docs)
5
+ **Website:** [tradingboy.ai](https://tradingboy.ai) | **Docs:** [tradingboy.ai/docs](https://tradingboy.ai/docs)
6
6
 
7
7
  ## Install
8
8
 
@@ -118,7 +118,7 @@ All plans include every feature. Limits apply to traded tokens and logged trades
118
118
  Trading Boy exposes an MCP server for AI agent integration and a REST API for programmatic access.
119
119
 
120
120
  ```bash
121
- # REST API is hosted at https://api.cabal.ventures
121
+ # REST API is hosted at https://api.tradingboy.ai
122
122
  # MCP server: npx @trading-boy/mcp-server
123
123
  # CLI onboarding and first-agent setup: see docs/getting-started.md
124
124
  ```
@@ -1,4 +1,4 @@
1
- import { redactApiKey } from './credentials.js';
1
+ import { redactApiKey, type StoredCredentials } from './credentials.js';
2
2
  export interface ApiResponse<T> {
3
3
  data: T;
4
4
  status: number;
@@ -8,11 +8,25 @@ export declare class ApiError extends Error {
8
8
  readonly body?: unknown | undefined;
9
9
  constructor(message: string, status: number, body?: unknown | undefined);
10
10
  }
11
+ export declare class ApiCredentialMissingError extends Error {
12
+ constructor();
13
+ }
14
+ export declare class ApiCredentialConflictError extends Error {
15
+ readonly sources: string[];
16
+ constructor(sources: string[]);
17
+ }
11
18
  /**
12
19
  * Dev mode: TRADING_BOY_API_URL points to localhost AND NODE_ENV=development.
13
20
  * In dev mode, auth is optional for a local API server.
14
21
  */
15
22
  export declare function isDevMode(): boolean;
23
+ export type ApiKeySourceName = 'TRADING_BOY_API_KEY env var' | '--api-key flag' | 'stored credentials';
24
+ export interface ResolvedApiKey {
25
+ apiKey: string;
26
+ source: ApiKeySourceName;
27
+ storedCredentials?: StoredCredentials;
28
+ }
29
+ export declare function resolveApiKeyWithSource(flagKey?: string): Promise<ResolvedApiKey>;
16
30
  export declare function resolveApiKey(flagKey?: string): Promise<string>;
17
31
  export declare function getApiBase(): string;
18
32
  export declare function apiRequest<T>(path: string, options?: {
@@ -2,7 +2,7 @@ import { createLogger } from './logger.js';
2
2
  import { loadCredentials, redactApiKey } from './credentials.js';
3
3
  const logger = createLogger('cli-api-client');
4
4
  // ─── Constants ───
5
- const DEFAULT_API_BASE = 'https://api.cabal.ventures';
5
+ const DEFAULT_API_BASE = 'https://api.tradingboy.ai';
6
6
  export class ApiError extends Error {
7
7
  status;
8
8
  body;
@@ -13,6 +13,21 @@ export class ApiError extends Error {
13
13
  this.name = 'ApiError';
14
14
  }
15
15
  }
16
+ export class ApiCredentialMissingError extends Error {
17
+ constructor() {
18
+ super('Not authenticated. Run `trading-boy login` to set up your API key.');
19
+ this.name = 'ApiCredentialMissingError';
20
+ }
21
+ }
22
+ export class ApiCredentialConflictError extends Error {
23
+ sources;
24
+ constructor(sources) {
25
+ super(`Conflicting API credentials found in ${sources.join(', ')}. ` +
26
+ 'Unset one credential source or make them match before running this command.');
27
+ this.sources = sources;
28
+ this.name = 'ApiCredentialConflictError';
29
+ }
30
+ }
16
31
  // ─── Dev Mode Detection ───
17
32
  /**
18
33
  * Dev mode: TRADING_BOY_API_URL points to localhost AND NODE_ENV=development.
@@ -32,24 +47,52 @@ export function isDevMode() {
32
47
  return false;
33
48
  }
34
49
  }
35
- // ─── API Key Resolution ───
36
- export async function resolveApiKey(flagKey) {
37
- // Priority: env var > flag > stored key
38
- const envKey = process.env.TRADING_BOY_API_KEY;
50
+ function normalizeCandidateKey(value) {
51
+ const trimmed = value?.trim();
52
+ return trimmed ? trimmed : null;
53
+ }
54
+ function assertNoCredentialConflicts(candidates) {
55
+ if (candidates.length <= 1)
56
+ return;
57
+ const first = candidates[0].apiKey;
58
+ const conflictingSources = candidates
59
+ .filter((candidate) => candidate.apiKey !== first)
60
+ .map((candidate) => candidate.source);
61
+ if (conflictingSources.length === 0)
62
+ return;
63
+ throw new ApiCredentialConflictError([
64
+ candidates[0].source,
65
+ ...conflictingSources,
66
+ ]);
67
+ }
68
+ export async function resolveApiKeyWithSource(flagKey) {
69
+ const candidates = [];
70
+ const envKey = normalizeCandidateKey(process.env.TRADING_BOY_API_KEY);
39
71
  if (envKey) {
40
- logger.debug('Using API key from TRADING_BOY_API_KEY env var');
41
- return envKey;
72
+ candidates.push({ apiKey: envKey, source: 'TRADING_BOY_API_KEY env var' });
42
73
  }
43
- if (flagKey) {
44
- logger.debug('Using API key from --api-key flag');
45
- return flagKey;
74
+ const normalizedFlagKey = normalizeCandidateKey(flagKey);
75
+ if (normalizedFlagKey) {
76
+ candidates.push({ apiKey: normalizedFlagKey, source: '--api-key flag' });
46
77
  }
47
78
  const stored = await loadCredentials();
48
79
  if (stored?.apiKey) {
49
- logger.debug('Using stored API key');
50
- return stored.apiKey;
80
+ candidates.push({
81
+ apiKey: stored.apiKey,
82
+ source: 'stored credentials',
83
+ storedCredentials: stored,
84
+ });
85
+ }
86
+ assertNoCredentialConflicts(candidates);
87
+ const resolved = candidates[0];
88
+ if (!resolved) {
89
+ throw new ApiCredentialMissingError();
51
90
  }
52
- throw new Error('Not authenticated. Run `trading-boy login` to set up your API key.');
91
+ logger.debug({ source: resolved.source }, 'Using API key');
92
+ return resolved;
93
+ }
94
+ export async function resolveApiKey(flagKey) {
95
+ return (await resolveApiKeyWithSource(flagKey)).apiKey;
53
96
  }
54
97
  // ─── HTTPS Enforcement ───
55
98
  function enforceHttps(url) {
@@ -77,7 +120,7 @@ export function getApiBase() {
77
120
  // ─── API Request ───
78
121
  export async function apiRequest(path, options = {}) {
79
122
  const dev = isDevMode();
80
- const apiKey = dev ? undefined : (options.apiKey ?? (await resolveApiKey()));
123
+ const apiKey = dev ? undefined : await resolveApiKey(options.apiKey);
81
124
  const apiBase = getApiBase();
82
125
  const url = `${apiBase}${path}`;
83
126
  const headers = {
@@ -19,11 +19,20 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
19
19
  if (typeof require !== "undefined") return require.apply(this, arguments);
20
20
  throw Error('Dynamic require of "' + x + '" is not supported');
21
21
  });
22
- var __esm = (fn, res) => function __init() {
23
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
22
+ var __esm = (fn, res, err) => function __init() {
23
+ if (err) throw err[0];
24
+ try {
25
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
26
+ } catch (e) {
27
+ throw err = [e], e;
28
+ }
24
29
  };
25
30
  var __commonJS = (cb, mod) => function __require2() {
26
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
31
+ try {
32
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
33
+ } catch (e) {
34
+ throw mod = 0, e;
35
+ }
27
36
  };
28
37
  var __export = (target, all) => {
29
38
  for (var name in all)
@@ -6979,13 +6988,16 @@ var init_credentials = __esm({
6979
6988
  // dist/api-client.js
6980
6989
  var api_client_exports = {};
6981
6990
  __export(api_client_exports, {
6991
+ ApiCredentialConflictError: () => ApiCredentialConflictError,
6992
+ ApiCredentialMissingError: () => ApiCredentialMissingError,
6982
6993
  ApiError: () => ApiError,
6983
6994
  apiRequest: () => apiRequest,
6984
6995
  getApiBase: () => getApiBase,
6985
6996
  isDevMode: () => isDevMode,
6986
6997
  isRemoteMode: () => isRemoteMode,
6987
6998
  redactApiKey: () => redactApiKey,
6988
- resolveApiKey: () => resolveApiKey
6999
+ resolveApiKey: () => resolveApiKey,
7000
+ resolveApiKeyWithSource: () => resolveApiKeyWithSource
6989
7001
  });
6990
7002
  function isDevMode() {
6991
7003
  if (process.env.NODE_ENV !== "development")
@@ -7000,22 +7012,50 @@ function isDevMode() {
7000
7012
  return false;
7001
7013
  }
7002
7014
  }
7003
- async function resolveApiKey(flagKey) {
7004
- const envKey = process.env.TRADING_BOY_API_KEY;
7015
+ function normalizeCandidateKey(value) {
7016
+ const trimmed = value?.trim();
7017
+ return trimmed ? trimmed : null;
7018
+ }
7019
+ function assertNoCredentialConflicts(candidates) {
7020
+ if (candidates.length <= 1)
7021
+ return;
7022
+ const first = candidates[0].apiKey;
7023
+ const conflictingSources = candidates.filter((candidate) => candidate.apiKey !== first).map((candidate) => candidate.source);
7024
+ if (conflictingSources.length === 0)
7025
+ return;
7026
+ throw new ApiCredentialConflictError([
7027
+ candidates[0].source,
7028
+ ...conflictingSources
7029
+ ]);
7030
+ }
7031
+ async function resolveApiKeyWithSource(flagKey) {
7032
+ const candidates = [];
7033
+ const envKey = normalizeCandidateKey(process.env.TRADING_BOY_API_KEY);
7005
7034
  if (envKey) {
7006
- logger2.debug("Using API key from TRADING_BOY_API_KEY env var");
7007
- return envKey;
7035
+ candidates.push({ apiKey: envKey, source: "TRADING_BOY_API_KEY env var" });
7008
7036
  }
7009
- if (flagKey) {
7010
- logger2.debug("Using API key from --api-key flag");
7011
- return flagKey;
7037
+ const normalizedFlagKey = normalizeCandidateKey(flagKey);
7038
+ if (normalizedFlagKey) {
7039
+ candidates.push({ apiKey: normalizedFlagKey, source: "--api-key flag" });
7012
7040
  }
7013
7041
  const stored = await loadCredentials();
7014
7042
  if (stored?.apiKey) {
7015
- logger2.debug("Using stored API key");
7016
- return stored.apiKey;
7043
+ candidates.push({
7044
+ apiKey: stored.apiKey,
7045
+ source: "stored credentials",
7046
+ storedCredentials: stored
7047
+ });
7048
+ }
7049
+ assertNoCredentialConflicts(candidates);
7050
+ const resolved = candidates[0];
7051
+ if (!resolved) {
7052
+ throw new ApiCredentialMissingError();
7017
7053
  }
7018
- throw new Error("Not authenticated. Run `trading-boy login` to set up your API key.");
7054
+ logger2.debug({ source: resolved.source }, "Using API key");
7055
+ return resolved;
7056
+ }
7057
+ async function resolveApiKey(flagKey) {
7058
+ return (await resolveApiKeyWithSource(flagKey)).apiKey;
7019
7059
  }
7020
7060
  function enforceHttps(url) {
7021
7061
  try {
@@ -7036,7 +7076,7 @@ function getApiBase() {
7036
7076
  }
7037
7077
  async function apiRequest(path3, options = {}) {
7038
7078
  const dev = isDevMode();
7039
- const apiKey = dev ? void 0 : options.apiKey ?? await resolveApiKey();
7079
+ const apiKey = dev ? void 0 : await resolveApiKey(options.apiKey);
7040
7080
  const apiBase = getApiBase();
7041
7081
  const url = `${apiBase}${path3}`;
7042
7082
  const headers = {
@@ -7109,14 +7149,14 @@ async function isRemoteMode() {
7109
7149
  const stored = await loadCredentials();
7110
7150
  return !!stored?.apiKey;
7111
7151
  }
7112
- var logger2, DEFAULT_API_BASE, ApiError;
7152
+ var logger2, DEFAULT_API_BASE, ApiError, ApiCredentialMissingError, ApiCredentialConflictError;
7113
7153
  var init_api_client = __esm({
7114
7154
  "dist/api-client.js"() {
7115
7155
  "use strict";
7116
7156
  init_logger();
7117
7157
  init_credentials();
7118
7158
  logger2 = createLogger2("cli-api-client");
7119
- DEFAULT_API_BASE = "https://api.cabal.ventures";
7159
+ DEFAULT_API_BASE = "https://api.tradingboy.ai";
7120
7160
  ApiError = class extends Error {
7121
7161
  status;
7122
7162
  body;
@@ -7127,6 +7167,20 @@ var init_api_client = __esm({
7127
7167
  this.name = "ApiError";
7128
7168
  }
7129
7169
  };
7170
+ ApiCredentialMissingError = class extends Error {
7171
+ constructor() {
7172
+ super("Not authenticated. Run `trading-boy login` to set up your API key.");
7173
+ this.name = "ApiCredentialMissingError";
7174
+ }
7175
+ };
7176
+ ApiCredentialConflictError = class extends Error {
7177
+ sources;
7178
+ constructor(sources) {
7179
+ super(`Conflicting API credentials found in ${sources.join(", ")}. Unset one credential source or make them match before running this command.`);
7180
+ this.sources = sources;
7181
+ this.name = "ApiCredentialConflictError";
7182
+ }
7183
+ };
7130
7184
  }
7131
7185
  });
7132
7186
 
@@ -21425,6 +21479,39 @@ function createLogger(_name) {
21425
21479
  }
21426
21480
 
21427
21481
  // src/public-core-shim.ts
21482
+ var LLM_REASONING_EFFORTS = ["minimal", "low", "medium", "high", "xhigh"];
21483
+ var EXIT_REASONER_MODES = ["off", "observe", "notify", "protect", "manage", "full"];
21484
+ var EXIT_STYLES = ["scalp", "swing", "runner", "defensive", "manual"];
21485
+ var EXIT_TIMEFRAMES = ["scalp", "swing", "runner"];
21486
+ var CUSTOMER_API_KEY_V0_PATTERN = /^(tb_(?:live|test|free)_)([a-f0-9]{32})$/;
21487
+ var CUSTOMER_API_KEY_V1_PATTERN = /^(tb_(live|test)_v1_)([A-Za-z0-9_-]{16})\.([A-Za-z0-9_-]{32})$/;
21488
+ function legacyEnvironmentFromPrefix(prefix) {
21489
+ return prefix === "tb_live_" ? "live" : "test";
21490
+ }
21491
+ function parseCustomerApiKey(apiKey) {
21492
+ if (apiKey.trim() !== apiKey) return null;
21493
+ const v1 = apiKey.match(CUSTOMER_API_KEY_V1_PATTERN);
21494
+ if (v1) {
21495
+ const environment = v1[2];
21496
+ const keyId = v1[3];
21497
+ const secret = v1[4];
21498
+ return {
21499
+ version: 1,
21500
+ environment,
21501
+ keyId,
21502
+ secret,
21503
+ keyPrefix: `${v1[1]}${keyId}`,
21504
+ last4: secret.slice(-4)
21505
+ };
21506
+ }
21507
+ const legacy = apiKey.match(CUSTOMER_API_KEY_V0_PATTERN);
21508
+ if (!legacy) return null;
21509
+ return {
21510
+ version: 0,
21511
+ environment: legacyEnvironmentFromPrefix(legacy[1]),
21512
+ keyPrefix: legacy[1] + legacy[2].substring(0, 8)
21513
+ };
21514
+ }
21428
21515
  function normalizeWatchlistSymbol(value) {
21429
21516
  const trimmed = value.trim();
21430
21517
  if (trimmed.length === 0) return trimmed;
@@ -22208,8 +22295,8 @@ function formatPnl(pnl) {
22208
22295
  if (pnl === null) {
22209
22296
  return source_default.dim("-");
22210
22297
  }
22211
- const sign = pnl >= 0 ? "+" : "";
22212
- const str = `${sign}$${pnl.toFixed(2)}`;
22298
+ const sign = pnl >= 0 ? "+" : "-";
22299
+ const str = `${sign}$${Math.abs(pnl).toFixed(2)}`;
22213
22300
  return pnl > 0 ? source_default.green(str) : pnl < 0 ? source_default.red(str) : source_default.dim(str);
22214
22301
  }
22215
22302
  function formatPercent(pct) {
@@ -24480,6 +24567,9 @@ init_logger();
24480
24567
  init_api_client();
24481
24568
  init_utils();
24482
24569
  var logger14 = createLogger2("cli-public-config");
24570
+ function formatModelReasoning(model, reasoningEffort) {
24571
+ return reasoningEffort ? `${model} (${reasoningEffort})` : model;
24572
+ }
24483
24573
  function printLlmConfig(result) {
24484
24574
  console.log("");
24485
24575
  console.log(source_default.bold.cyan(" LLM Configuration"));
@@ -24488,21 +24578,21 @@ function printLlmConfig(result) {
24488
24578
  if (result.effectiveProvider && (result.effectiveProvider !== result.provider || result.effectiveModel !== result.model)) {
24489
24579
  console.log(` ${source_default.gray("Effective:")} ${result.effectiveProvider} / ${result.effectiveModel ?? result.model}`);
24490
24580
  }
24491
- console.log(` ${source_default.gray("Model:")} ${result.model}`);
24492
- if (result.scanProvider || result.scanModel) {
24493
- console.log(` ${source_default.gray("Scan:")} ${result.scanProvider ?? result.provider} / ${result.scanModel ?? result.model}`);
24581
+ console.log(` ${source_default.gray("Model:")} ${formatModelReasoning(result.model, result.reasoningEffort)}`);
24582
+ if (result.scanProvider || result.scanModel || result.scanReasoningEffort) {
24583
+ console.log(` ${source_default.gray("Scan:")} ${result.scanProvider ?? result.provider} / ${formatModelReasoning(result.scanModel ?? result.model, result.scanReasoningEffort ?? result.reasoningEffort)}`);
24494
24584
  }
24495
- if (result.analyzeProvider || result.analyzeModel) {
24496
- console.log(` ${source_default.gray("Analyze:")} ${result.analyzeProvider ?? result.provider} / ${result.analyzeModel ?? result.model}`);
24585
+ if (result.analyzeProvider || result.analyzeModel || result.analyzeReasoningEffort) {
24586
+ console.log(` ${source_default.gray("Analyze:")} ${result.analyzeProvider ?? result.provider} / ${formatModelReasoning(result.analyzeModel ?? result.model, result.analyzeReasoningEffort ?? result.reasoningEffort)}`);
24497
24587
  }
24498
- if (result.decideProvider || result.decideModel) {
24499
- console.log(` ${source_default.gray("Decide:")} ${result.decideProvider ?? result.provider} / ${result.decideModel ?? result.model}`);
24588
+ if (result.decideProvider || result.decideModel || result.decideReasoningEffort) {
24589
+ console.log(` ${source_default.gray("Decide:")} ${result.decideProvider ?? result.provider} / ${formatModelReasoning(result.decideModel ?? result.model, result.decideReasoningEffort ?? result.reasoningEffort)}`);
24500
24590
  }
24501
- if (result.exitHeartbeatModel) {
24502
- console.log(` ${source_default.gray("Exit HB:")} ${result.exitHeartbeatModel}`);
24591
+ if (result.exitHeartbeatModel || result.exitHeartbeatReasoningEffort) {
24592
+ console.log(` ${source_default.gray("Exit HB:")} ${formatModelReasoning(result.exitHeartbeatModel ?? result.model, result.exitHeartbeatReasoningEffort ?? result.reasoningEffort)}`);
24503
24593
  }
24504
- if (result.exitEventModel) {
24505
- console.log(` ${source_default.gray("Exit Event:")} ${result.exitEventModel}`);
24594
+ if (result.exitEventModel || result.exitEventReasoningEffort) {
24595
+ console.log(` ${source_default.gray("Exit Event:")} ${formatModelReasoning(result.exitEventModel ?? result.model, result.exitEventReasoningEffort ?? result.reasoningEffort)}`);
24506
24596
  }
24507
24597
  if (result.baseUrl) {
24508
24598
  console.log(` ${source_default.gray("Base URL:")} ${result.baseUrl}`);
@@ -24566,7 +24656,44 @@ function registerConfigCommand(program2) {
24566
24656
  }
24567
24657
  printLlmConfig(llmConfig);
24568
24658
  });
24569
- configCmd.command("set-llm-key <apiKey>").description("Store your LLM provider API key for agents, thesis extraction, and coaching (BYOK)").addOption(new Option("-p, --provider <provider>", "LLM provider (auto-detected from key prefix if omitted)").choices(["anthropic", "openai", "openrouter", "ollama", "gemini", "groq", "deepseek", "codex", "custom"])).option("-m, --model <model>", "Model name (default for all phases)").option("--base-url <url>", "Custom base URL (for openrouter/ollama/custom providers)").option("--scan-model <model>", "Model for market scanning").option("--analyze-model <model>", "Model for deep analysis").option("--decide-model <model>", "Model for trade decisions").addOption(new Option("--scan-provider <provider>", "Provider for scan phase").choices(["anthropic", "openai", "openrouter", "ollama", "gemini", "groq", "deepseek", "custom"])).option("--scan-key <key>", "LLM provider API key for the scan phase").addOption(new Option("--analyze-provider <provider>", "Provider for analyze phase").choices(["anthropic", "openai", "openrouter", "ollama", "gemini", "groq", "deepseek", "custom"])).option("--analyze-key <key>", "LLM provider API key for the analyze phase").addOption(new Option("--decide-provider <provider>", "Provider for decide phase").choices(["anthropic", "openai", "openrouter", "ollama", "gemini", "groq", "deepseek", "custom"])).option("--decide-key <key>", "LLM provider API key for the decide phase").action(async (apiKey, opts) => {
24659
+ configCmd.command("set-llm-key <apiKey>").description("Store your LLM provider API key for agents, thesis extraction, and coaching (BYOK)").addOption(new Option("-p, --provider <provider>", "LLM provider (auto-detected from key prefix if omitted)").choices([
24660
+ "anthropic",
24661
+ "openai",
24662
+ "openrouter",
24663
+ "ollama",
24664
+ "gemini",
24665
+ "groq",
24666
+ "deepseek",
24667
+ "codex",
24668
+ "custom"
24669
+ ])).option("-m, --model <model>", "Model name (default for all phases)").option("--base-url <url>", "Custom base URL (for openrouter/ollama/custom providers)").option("--scan-model <model>", "Model for market scanning").option("--analyze-model <model>", "Model for deep analysis").option("--decide-model <model>", "Model for trade decisions").addOption(new Option("--scan-provider <provider>", "Provider for scan phase").choices([
24670
+ "anthropic",
24671
+ "openai",
24672
+ "openrouter",
24673
+ "ollama",
24674
+ "gemini",
24675
+ "groq",
24676
+ "deepseek",
24677
+ "custom"
24678
+ ])).option("--scan-key <key>", "LLM provider API key for the scan phase").addOption(new Option("--analyze-provider <provider>", "Provider for analyze phase").choices([
24679
+ "anthropic",
24680
+ "openai",
24681
+ "openrouter",
24682
+ "ollama",
24683
+ "gemini",
24684
+ "groq",
24685
+ "deepseek",
24686
+ "custom"
24687
+ ])).option("--analyze-key <key>", "LLM provider API key for the analyze phase").addOption(new Option("--decide-provider <provider>", "Provider for decide phase").choices([
24688
+ "anthropic",
24689
+ "openai",
24690
+ "openrouter",
24691
+ "ollama",
24692
+ "gemini",
24693
+ "groq",
24694
+ "deepseek",
24695
+ "custom"
24696
+ ])).option("--decide-key <key>", "LLM provider API key for the decide phase").action(async (apiKey, opts) => {
24570
24697
  if (!await ensureRemote())
24571
24698
  return;
24572
24699
  try {
@@ -24615,12 +24742,14 @@ function registerConfigCommand(program2) {
24615
24742
  process.exitCode = error2 instanceof ApiError ? 2 : 1;
24616
24743
  }
24617
24744
  });
24618
- configCmd.command("set-models").description("Update per-stage model assignments (works with BYOK or a ChatGPT connection)").option("-m, --model <model>", "Default model for all phases").option("--scan-model <model>", "Model for market scanning").option("--analyze-model <model>", "Model for deep analysis").option("--decide-model <model>", "Model for trade decisions").option("--exit-heartbeat-model <model>", "Model for exit heartbeat checks").option("--exit-event-model <model>", "Model for exit event-driven analysis").action(async (opts) => {
24745
+ configCmd.command("set-models").description("Update per-stage model assignments (works with BYOK or a ChatGPT connection)").option("-m, --model <model>", "Default model for all phases").addOption(new Option("--reasoning-effort <effort>", "Default reasoning effort").choices([
24746
+ ...LLM_REASONING_EFFORTS
24747
+ ])).option("--scan-model <model>", "Model for market scanning").addOption(new Option("--scan-reasoning-effort <effort>", "Reasoning effort for market scanning").choices([...LLM_REASONING_EFFORTS])).option("--analyze-model <model>", "Model for deep analysis").addOption(new Option("--analyze-reasoning-effort <effort>", "Reasoning effort for deep analysis").choices([...LLM_REASONING_EFFORTS])).option("--decide-model <model>", "Model for trade decisions").addOption(new Option("--decide-reasoning-effort <effort>", "Reasoning effort for trade decisions").choices([...LLM_REASONING_EFFORTS])).option("--exit-heartbeat-model <model>", "Model for exit heartbeat checks").addOption(new Option("--exit-heartbeat-reasoning-effort <effort>", "Reasoning effort for exit heartbeat checks").choices([...LLM_REASONING_EFFORTS])).option("--exit-event-model <model>", "Model for exit event-driven analysis").addOption(new Option("--exit-event-reasoning-effort <effort>", "Reasoning effort for exit event-driven analysis").choices([...LLM_REASONING_EFFORTS])).action(async (opts) => {
24619
24748
  if (!await ensureRemote())
24620
24749
  return;
24621
- if (!opts.model && !opts.scanModel && !opts.analyzeModel && !opts.decideModel && !opts.exitHeartbeatModel && !opts.exitEventModel) {
24622
- console.error(source_default.red(" Error: At least one model flag is required."));
24623
- console.log(source_default.dim(" Example: trading-boy config set-models --scan-model gpt-5.4-mini --analyze-model gpt-5.4"));
24750
+ if (!opts.model && !opts.reasoningEffort && !opts.scanModel && !opts.scanReasoningEffort && !opts.analyzeModel && !opts.analyzeReasoningEffort && !opts.decideModel && !opts.decideReasoningEffort && !opts.exitHeartbeatModel && !opts.exitHeartbeatReasoningEffort && !opts.exitEventModel && !opts.exitEventReasoningEffort) {
24751
+ console.error(source_default.red(" Error: At least one model or reasoning flag is required."));
24752
+ console.log(source_default.dim(" Example: trading-boy config set-models --model gpt-5.5 --reasoning-effort low"));
24624
24753
  process.exitCode = 1;
24625
24754
  return;
24626
24755
  }
@@ -24629,27 +24758,33 @@ function registerConfigCommand(program2) {
24629
24758
  method: "PATCH",
24630
24759
  body: {
24631
24760
  ...opts.model ? { model: opts.model } : {},
24761
+ ...opts.reasoningEffort ? { reasoningEffort: opts.reasoningEffort } : {},
24632
24762
  ...opts.scanModel ? { scanModel: opts.scanModel } : {},
24763
+ ...opts.scanReasoningEffort ? { scanReasoningEffort: opts.scanReasoningEffort } : {},
24633
24764
  ...opts.analyzeModel ? { analyzeModel: opts.analyzeModel } : {},
24765
+ ...opts.analyzeReasoningEffort ? { analyzeReasoningEffort: opts.analyzeReasoningEffort } : {},
24634
24766
  ...opts.decideModel ? { decideModel: opts.decideModel } : {},
24767
+ ...opts.decideReasoningEffort ? { decideReasoningEffort: opts.decideReasoningEffort } : {},
24635
24768
  ...opts.exitHeartbeatModel ? { exitHeartbeatModel: opts.exitHeartbeatModel } : {},
24636
- ...opts.exitEventModel ? { exitEventModel: opts.exitEventModel } : {}
24769
+ ...opts.exitHeartbeatReasoningEffort ? { exitHeartbeatReasoningEffort: opts.exitHeartbeatReasoningEffort } : {},
24770
+ ...opts.exitEventModel ? { exitEventModel: opts.exitEventModel } : {},
24771
+ ...opts.exitEventReasoningEffort ? { exitEventReasoningEffort: opts.exitEventReasoningEffort } : {}
24637
24772
  }
24638
24773
  });
24639
24774
  console.log("");
24640
- console.log(source_default.green(" Stage models updated"));
24775
+ console.log(source_default.green(" Stage LLM settings updated"));
24641
24776
  console.log(source_default.gray(" " + "\u2500".repeat(40)));
24642
- console.log(` ${source_default.gray("Default:")} ${result.model}`);
24643
- if (result.scanModel)
24644
- console.log(` ${source_default.gray("Scan:")} ${result.scanModel}`);
24645
- if (result.analyzeModel)
24646
- console.log(` ${source_default.gray("Analyze:")} ${result.analyzeModel}`);
24647
- if (result.decideModel)
24648
- console.log(` ${source_default.gray("Decide:")} ${result.decideModel}`);
24649
- if (result.exitHeartbeatModel)
24650
- console.log(` ${source_default.gray("Exit heartbeat:")} ${result.exitHeartbeatModel}`);
24651
- if (result.exitEventModel)
24652
- console.log(` ${source_default.gray("Exit event:")} ${result.exitEventModel}`);
24777
+ console.log(` ${source_default.gray("Default:")} ${formatModelReasoning(result.model, result.reasoningEffort)}`);
24778
+ if (result.scanModel || result.scanReasoningEffort)
24779
+ console.log(` ${source_default.gray("Scan:")} ${formatModelReasoning(result.scanModel ?? result.model, result.scanReasoningEffort ?? result.reasoningEffort)}`);
24780
+ if (result.analyzeModel || result.analyzeReasoningEffort)
24781
+ console.log(` ${source_default.gray("Analyze:")} ${formatModelReasoning(result.analyzeModel ?? result.model, result.analyzeReasoningEffort ?? result.reasoningEffort)}`);
24782
+ if (result.decideModel || result.decideReasoningEffort)
24783
+ console.log(` ${source_default.gray("Decide:")} ${formatModelReasoning(result.decideModel ?? result.model, result.decideReasoningEffort ?? result.reasoningEffort)}`);
24784
+ if (result.exitHeartbeatModel || result.exitHeartbeatReasoningEffort)
24785
+ console.log(` ${source_default.gray("Exit heartbeat:")} ${formatModelReasoning(result.exitHeartbeatModel ?? result.model, result.exitHeartbeatReasoningEffort ?? result.reasoningEffort)}`);
24786
+ if (result.exitEventModel || result.exitEventReasoningEffort)
24787
+ console.log(` ${source_default.gray("Exit event:")} ${formatModelReasoning(result.exitEventModel ?? result.model, result.exitEventReasoningEffort ?? result.reasoningEffort)}`);
24653
24788
  console.log("");
24654
24789
  console.log(source_default.dim(" Agents will use these models on their next tick."));
24655
24790
  console.log("");
@@ -24712,9 +24847,9 @@ init_source();
24712
24847
  init_credentials();
24713
24848
  init_api_client();
24714
24849
  var logger15 = createLogger("cli-login");
24715
- var API_KEY_PATTERN = /^tb_(live|test|free)_[a-f0-9]{32}$/;
24850
+ var API_KEY_FORMAT_HELP = "Expected: tb_live_v1_<keyId>.<secret>, tb_test_v1_<keyId>.<secret>, or legacy tb_live_<32hex>/tb_test_<32hex>/tb_free_<32hex>";
24716
24851
  function validateApiKeyFormat(key) {
24717
- return API_KEY_PATTERN.test(key);
24852
+ return parseCustomerApiKey(key) !== null;
24718
24853
  }
24719
24854
  async function verifyApiKey(apiKey) {
24720
24855
  const apiBase = getApiBase();
@@ -24776,14 +24911,14 @@ function registerLoginCommand(program2) {
24776
24911
  if (!input)
24777
24912
  return "Trading Boy API key is required";
24778
24913
  if (!validateApiKeyFormat(input)) {
24779
- return "Invalid Trading Boy API key format. Expected: tb_live_<32hex>, tb_test_<32hex>, or tb_free_<32hex>";
24914
+ return `Invalid Trading Boy API key format. ${API_KEY_FORMAT_HELP}`;
24780
24915
  }
24781
24916
  return true;
24782
24917
  }
24783
24918
  });
24784
24919
  }
24785
24920
  if (!validateApiKeyFormat(apiKey)) {
24786
- console.error(source_default.red(" Invalid Trading Boy API key format. Expected: tb_live_<32hex>, tb_test_<32hex>, or tb_free_<32hex>"));
24921
+ console.error(source_default.red(` Invalid Trading Boy API key format. ${API_KEY_FORMAT_HELP}`));
24787
24922
  process.exitCode = 1;
24788
24923
  return;
24789
24924
  }
@@ -24915,34 +25050,31 @@ init_credentials();
24915
25050
  init_api_client();
24916
25051
  var logger17 = createLogger("cli-whoami");
24917
25052
  async function executeWhoami() {
24918
- const envKey = process.env.TRADING_BOY_API_KEY;
24919
- if (envKey) {
24920
- return {
24921
- authenticated: true,
24922
- redactedKey: redactApiKey(envKey),
24923
- storageMethod: "TRADING_BOY_API_KEY env var"
24924
- };
24925
- }
24926
- const creds = await loadCredentials();
24927
- if (!creds) {
24928
- return { authenticated: false };
25053
+ let resolved;
25054
+ try {
25055
+ resolved = await resolveApiKeyWithSource();
25056
+ } catch (error2) {
25057
+ if (error2 instanceof ApiCredentialMissingError) {
25058
+ return { authenticated: false };
25059
+ }
25060
+ throw error2;
24929
25061
  }
25062
+ const creds = resolved.source === "stored credentials" ? resolved.storedCredentials ?? await loadCredentials() : null;
24930
25063
  const result = {
24931
25064
  authenticated: true,
24932
- email: creds.email,
24933
- plan: creds.plan,
24934
- keyId: creds.keyId,
24935
- redactedKey: redactApiKey(creds.apiKey),
24936
- storageMethod: creds.storageMethod,
24937
- storedAt: creds.storedAt
25065
+ email: creds?.email,
25066
+ plan: creds?.plan,
25067
+ keyId: creds?.keyId,
25068
+ redactedKey: redactApiKey(resolved.apiKey),
25069
+ storageMethod: resolved.source === "stored credentials" ? creds?.storageMethod : resolved.source,
25070
+ storedAt: creds?.storedAt
24938
25071
  };
24939
25072
  try {
24940
- const apiKey = await resolveApiKey();
24941
25073
  const response = await fetch(`${getApiBase()}/api/v1/auth/verify`, {
24942
25074
  method: "POST",
24943
25075
  headers: {
24944
25076
  "Content-Type": "application/json",
24945
- "Authorization": `Bearer ${apiKey}`
25077
+ "Authorization": `Bearer ${resolved.apiKey}`
24946
25078
  },
24947
25079
  body: "{}"
24948
25080
  });
@@ -25011,16 +25143,9 @@ function registerWhoamiCommand(program2) {
25011
25143
  try {
25012
25144
  const result = await executeWhoami();
25013
25145
  if (options.showKey && result.authenticated) {
25014
- const envKey = process.env.TRADING_BOY_API_KEY;
25015
- if (envKey) {
25016
- console.log(envKey);
25017
- return;
25018
- }
25019
- const creds = await loadCredentials();
25020
- if (creds) {
25021
- console.log(creds.apiKey);
25022
- return;
25023
- }
25146
+ const resolved = await resolveApiKeyWithSource();
25147
+ console.log(resolved.apiKey);
25148
+ return;
25024
25149
  }
25025
25150
  if (options.format === "json") {
25026
25151
  console.log(JSON.stringify({ ...result, apiUrl: getApiBase() }, null, 2));
@@ -25735,7 +25860,7 @@ function printExploreQuickReference() {
25735
25860
  console.log(` ${source_default.white("trading-boy stats")} ${source_default.dim("Track performance")}`);
25736
25861
  console.log("");
25737
25862
  console.log(source_default.dim(" Build an agent later: trading-boy onboarding"));
25738
- console.log(source_default.dim(" Full docs: https://api.cabal.ventures/docs"));
25863
+ console.log(source_default.dim(" Full docs: https://api.tradingboy.ai/docs"));
25739
25864
  console.log("");
25740
25865
  }
25741
25866
  function printAgentRuntimeOverview() {
@@ -25779,7 +25904,7 @@ function printAgentQuickReference(traderRegistered) {
25779
25904
  console.log(` ${source_default.white("trading-boy behavioral profile --trader <trader-id>")} ${source_default.dim("Behavioral analysis")}`);
25780
25905
  }
25781
25906
  console.log("");
25782
- console.log(source_default.dim(" Full docs: https://api.cabal.ventures/docs"));
25907
+ console.log(source_default.dim(" Full docs: https://api.tradingboy.ai/docs"));
25783
25908
  console.log("");
25784
25909
  }
25785
25910
  async function resolveTraderId(nameOrId) {
@@ -26208,7 +26333,7 @@ async function pollForApiKey(token, onTick) {
26208
26333
  if (result.status === "expired" || result.status === "not_found") {
26209
26334
  return {
26210
26335
  success: false,
26211
- error: "Provisioning token expired. Your API key will be emailed to you shortly. Then run: `trading-boy login`"
26336
+ error: "Provisioning token expired or was not found. Run `trading-boy forgot-key -e <email>` to request a one-time HTTPS recovery link. Full API keys are never emailed."
26212
26337
  };
26213
26338
  }
26214
26339
  if (result.status === "already_retrieved") {
@@ -26225,7 +26350,7 @@ async function pollForApiKey(token, onTick) {
26225
26350
  }
26226
26351
  return {
26227
26352
  success: false,
26228
- error: "Timed out waiting for payment confirmation. Your API key will be emailed to you shortly. Then run: `trading-boy login`"
26353
+ error: "Timed out waiting for payment confirmation. Check your payment status, rerun `trading-boy subscribe`, or use `trading-boy forgot-key -e <email>` after activation. Full API keys are never emailed."
26229
26354
  };
26230
26355
  }
26231
26356
  async function saveApiKey(apiKey, metadata) {
@@ -26537,7 +26662,7 @@ function sleep2(ms) {
26537
26662
  var ALLOWED_CHECKOUT_DOMAINS = /* @__PURE__ */ new Set([
26538
26663
  "checkout.stripe.com",
26539
26664
  "billing.stripe.com",
26540
- "api.cabal.ventures"
26665
+ "api.tradingboy.ai"
26541
26666
  ]);
26542
26667
  function validateBrowserUrl(url) {
26543
26668
  const parsed = new URL(url);
@@ -26693,7 +26818,7 @@ function formatElapsed2(ms) {
26693
26818
  return `${seconds}s`;
26694
26819
  }
26695
26820
  function registerForgotKeyCommand(program2) {
26696
- program2.command("forgot-key").description("Recover your API key via email").requiredOption("-e, --email <email>", "Your account email address").action(async (options) => {
26821
+ program2.command("forgot-key").description("Recover your API key with a one-time HTTPS claim link").requiredOption("-e, --email <email>", "Your account email address").action(async (options) => {
26697
26822
  const { email } = options;
26698
26823
  if (!isValidEmail2(email)) {
26699
26824
  console.error(source_default.red(" Invalid email address."));
@@ -26708,6 +26833,7 @@ function registerForgotKeyCommand(program2) {
26708
26833
  const recovery = await requestKeyRecovery(email);
26709
26834
  console.log(source_default.white(" Recovery email sent! Check your inbox."));
26710
26835
  console.log(source_default.dim(" Click the link in the email to provision a new key."));
26836
+ console.log(source_default.dim(" Full API keys are never emailed."));
26711
26837
  console.log("");
26712
26838
  let pollResult;
26713
26839
  try {
@@ -26842,9 +26968,9 @@ function formatAttributionOutput(data) {
26842
26968
  }
26843
26969
  lines.push(source_default.bold(" Summary"));
26844
26970
  lines.push(` ${source_default.gray("Total PnL:")} ${colorPnl(s.totalPnlUsd ?? 0)}`);
26845
- lines.push(` ${source_default.gray("Signal Alpha:")} ${formatPct2(s.avgSignalAlphaPct ?? 0)}`);
26846
- lines.push(` ${source_default.gray("Execution Cost:")} ${formatPct2(s.avgExecutionCostPct ?? 0)}`);
26847
- lines.push(` ${source_default.gray("Timing Variance:")} ${formatPct2(s.avgTimingVariancePct ?? 0)}`);
26971
+ lines.push(` ${source_default.gray("Signal Alpha:")} ${formatPctPoints(s.avgSignalAlphaPct ?? 0)}`);
26972
+ lines.push(` ${source_default.gray("Execution Cost:")} ${formatPctPoints(s.avgExecutionCostPct ?? 0)}`);
26973
+ lines.push(` ${source_default.gray("Timing Variance:")} ${formatPctPoints(s.avgTimingVariancePct ?? 0)}`);
26848
26974
  lines.push(` ${source_default.gray("Trade Count:")} ${s.trades ?? 0}`);
26849
26975
  const setupEntries = Object.entries(data.analysis.bySetupType ?? {});
26850
26976
  if (setupEntries.length > 0) {
@@ -26853,7 +26979,7 @@ function formatAttributionOutput(data) {
26853
26979
  lines.push(` ${source_default.gray("Setup".padEnd(20))}${"Signal%".padStart(10)}${"Exec%".padStart(10)}${"Timing%".padStart(10)}${"N".padStart(5)}`);
26854
26980
  lines.push(source_default.gray(" " + "\u2500".repeat(55)));
26855
26981
  for (const [setup, v] of setupEntries) {
26856
- lines.push(` ${setup.padEnd(20)}${formatPct2(v.avgSignalAlphaPct).padStart(10)}${formatPct2(v.avgExecutionCostPct).padStart(10)}${formatPct2(v.avgTimingVariancePct).padStart(10)}${String(v.trades).padStart(5)}`);
26982
+ lines.push(` ${setup.padEnd(20)}${formatPctPoints(v.avgSignalAlphaPct).padStart(10)}${formatPctPoints(v.avgExecutionCostPct).padStart(10)}${formatPctPoints(v.avgTimingVariancePct).padStart(10)}${String(v.trades).padStart(5)}`);
26857
26983
  }
26858
26984
  }
26859
26985
  if (data.analysis.attributions.length > 0) {
@@ -26888,13 +27014,17 @@ function colorPnl(usd) {
26888
27014
  }
26889
27015
  function formatUsd4(val) {
26890
27016
  const v = val ?? 0;
26891
- const sign = v >= 0 ? "+" : "";
26892
- return `${sign}$${v.toFixed(2)}`;
27017
+ const sign = v >= 0 ? "+" : "-";
27018
+ return `${sign}$${Math.abs(v).toFixed(2)}`;
26893
27019
  }
26894
27020
  function formatPct2(val) {
26895
27021
  const v = val ?? 0;
26896
27022
  return `${v >= 0 ? "+" : ""}${(v * 100).toFixed(1)}%`;
26897
27023
  }
27024
+ function formatPctPoints(val) {
27025
+ const v = val ?? 0;
27026
+ return `${v >= 0 ? "+" : ""}${v.toFixed(1)}%`;
27027
+ }
26898
27028
  function registerEdgeCommand(program2) {
26899
27029
  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) => {
26900
27030
  try {
@@ -28247,6 +28377,8 @@ var AUTONOMY_HELP_UPDATE = "Autonomy mode. Choose Fully auto (FULLY_AUTONOMOUS),
28247
28377
  var MIN_CONFIDENCE_HELP = "Minimum confidence threshold (0-1, so 0.70 = 70%)";
28248
28378
  var EXIT_REASONER_HELP = "Enable LLM-powered exit reasoning for open positions (hold, exit, tighten stop, or extend take profit)";
28249
28379
  var DISABLE_EXIT_REASONER_HELP = "Disable LLM-powered exit reasoning for open positions";
28380
+ var EXIT_REASONER_MODE_HELP = "Exit reasoner authority mode for open positions";
28381
+ var URGENT_EVENT_EXIT_MODE_HELP = "Urgent event exit review mode; cannot exceed the normal exit reasoner mode";
28250
28382
  var SHARED_SCAN_CACHE_INTERNAL_HELP = "Internal/load-test only: bypass shared scan cache for this agent";
28251
28383
  var ENABLE_SHARED_SCAN_CACHE_HELP = "Internal/load-test only: re-enable shared scan cache for this agent";
28252
28384
  function formatShortDate5(isoString) {
@@ -28311,7 +28443,7 @@ function parseHumanInterval2(value) {
28311
28443
  var MIN_SCAN_INTERVAL_MS2 = 6e4;
28312
28444
  function registerAgentCommand(program2) {
28313
28445
  const agent = program2.command("agent").description("Manage autonomous trading agents");
28314
- agent.command("create").description("Create a new agent").option("--trader-id <traderId>", "Trader ID").option("--strategy-id <strategyId>", "Strategy ID (optional \u2014 auto-creates if omitted)").option("--name <name>", "Agent name").option("--autonomy <level>", AUTONOMY_HELP_CREATE, "FULLY_AUTONOMOUS").option("--scan-interval <ms>", "Scan interval in raw milliseconds (min 60000; e.g. 300000 = 5m)", "300000").option("--scan-interval-human <duration>", "Scan interval in human-readable format (e.g. 1m, 5m, 15m, 30m, 1h). Overrides --scan-interval").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_HELP, "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").option("--disable-shared-scan-cache", SHARED_SCAN_CACHE_INTERNAL_HELP).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").option("--exit-reasoner", EXIT_REASONER_HELP).addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (options) => {
28446
+ agent.command("create").description("Create a new agent").option("--trader-id <traderId>", "Trader ID").option("--strategy-id <strategyId>", "Strategy ID (optional \u2014 auto-creates if omitted)").option("--name <name>", "Agent name").option("--autonomy <level>", AUTONOMY_HELP_CREATE, "FULLY_AUTONOMOUS").option("--scan-interval <ms>", "Scan interval in raw milliseconds (min 60000; e.g. 300000 = 5m)", "300000").option("--scan-interval-human <duration>", "Scan interval in human-readable format (e.g. 1m, 5m, 15m, 30m, 1h). Overrides --scan-interval").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_HELP, "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").option("--disable-shared-scan-cache", SHARED_SCAN_CACHE_INTERNAL_HELP).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").option("--exit-reasoner", EXIT_REASONER_HELP).addOption(new Option("--exit-reasoner-mode <mode>", EXIT_REASONER_MODE_HELP).choices([...EXIT_REASONER_MODES])).addOption(new Option("--urgent-event-exit-mode <mode>", URGENT_EVENT_EXIT_MODE_HELP).choices([...EXIT_REASONER_MODES])).addOption(new Option("--exit-style <style>", "Exit management preset").choices([...EXIT_STYLES])).addOption(new Option("--exit-timeframe <timeframe>", "Strategy exit timing preset").choices([...EXIT_TIMEFRAMES])).addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (options) => {
28315
28447
  if (!await ensureRemote())
28316
28448
  return;
28317
28449
  if (!options.traderId) {
@@ -28367,6 +28499,14 @@ function registerAgentCommand(program2) {
28367
28499
  body.assetClass = options.assetClass;
28368
28500
  if (options.exitReasoner)
28369
28501
  body.exitReasonerEnabled = true;
28502
+ if (options.exitReasonerMode)
28503
+ body.exitReasonerMode = options.exitReasonerMode;
28504
+ if (options.urgentEventExitMode)
28505
+ body.urgentEventExitReviewMode = options.urgentEventExitMode;
28506
+ if (options.exitStyle)
28507
+ body.exitStyle = options.exitStyle;
28508
+ if (options.exitTimeframe)
28509
+ body.exitTimingPolicy = { timeframe: options.exitTimeframe };
28370
28510
  if (options.soulFile) {
28371
28511
  const path3 = resolve(options.soulFile);
28372
28512
  if (!existsSync2(path3)) {
@@ -28404,7 +28544,8 @@ function registerAgentCommand(program2) {
28404
28544
  console.log(` ${source_default.gray("Trader:")} ${result.traderId}`);
28405
28545
  console.log(` ${source_default.gray("Strategy:")} ${result.strategyId}${result.autoStrategyCreated ? source_default.dim(" (auto-created)") : ""}`);
28406
28546
  console.log(` ${source_default.gray("Autonomy:")} ${formatAutonomy(result.autonomyLevel)}`);
28407
- console.log(` ${source_default.gray("Exit reasoner:")} ${result.exitReasonerEnabled ? source_default.green("enabled") : source_default.dim("disabled")}`);
28547
+ console.log(` ${source_default.gray("Exit reasoner:")} ${result.exitReasonerEnabled ? source_default.green("enabled") : source_default.dim("disabled")} (${result.exitReasonerMode ?? "off"})`);
28548
+ console.log(` ${source_default.gray("Exit preset:")} ${result.exitStyle ?? "balanced"} / ${result.exitTimingPolicy?.timeframe ?? "swing"}`);
28408
28549
  console.log(` ${source_default.gray("Interval:")} ${formatInterval2(result.scanIntervalMs)}`);
28409
28550
  console.log(` ${source_default.gray("Next scan:")} ${formatShortDate5(result.nextScanAt)}`);
28410
28551
  console.log("");
@@ -28474,7 +28615,8 @@ function registerAgentCommand(program2) {
28474
28615
  if (a.decideModel)
28475
28616
  console.log(` ${source_default.gray("Decide model:")} ${a.decideModel}`);
28476
28617
  console.log(` ${source_default.gray("Shared scan cache:")} ${a.disableSharedScanCache ? source_default.yellow("bypassed") : source_default.dim("enabled")}`);
28477
- console.log(` ${source_default.gray("Exit reasoner:")} ${a.exitReasonerEnabled ? source_default.green("enabled") : source_default.dim("disabled")}`);
28618
+ console.log(` ${source_default.gray("Exit reasoner:")} ${a.exitReasonerEnabled ? source_default.green("enabled") : source_default.dim("disabled")} (${a.exitReasonerMode ?? "off"})`);
28619
+ console.log(` ${source_default.gray("Exit preset:")} ${a.exitStyle ?? "balanced"} / ${a.exitTimingPolicy?.timeframe ?? "swing"}`);
28478
28620
  console.log(` ${source_default.gray("Scan interval:")} ${formatInterval2(a.scanIntervalMs)}`);
28479
28621
  console.log(` ${source_default.gray("Max daily trades:")} ${a.maxDailyTrades}`);
28480
28622
  console.log(` ${source_default.gray("Max daily loss:")} $${a.maxDailyLossUsd}`);
@@ -28598,7 +28740,7 @@ function registerAgentCommand(program2) {
28598
28740
  handleApiError(error2, "Position exit failed", logger30);
28599
28741
  }
28600
28742
  });
28601
- agent.command("update <agentId>").description("Update agent config").option("--name <name>", "Agent name").option("--autonomy <level>", AUTONOMY_HELP_UPDATE).option("--scan-interval <ms>", "Scan interval in raw milliseconds (e.g. 300000 = 5m)").option("--scan-interval-human <duration>", "Scan interval in human-readable format (e.g. 1m, 5m, 15m, 30m, 1h). Overrides --scan-interval").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_HELP).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").option("--disable-shared-scan-cache", SHARED_SCAN_CACHE_INTERNAL_HELP).option("--enable-shared-scan-cache", ENABLE_SHARED_SCAN_CACHE_HELP).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").option("--exit-reasoner", EXIT_REASONER_HELP).option("--disable-exit-reasoner", DISABLE_EXIT_REASONER_HELP).addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (agentId, options) => {
28743
+ agent.command("update <agentId>").description("Update agent config").option("--name <name>", "Agent name").option("--autonomy <level>", AUTONOMY_HELP_UPDATE).option("--scan-interval <ms>", "Scan interval in raw milliseconds (e.g. 300000 = 5m)").option("--scan-interval-human <duration>", "Scan interval in human-readable format (e.g. 1m, 5m, 15m, 30m, 1h). Overrides --scan-interval").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_HELP).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").option("--disable-shared-scan-cache", SHARED_SCAN_CACHE_INTERNAL_HELP).option("--enable-shared-scan-cache", ENABLE_SHARED_SCAN_CACHE_HELP).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").option("--exit-reasoner", EXIT_REASONER_HELP).option("--disable-exit-reasoner", DISABLE_EXIT_REASONER_HELP).addOption(new Option("--exit-reasoner-mode <mode>", EXIT_REASONER_MODE_HELP).choices([...EXIT_REASONER_MODES])).addOption(new Option("--urgent-event-exit-mode <mode>", URGENT_EVENT_EXIT_MODE_HELP).choices([...EXIT_REASONER_MODES])).addOption(new Option("--exit-style <style>", "Exit management preset").choices([...EXIT_STYLES])).addOption(new Option("--exit-timeframe <timeframe>", "Strategy exit timing preset").choices([...EXIT_TIMEFRAMES])).addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (agentId, options) => {
28602
28744
  if (!await ensureRemote())
28603
28745
  return;
28604
28746
  const body = {};
@@ -28656,6 +28798,14 @@ function registerAgentCommand(program2) {
28656
28798
  body.exitReasonerEnabled = true;
28657
28799
  if (options.disableExitReasoner)
28658
28800
  body.exitReasonerEnabled = false;
28801
+ if (options.exitReasonerMode)
28802
+ body.exitReasonerMode = options.exitReasonerMode;
28803
+ if (options.urgentEventExitMode)
28804
+ body.urgentEventExitReviewMode = options.urgentEventExitMode;
28805
+ if (options.exitStyle)
28806
+ body.exitStyle = options.exitStyle;
28807
+ if (options.exitTimeframe)
28808
+ body.exitTimingPolicy = { timeframe: options.exitTimeframe };
28659
28809
  if (options.assetClass)
28660
28810
  body.assetClass = options.assetClass;
28661
28811
  if (options.soulFile) {
@@ -28701,6 +28851,417 @@ function registerAgentCommand(program2) {
28701
28851
  });
28702
28852
  }
28703
28853
 
28854
+ // dist/commands/prediction-market-paper.js
28855
+ init_source();
28856
+ init_api_client();
28857
+ init_utils();
28858
+ var PREDICTION_MARKET_PAPER_RESULTS_CLI_FLAG = "PREDICTION_MARKET_PAPER_RESULTS_CLI_ENABLED";
28859
+ var DEFAULT_PAPER_POSITION_LIMIT = 50;
28860
+ var MAX_PAPER_POSITION_LIMIT = 100;
28861
+ var MAX_PAPER_POSITION_OFFSET = 1e4;
28862
+ var SAFE_IDENTIFIER_PATTERN = /^[A-Za-z0-9_.:@-]+$/;
28863
+ var PAPER_POSITION_STATUSES = [
28864
+ "OPEN",
28865
+ "CLOSING",
28866
+ "CLOSED",
28867
+ "EXPIRED",
28868
+ "RESOLVED_WON",
28869
+ "RESOLVED_LOST",
28870
+ "INVALID",
28871
+ "CANCELED",
28872
+ "SETTLED"
28873
+ ];
28874
+ function isPredictionMarketPaperCliEnabled(env2 = process.env) {
28875
+ return env2[PREDICTION_MARKET_PAPER_RESULTS_CLI_FLAG] === "true";
28876
+ }
28877
+ function printDisabled(format2) {
28878
+ const payload = {
28879
+ disabled: true,
28880
+ featureFlag: PREDICTION_MARKET_PAPER_RESULTS_CLI_FLAG,
28881
+ mode: "paper",
28882
+ label: "Paper / Simulated",
28883
+ noVenueOrderPlaced: true,
28884
+ message: "Prediction-market paper result inspection is disabled."
28885
+ };
28886
+ if (format2 === "json") {
28887
+ console.log(JSON.stringify(payload, null, 2));
28888
+ return;
28889
+ }
28890
+ console.log(source_default.yellow("Prediction-market paper result inspection is disabled."));
28891
+ console.log(source_default.dim("Paper / Simulated. No venue order placed. Read-only inspection is unavailable in this CLI environment."));
28892
+ }
28893
+ function validateIdentifier(label, value) {
28894
+ if (!value)
28895
+ return null;
28896
+ if (value.length > 300)
28897
+ return `${label} must be 300 characters or fewer.`;
28898
+ if (!SAFE_IDENTIFIER_PATTERN.test(value)) {
28899
+ return `${label} may contain only letters, numbers, underscore, dot, colon, at, or dash.`;
28900
+ }
28901
+ return null;
28902
+ }
28903
+ function parseTimestamp(label, value) {
28904
+ if (!value)
28905
+ return { value: null, error: null };
28906
+ const parsed = new Date(value);
28907
+ if (Number.isNaN(parsed.getTime())) {
28908
+ return { value: null, error: `${label} must be a valid timestamp.` };
28909
+ }
28910
+ return { value: parsed.toISOString(), error: null };
28911
+ }
28912
+ function parseIntegerOption(label, value, fallback, min, max) {
28913
+ if (value == null)
28914
+ return { value: fallback, error: null };
28915
+ const parsed = Number(value);
28916
+ if (!Number.isInteger(parsed)) {
28917
+ return { value: fallback, error: `${label} must be an integer.` };
28918
+ }
28919
+ if (parsed < min || parsed > max) {
28920
+ return { value: fallback, error: `${label} must be between ${min} and ${max}.` };
28921
+ }
28922
+ return { value: parsed, error: null };
28923
+ }
28924
+ function validateWindow(from, to) {
28925
+ if (!from || !to)
28926
+ return null;
28927
+ return new Date(from) < new Date(to) ? null : "--from must be before --to.";
28928
+ }
28929
+ function printValidationError(message) {
28930
+ console.error(source_default.red(`Error: ${message}`));
28931
+ process.exitCode = 1;
28932
+ }
28933
+ function buildQuery(options, includePagination) {
28934
+ for (const [label, value] of [
28935
+ ["--agent", options.agent],
28936
+ ["--venue", options.venue],
28937
+ ["--market-id", options.marketId],
28938
+ ["--instrument-key", options.instrumentKey]
28939
+ ]) {
28940
+ const error2 = validateIdentifier(label, value);
28941
+ if (error2) {
28942
+ printValidationError(error2);
28943
+ return null;
28944
+ }
28945
+ }
28946
+ const from = parseTimestamp("--from", options.from);
28947
+ if (from.error) {
28948
+ printValidationError(from.error);
28949
+ return null;
28950
+ }
28951
+ const to = parseTimestamp("--to", options.to);
28952
+ if (to.error) {
28953
+ printValidationError(to.error);
28954
+ return null;
28955
+ }
28956
+ const windowError = validateWindow(from.value, to.value);
28957
+ if (windowError) {
28958
+ printValidationError(windowError);
28959
+ return null;
28960
+ }
28961
+ const params = new URLSearchParams();
28962
+ if (options.agent)
28963
+ params.set("agentId", options.agent);
28964
+ if ("status" in options && options.status)
28965
+ params.set("status", options.status);
28966
+ if (options.venue)
28967
+ params.set("venue", options.venue);
28968
+ if (options.marketId)
28969
+ params.set("marketId", options.marketId);
28970
+ if (options.instrumentKey)
28971
+ params.set("instrumentKey", options.instrumentKey);
28972
+ if (from.value)
28973
+ params.set("from", from.value);
28974
+ if (to.value)
28975
+ params.set("to", to.value);
28976
+ if (includePagination) {
28977
+ const queryOptions = options;
28978
+ const limit = parseIntegerOption("--limit", queryOptions.limit, DEFAULT_PAPER_POSITION_LIMIT, 1, MAX_PAPER_POSITION_LIMIT);
28979
+ if (limit.error) {
28980
+ printValidationError(limit.error);
28981
+ return null;
28982
+ }
28983
+ const offset = parseIntegerOption("--offset", queryOptions.offset, 0, 0, MAX_PAPER_POSITION_OFFSET);
28984
+ if (offset.error) {
28985
+ printValidationError(offset.error);
28986
+ return null;
28987
+ }
28988
+ params.set("limit", String(limit.value));
28989
+ params.set("offset", String(offset.value));
28990
+ }
28991
+ return params;
28992
+ }
28993
+ function withQuery(path3, params) {
28994
+ const query = params.toString();
28995
+ return query ? `${path3}?${query}` : path3;
28996
+ }
28997
+ function formatUsd6(value) {
28998
+ if (value == null || !Number.isFinite(value))
28999
+ return source_default.dim("-");
29000
+ return `$${value.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
29001
+ }
29002
+ function formatSignedUsd(value) {
29003
+ if (value == null || !Number.isFinite(value))
29004
+ return source_default.dim("-");
29005
+ const formatted = formatUsd6(Math.abs(value));
29006
+ if (value > 0)
29007
+ return source_default.green(`+${formatted}`);
29008
+ if (value < 0)
29009
+ return source_default.red(`-${formatted}`);
29010
+ return formatted;
29011
+ }
29012
+ function formatProbability(value) {
29013
+ if (value == null || !Number.isFinite(value))
29014
+ return source_default.dim("-");
29015
+ return `${(value * 100).toFixed(1)}%`;
29016
+ }
29017
+ function formatQuantity(value) {
29018
+ if (value == null || !Number.isFinite(value))
29019
+ return source_default.dim("-");
29020
+ return value.toLocaleString("en-US", { maximumFractionDigits: 4 });
29021
+ }
29022
+ function formatTimestamp(value) {
29023
+ if (!value)
29024
+ return source_default.dim("-");
29025
+ try {
29026
+ return new Date(value).toISOString().slice(0, 19).replace("T", " ");
29027
+ } catch {
29028
+ return value;
29029
+ }
29030
+ }
29031
+ function latestLifecycleTime(position) {
29032
+ return position.closedAt ?? position.settledAt ?? position.resolvedAt ?? position.lastMonitoredAt ?? position.latestSnapshotAt ?? null;
29033
+ }
29034
+ function formatPaperSummary(summary) {
29035
+ const lines = [];
29036
+ lines.push("");
29037
+ lines.push(source_default.bold.cyan(" Prediction Market Paper Results"));
29038
+ lines.push(source_default.gray(" Paper / Simulated"));
29039
+ lines.push(source_default.gray(" No venue order placed"));
29040
+ lines.push(source_default.gray(" Read-only inspection"));
29041
+ lines.push(source_default.gray(" " + "-".repeat(52)));
29042
+ lines.push(` ${source_default.gray("Open positions:")} ${summary.openPositions}`);
29043
+ lines.push(` ${source_default.gray("Closed positions:")} ${summary.closedPositions}`);
29044
+ lines.push(` ${source_default.gray("Resolved won:")} ${summary.resolvedWon}`);
29045
+ lines.push(` ${source_default.gray("Resolved lost:")} ${summary.resolvedLost}`);
29046
+ lines.push(` ${source_default.gray("Invalid / canceled:")} ${summary.invalidOrCanceled}`);
29047
+ lines.push(` ${source_default.gray("Total notional:")} ${formatUsd6(summary.totalNotionalUsd)}`);
29048
+ lines.push(` ${source_default.gray("Realized PnL:")} ${formatSignedUsd(summary.realizedPnlUsd)}`);
29049
+ lines.push(` ${source_default.gray("Unrealized PnL:")} ${formatSignedUsd(summary.unrealizedPnlUsd)}`);
29050
+ lines.push(` ${source_default.gray("Last updated:")} ${formatTimestamp(summary.lastUpdatedAt)}`);
29051
+ lines.push("");
29052
+ return lines.join("\n");
29053
+ }
29054
+ function formatPaperPositions(positions) {
29055
+ const lines = [];
29056
+ lines.push("");
29057
+ lines.push(source_default.bold.cyan(" Prediction Market Paper Positions"));
29058
+ lines.push(source_default.gray(" Paper / Simulated"));
29059
+ lines.push(source_default.gray(" No venue order placed"));
29060
+ lines.push(source_default.gray(" Read-only inspection"));
29061
+ lines.push(source_default.gray(" " + "-".repeat(132)));
29062
+ if (positions.length === 0) {
29063
+ lines.push(` ${source_default.dim("No paper prediction-market positions found.")}`);
29064
+ lines.push(` ${source_default.dim("Paper PM results appear only after paper runtime and paper writes are enabled.")}`);
29065
+ lines.push("");
29066
+ return lines.join("\n");
29067
+ }
29068
+ lines.push(` ${source_default.gray("Position".padEnd(14))}${source_default.gray("Agent".padEnd(14))}${source_default.gray("Venue".padEnd(12))}${source_default.gray("Outcome".padEnd(10))}${source_default.gray("Side".padEnd(10))}${source_default.gray("Qty".padStart(10))}${source_default.gray("Entry".padStart(10))}${source_default.gray("Mark".padStart(10))}${source_default.gray("Notional".padStart(12))}${source_default.gray("Realized".padStart(12))}${source_default.gray("Unrealized".padStart(12))}${source_default.gray("Status".padStart(14))}`);
29069
+ lines.push(source_default.gray(" " + "-".repeat(132)));
29070
+ for (const position of positions) {
29071
+ lines.push(` ${truncate(position.positionId, 13).padEnd(14)}${truncate(position.agentId ?? "-", 13).padEnd(14)}${truncate(position.venue ?? "-", 11).padEnd(12)}${truncate(position.outcomeSide ?? "-", 9).padEnd(10)}${truncate(position.positionSide, 9).padEnd(10)}${formatQuantity(position.quantity).padStart(10)}${formatProbability(position.averageEntryPrice).padStart(10)}${formatProbability(position.markPrice).padStart(10)}${formatUsd6(position.notionalUsd).padStart(12)}${formatSignedUsd(position.realizedPnlUsd).padStart(12)}${formatSignedUsd(position.unrealizedPnlUsd).padStart(12)}${truncate(position.status, 13).padStart(14)}`);
29072
+ lines.push(` ${source_default.dim("Market:")} ${truncate(position.marketQuestion ?? position.marketId ?? position.instrumentKey, 92)} ${source_default.dim("Opened:")} ${formatTimestamp(position.openedAt)} ${source_default.dim("Latest:")} ${formatTimestamp(latestLifecycleTime(position))} ` + source_default.dim("paper / no venue order placed"));
29073
+ }
29074
+ lines.push("");
29075
+ return lines.join("\n");
29076
+ }
29077
+ function formatPaperPositionDetail(position) {
29078
+ const lines = [];
29079
+ lines.push("");
29080
+ lines.push(source_default.bold.cyan(` Prediction Market Paper Position - ${position.positionId}`));
29081
+ lines.push(source_default.gray(" Paper / Simulated"));
29082
+ lines.push(source_default.gray(" No venue order placed"));
29083
+ lines.push(source_default.gray(" Read-only inspection"));
29084
+ lines.push(source_default.gray(" " + "-".repeat(68)));
29085
+ lines.push(` ${source_default.gray("Agent:")} ${position.agentId ?? "-"}`);
29086
+ lines.push(` ${source_default.gray("Trader:")} ${position.traderId ?? "-"}`);
29087
+ lines.push(` ${source_default.gray("Instrument key:")} ${position.instrumentKey}`);
29088
+ lines.push(` ${source_default.gray("Instrument type:")} ${position.instrumentType}`);
29089
+ lines.push(` ${source_default.gray("Venue:")} ${position.venue ?? "-"}`);
29090
+ lines.push(` ${source_default.gray("Market ID:")} ${position.marketId ?? "-"}`);
29091
+ lines.push(` ${source_default.gray("Market question:")} ${position.marketQuestion ?? "-"}`);
29092
+ lines.push(` ${source_default.gray("Outcome side:")} ${position.outcomeSide ?? "-"}`);
29093
+ lines.push(` ${source_default.gray("Position side:")} ${position.positionSide}`);
29094
+ lines.push(` ${source_default.gray("Quantity:")} ${formatQuantity(position.quantity)}`);
29095
+ lines.push(` ${source_default.gray("Average entry:")} ${formatProbability(position.averageEntryPrice)}`);
29096
+ lines.push(` ${source_default.gray("Mark:")} ${formatProbability(position.markPrice)}`);
29097
+ lines.push(` ${source_default.gray("Notional USD:")} ${formatUsd6(position.notionalUsd)}`);
29098
+ lines.push(` ${source_default.gray("Realized PnL:")} ${formatSignedUsd(position.realizedPnlUsd)}`);
29099
+ lines.push(` ${source_default.gray("Unrealized PnL:")} ${formatSignedUsd(position.unrealizedPnlUsd)}`);
29100
+ lines.push(` ${source_default.gray("Status:")} ${position.status}`);
29101
+ lines.push(` ${source_default.gray("Opened:")} ${formatTimestamp(position.openedAt)}`);
29102
+ lines.push(` ${source_default.gray("Closed:")} ${formatTimestamp(position.closedAt)}`);
29103
+ lines.push(` ${source_default.gray("Expiry:")} ${formatTimestamp(position.expiryAt)}`);
29104
+ lines.push(` ${source_default.gray("Resolved:")} ${formatTimestamp(position.resolvedAt)}`);
29105
+ lines.push(` ${source_default.gray("Settled:")} ${formatTimestamp(position.settledAt)}`);
29106
+ lines.push(` ${source_default.gray("Settlement ID:")} ${position.settlementId ?? "-"}`);
29107
+ lines.push(` ${source_default.gray("Execution mode:")} ${position.executionMode}`);
29108
+ lines.push(` ${source_default.gray("Source:")} ${position.source}`);
29109
+ lines.push(` ${source_default.gray("No venue order:")} ${String(position.noVenueOrderPlaced)}`);
29110
+ lines.push("");
29111
+ return lines.join("\n");
29112
+ }
29113
+ async function readSummary(options) {
29114
+ const query = buildQuery(options, false);
29115
+ if (!query)
29116
+ return null;
29117
+ const path3 = withQuery("/api/v1/prediction-market-paper/summary", query);
29118
+ return apiRequest(path3);
29119
+ }
29120
+ async function readPositions(options) {
29121
+ const query = buildQuery(options, true);
29122
+ if (!query)
29123
+ return null;
29124
+ const path3 = withQuery("/api/v1/prediction-market-paper/positions", query);
29125
+ return apiRequest(path3);
29126
+ }
29127
+ async function readAgentSummary(agentId, options) {
29128
+ const idError = validateIdentifier("agentId", agentId);
29129
+ if (idError) {
29130
+ printValidationError(idError);
29131
+ return null;
29132
+ }
29133
+ const query = buildQuery({ ...options, agent: void 0 }, false);
29134
+ if (!query)
29135
+ return null;
29136
+ const path3 = withQuery(`/api/v1/agents/${encodeURIComponent(agentId)}/prediction-market-paper/summary`, query);
29137
+ return apiRequest(path3);
29138
+ }
29139
+ async function readAgentPositions(agentId, options) {
29140
+ const idError = validateIdentifier("agentId", agentId);
29141
+ if (idError) {
29142
+ printValidationError(idError);
29143
+ return null;
29144
+ }
29145
+ const query = buildQuery({ ...options, agent: void 0 }, true);
29146
+ if (!query)
29147
+ return null;
29148
+ const path3 = withQuery(`/api/v1/agents/${encodeURIComponent(agentId)}/prediction-market-paper/positions`, query);
29149
+ return apiRequest(path3);
29150
+ }
29151
+ async function readPosition(positionId) {
29152
+ const idError = validateIdentifier("positionId", positionId);
29153
+ if (idError) {
29154
+ printValidationError(idError);
29155
+ return null;
29156
+ }
29157
+ return apiRequest(`/api/v1/prediction-market-paper/positions/${encodeURIComponent(positionId)}`);
29158
+ }
29159
+ function isBackendDisabled(error2) {
29160
+ if (!(error2 instanceof ApiError))
29161
+ return false;
29162
+ const body = error2.body;
29163
+ return error2.status === 404 && (error2.message.toLowerCase().includes("paper result reads are disabled") || typeof body === "object" && body !== null && body.code === "PREDICTION_MARKET_PAPER_RESULTS_READ_DISABLED");
29164
+ }
29165
+ function handlePaperError(error2, format2) {
29166
+ if (isBackendDisabled(error2)) {
29167
+ const payload = {
29168
+ disabled: true,
29169
+ mode: "paper",
29170
+ label: "Paper / Simulated",
29171
+ noVenueOrderPlaced: true,
29172
+ message: "Prediction-market paper result reads are disabled by the API."
29173
+ };
29174
+ if (format2 === "json") {
29175
+ console.log(JSON.stringify(payload, null, 2));
29176
+ } else {
29177
+ console.log(source_default.yellow("Prediction-market paper result reads are disabled by the API."));
29178
+ console.log(source_default.dim("Paper / Simulated. No venue order placed. Read-only inspection only."));
29179
+ }
29180
+ return;
29181
+ }
29182
+ const message = error2 instanceof Error ? error2.message : String(error2);
29183
+ console.error(source_default.red(`Error: ${message}`));
29184
+ process.exitCode = error2 instanceof ApiError ? 2 : 1;
29185
+ }
29186
+ async function runReadAction(format2, action) {
29187
+ if (!isPredictionMarketPaperCliEnabled()) {
29188
+ printDisabled(format2);
29189
+ return;
29190
+ }
29191
+ if (!await ensureRemote())
29192
+ return;
29193
+ try {
29194
+ await action();
29195
+ } catch (error2) {
29196
+ handlePaperError(error2, format2);
29197
+ }
29198
+ }
29199
+ function registerPredictionMarketPaperCommand(program2) {
29200
+ const command = program2.command("prediction-market-paper").alias("pm-paper").description("Inspect paper prediction-market positions and results");
29201
+ const addSummaryFilters = (cmd) => cmd.option("--agent <agentId>", "Optional agent ID filter").option("--venue <venue>", "Venue filter").option("--market-id <marketId>", "Prediction-market ID filter").option("--instrument-key <instrumentKey>", "Instrument key filter").option("--from <timestamp>", "Opened-at window start").option("--to <timestamp>", "Opened-at window end").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text"));
29202
+ const addPositionFilters = (cmd) => addSummaryFilters(cmd).addOption(new Option("--status <status>", "Position status filter").choices([...PAPER_POSITION_STATUSES])).option("--limit <n>", `Maximum rows, 1-${MAX_PAPER_POSITION_LIMIT}`, String(DEFAULT_PAPER_POSITION_LIMIT)).option("--offset <n>", `Rows to skip, 0-${MAX_PAPER_POSITION_OFFSET}`, "0");
29203
+ addSummaryFilters(command.command("summary").description("Show read-only paper prediction-market result summary")).action(async (options) => {
29204
+ await runReadAction(options.format, async () => {
29205
+ const data = await readSummary(options);
29206
+ if (!data)
29207
+ return;
29208
+ if (options.format === "json") {
29209
+ console.log(JSON.stringify(data, null, 2));
29210
+ } else {
29211
+ console.log(formatPaperSummary(data.summary));
29212
+ }
29213
+ });
29214
+ });
29215
+ addPositionFilters(command.command("positions").description("List read-only paper prediction-market positions")).action(async (options) => {
29216
+ await runReadAction(options.format, async () => {
29217
+ const data = await readPositions(options);
29218
+ if (!data)
29219
+ return;
29220
+ if (options.format === "json") {
29221
+ console.log(JSON.stringify(data, null, 2));
29222
+ } else {
29223
+ console.log(formatPaperPositions(data.positions));
29224
+ }
29225
+ });
29226
+ });
29227
+ command.command("position").description("Show one read-only paper prediction-market position").argument("<positionId>", "Paper prediction-market position ID").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (positionId, options) => {
29228
+ await runReadAction(options.format, async () => {
29229
+ const data = await readPosition(positionId);
29230
+ if (!data)
29231
+ return;
29232
+ if (options.format === "json") {
29233
+ console.log(JSON.stringify(data, null, 2));
29234
+ } else {
29235
+ console.log(formatPaperPositionDetail(data.position));
29236
+ }
29237
+ });
29238
+ });
29239
+ addSummaryFilters(command.command("agent-summary").description("Show read-only paper prediction-market result summary for one agent").argument("<agentId>", "Agent ID")).action(async (agentId, options) => {
29240
+ await runReadAction(options.format, async () => {
29241
+ const data = await readAgentSummary(agentId, options);
29242
+ if (!data)
29243
+ return;
29244
+ if (options.format === "json") {
29245
+ console.log(JSON.stringify(data, null, 2));
29246
+ } else {
29247
+ console.log(formatPaperSummary(data.summary));
29248
+ }
29249
+ });
29250
+ });
29251
+ addPositionFilters(command.command("agent-positions").description("List read-only paper prediction-market positions for one agent").argument("<agentId>", "Agent ID")).action(async (agentId, options) => {
29252
+ await runReadAction(options.format, async () => {
29253
+ const data = await readAgentPositions(agentId, options);
29254
+ if (!data)
29255
+ return;
29256
+ if (options.format === "json") {
29257
+ console.log(JSON.stringify(data, null, 2));
29258
+ } else {
29259
+ console.log(formatPaperPositions(data.positions));
29260
+ }
29261
+ });
29262
+ });
29263
+ }
29264
+
28704
29265
  // dist/cli.js
28705
29266
  import { readFileSync as readFileSync5 } from "node:fs";
28706
29267
  import { fileURLToPath as fileURLToPath2 } from "node:url";
@@ -28752,6 +29313,7 @@ function createCli() {
28752
29313
  registerSuggestionsCommand(program2);
28753
29314
  registerCronCommand(program2);
28754
29315
  registerAgentCommand(program2);
29316
+ registerPredictionMarketPaperCommand(program2);
28755
29317
  registerConfigCommand(program2);
28756
29318
  const GROUPS = {
28757
29319
  "Core": ["context", "query", "watch"],
@@ -28760,7 +29322,7 @@ function createCli() {
28760
29322
  "Edge & Safety": ["edge", "edge-guard", "coaching", "thesis"],
28761
29323
  "Strategy & Benchmarking": ["strategy", "replay", "benchmark", "suggestions"],
28762
29324
  "Scheduling": ["cron"],
28763
- "Agents": ["agent"],
29325
+ "Agents": ["agent", "prediction-market-paper"],
28764
29326
  "Account": ["login", "logout", "whoami", "billing", "subscribe", "rotate-key", "forgot-key", "connect-chatgpt", "config"]
28765
29327
  };
28766
29328
  program2.addHelpText("before", "");
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { formatConnectionError } from './utils.js';
2
2
  export { storeCredentials, loadCredentials, clearCredentials, redactApiKey, getCredentialsFilePath } from './credentials.js';
3
3
  export type { StoredCredentials } from './credentials.js';
4
- export { apiRequest, resolveApiKey, getApiBase, ApiError } from './api-client.js';
4
+ export { apiRequest, resolveApiKey, resolveApiKeyWithSource, getApiBase, ApiError, ApiCredentialConflictError, ApiCredentialMissingError, } from './api-client.js';
5
+ export type { ApiKeySourceName, ResolvedApiKey } from './api-client.js';
5
6
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export { formatConnectionError } from './utils.js';
2
2
  export { storeCredentials, loadCredentials, clearCredentials, redactApiKey, getCredentialsFilePath } from './credentials.js';
3
- export { apiRequest, resolveApiKey, getApiBase, ApiError } from './api-client.js';
3
+ export { apiRequest, resolveApiKey, resolveApiKeyWithSource, getApiBase, ApiError, ApiCredentialConflictError, ApiCredentialMissingError, } from './api-client.js';
4
4
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@trading-boy/cli",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "Trading Boy CLI — remote trading intelligence for traders and AI agents.",
5
- "homepage": "https://cabal.ventures",
5
+ "homepage": "https://tradingboy.ai",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "https://github.com/0xLLM73/Trading-Boy"
8
+ "url": "git+https://github.com/0xLLM73/Trading-Boy.git"
9
9
  },
10
10
  "license": "MIT",
11
11
  "keywords": [
@@ -69,7 +69,7 @@
69
69
  },
70
70
  "devDependencies": {
71
71
  "@trading-boy/core": "workspace:*",
72
- "esbuild": "~0.27.7",
72
+ "esbuild": "~0.28.1",
73
73
  "typescript": "^5.7.0"
74
74
  }
75
75
  }