@trading-boy/cli 2.0.1 → 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.
@@ -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) {
@@ -22472,6 +22559,7 @@ init_utils();
22472
22559
  init_api_client();
22473
22560
  init_esm15();
22474
22561
  var logger9 = createLogger("cli-trader");
22562
+ var ALIAS_PATTERN = /^[a-zA-Z0-9_-]{3,30}$/;
22475
22563
  function formatTraderOutput(trader, wallets) {
22476
22564
  const lines = [];
22477
22565
  lines.push("");
@@ -22495,6 +22583,10 @@ function formatTraderOutput(trader, wallets) {
22495
22583
  lines.push(` ${source_default.dim("\u2022")} ${source_default.white(w.address)} ${source_default.dim(`(${w.chain})`)} ${source_default.dim(`since ${w.since}`)}`);
22496
22584
  }
22497
22585
  }
22586
+ lines.push(` ${source_default.gray("Public Board:")} ${trader.benchmarkOptIn ? source_default.green("visible") : source_default.dim("private")}`);
22587
+ if (trader.publicAlias) {
22588
+ lines.push(` ${source_default.gray("Public Alias:")} ${source_default.white(trader.publicAlias)}`);
22589
+ }
22498
22590
  lines.push("");
22499
22591
  lines.push(` ${source_default.gray("Created:")} ${source_default.dim(trader.createdAt)}`);
22500
22592
  lines.push(` ${source_default.gray("Updated:")} ${source_default.dim(trader.updatedAt)}`);
@@ -22526,14 +22618,26 @@ function formatTraderListOutput(traders) {
22526
22618
  }
22527
22619
  function registerTraderCommand(program2) {
22528
22620
  const trader = program2.command("trader").description("Manage trader profiles");
22529
- trader.command("register").description("Register a new trader profile").requiredOption("--name <name>", "Trader display name").option("--wallet <address>", "Initial Solana wallet address").option("--max-drawdown <pct>", "Max acceptable drawdown percentage", parseFloatOption).addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (options) => {
22621
+ trader.command("register").description("Register a new trader profile").requiredOption("--name <name>", "Trader display name").option("--wallet <address>", "Initial Solana wallet address").option("--max-drawdown <pct>", "Max acceptable drawdown percentage", parseFloatOption).option("--public-leaderboard", "Opt this trader into the public leaderboard").option("--alias <alias>", "Public leaderboard alias for --public-leaderboard").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (options) => {
22622
+ if (options.publicLeaderboard && !options.alias) {
22623
+ console.error(source_default.red("Error: --alias is required with --public-leaderboard."));
22624
+ process.exitCode = 1;
22625
+ return;
22626
+ }
22627
+ if (options.alias && !ALIAS_PATTERN.test(options.alias)) {
22628
+ console.error(source_default.red("Error: Invalid alias. Must be 3-30 characters, alphanumeric with underscores and hyphens."));
22629
+ process.exitCode = 1;
22630
+ return;
22631
+ }
22530
22632
  try {
22531
22633
  const result = await apiRequest("/api/v1/traders", {
22532
22634
  method: "POST",
22533
22635
  body: {
22534
22636
  name: options.name,
22535
22637
  wallet: options.wallet,
22536
- maxDrawdown: options.maxDrawdown
22638
+ maxDrawdown: options.maxDrawdown,
22639
+ benchmarkOptIn: options.publicLeaderboard === true,
22640
+ publicAlias: options.alias
22537
22641
  }
22538
22642
  });
22539
22643
  const traderId = result.name ?? result.id;
@@ -22549,8 +22653,11 @@ function registerTraderCommand(program2) {
22549
22653
  } else {
22550
22654
  console.log(formatTraderOutput(result));
22551
22655
  console.log(source_default.green(" Trader registered successfully."));
22552
- if (result.alias) {
22553
- console.log(` ${source_default.gray("Leaderboard alias:")} ${source_default.white(result.alias)} ${source_default.dim("(change with: trading-boy trader set-alias <name> <alias>)")}`);
22656
+ if (result.publicAlias) {
22657
+ console.log(` ${source_default.gray("Leaderboard alias:")} ${source_default.white(result.publicAlias)} ${source_default.dim("(change with: trading-boy trader set-alias <name> <alias>)")}`);
22658
+ } else {
22659
+ console.log(` ${source_default.gray("Public leaderboard:")} ${source_default.dim("private by default")}`);
22660
+ console.log(` ${source_default.dim("Opt in later:")} ${source_default.white("trading-boy trader leaderboard opt-in <name> <alias>")}`);
22554
22661
  }
22555
22662
  console.log("");
22556
22663
  console.log(source_default.bold(" Next: Set up your trading identity"));
@@ -22636,7 +22743,46 @@ function registerTraderCommand(program2) {
22636
22743
  process.exitCode = error2 instanceof ApiError ? 2 : 1;
22637
22744
  }
22638
22745
  });
22639
- const ALIAS_PATTERN = /^[a-zA-Z0-9_-]{3,30}$/;
22746
+ const leaderboard = trader.command("leaderboard").description("Manage public leaderboard visibility");
22747
+ leaderboard.command("opt-in").description("Show a trader on the public leaderboard").argument("<name-or-id>", "Trader name or ID").argument("<alias>", "Public alias (3-30 chars, alphanumeric/underscores/hyphens)").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (nameOrId, alias, options) => {
22748
+ const jsonMode = options.format === "json";
22749
+ if (!ALIAS_PATTERN.test(alias)) {
22750
+ const msg = "Invalid alias. Must be 3-30 characters, alphanumeric with underscores and hyphens.";
22751
+ if (jsonMode)
22752
+ console.error(JSON.stringify({ error: msg }));
22753
+ else
22754
+ console.error(source_default.red(` ${msg}`));
22755
+ process.exitCode = 1;
22756
+ return;
22757
+ }
22758
+ try {
22759
+ const result = await apiRequest(`/api/v1/traders/${encodeURIComponent(nameOrId)}/public-profile`, { method: "PATCH", body: { benchmarkOptIn: true, publicAlias: alias } });
22760
+ if (jsonMode) {
22761
+ console.log(JSON.stringify(result));
22762
+ } else {
22763
+ console.log("");
22764
+ console.log(source_default.green(` Public leaderboard enabled as: ${source_default.white(result.publicAlias)}`));
22765
+ console.log("");
22766
+ }
22767
+ } catch (error2) {
22768
+ handlePublicProfileError(error2, jsonMode, "enable public leaderboard");
22769
+ }
22770
+ });
22771
+ leaderboard.command("opt-out").description("Hide a trader from the public leaderboard").argument("<name-or-id>", "Trader name or ID").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (nameOrId, options) => {
22772
+ const jsonMode = options.format === "json";
22773
+ try {
22774
+ const result = await apiRequest(`/api/v1/traders/${encodeURIComponent(nameOrId)}/public-profile`, { method: "PATCH", body: { benchmarkOptIn: false } });
22775
+ if (jsonMode) {
22776
+ console.log(JSON.stringify(result));
22777
+ } else {
22778
+ console.log("");
22779
+ console.log(source_default.green(" Public leaderboard disabled. This trader is private."));
22780
+ console.log("");
22781
+ }
22782
+ } catch (error2) {
22783
+ handlePublicProfileError(error2, jsonMode, "disable public leaderboard");
22784
+ }
22785
+ });
22640
22786
  trader.command("set-alias").description("Set a public leaderboard alias").argument("<name-or-id>", "Trader name or ID").argument("<alias>", "Leaderboard alias (3-30 chars, alphanumeric/underscores/hyphens)").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (nameOrId, alias, options) => {
22641
22787
  const jsonMode = options.format === "json";
22642
22788
  if (!ALIAS_PATTERN.test(alias)) {
@@ -23210,6 +23356,19 @@ function handleAgentError(error2, jsonMode, agentId, action) {
23210
23356
  }
23211
23357
  process.exitCode = error2 instanceof ApiError ? 2 : 1;
23212
23358
  }
23359
+ function handlePublicProfileError(error2, jsonMode, action) {
23360
+ let message = error2 instanceof Error ? error2.message : String(error2);
23361
+ if (error2 instanceof ApiError && error2.status === 409) {
23362
+ message = "Alias already taken. Try a different name.";
23363
+ }
23364
+ logger9.error({ error: message }, `Failed to ${action}`);
23365
+ if (jsonMode) {
23366
+ console.error(JSON.stringify({ error: message }));
23367
+ } else {
23368
+ console.error(source_default.red(`Error: ${message}`));
23369
+ }
23370
+ process.exitCode = error2 instanceof ApiError ? 2 : 1;
23371
+ }
23213
23372
  function parseFloatOption(value) {
23214
23373
  return parseFloat(value);
23215
23374
  }
@@ -24408,6 +24567,9 @@ init_logger();
24408
24567
  init_api_client();
24409
24568
  init_utils();
24410
24569
  var logger14 = createLogger2("cli-public-config");
24570
+ function formatModelReasoning(model, reasoningEffort) {
24571
+ return reasoningEffort ? `${model} (${reasoningEffort})` : model;
24572
+ }
24411
24573
  function printLlmConfig(result) {
24412
24574
  console.log("");
24413
24575
  console.log(source_default.bold.cyan(" LLM Configuration"));
@@ -24416,21 +24578,21 @@ function printLlmConfig(result) {
24416
24578
  if (result.effectiveProvider && (result.effectiveProvider !== result.provider || result.effectiveModel !== result.model)) {
24417
24579
  console.log(` ${source_default.gray("Effective:")} ${result.effectiveProvider} / ${result.effectiveModel ?? result.model}`);
24418
24580
  }
24419
- console.log(` ${source_default.gray("Model:")} ${result.model}`);
24420
- if (result.scanProvider || result.scanModel) {
24421
- 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)}`);
24422
24584
  }
24423
- if (result.analyzeProvider || result.analyzeModel) {
24424
- 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)}`);
24425
24587
  }
24426
- if (result.decideProvider || result.decideModel) {
24427
- 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)}`);
24428
24590
  }
24429
- if (result.exitHeartbeatModel) {
24430
- 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)}`);
24431
24593
  }
24432
- if (result.exitEventModel) {
24433
- 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)}`);
24434
24596
  }
24435
24597
  if (result.baseUrl) {
24436
24598
  console.log(` ${source_default.gray("Base URL:")} ${result.baseUrl}`);
@@ -24494,7 +24656,44 @@ function registerConfigCommand(program2) {
24494
24656
  }
24495
24657
  printLlmConfig(llmConfig);
24496
24658
  });
24497
- 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) => {
24498
24697
  if (!await ensureRemote())
24499
24698
  return;
24500
24699
  try {
@@ -24543,12 +24742,14 @@ function registerConfigCommand(program2) {
24543
24742
  process.exitCode = error2 instanceof ApiError ? 2 : 1;
24544
24743
  }
24545
24744
  });
24546
- 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) => {
24547
24748
  if (!await ensureRemote())
24548
24749
  return;
24549
- if (!opts.model && !opts.scanModel && !opts.analyzeModel && !opts.decideModel && !opts.exitHeartbeatModel && !opts.exitEventModel) {
24550
- console.error(source_default.red(" Error: At least one model flag is required."));
24551
- 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"));
24552
24753
  process.exitCode = 1;
24553
24754
  return;
24554
24755
  }
@@ -24557,27 +24758,33 @@ function registerConfigCommand(program2) {
24557
24758
  method: "PATCH",
24558
24759
  body: {
24559
24760
  ...opts.model ? { model: opts.model } : {},
24761
+ ...opts.reasoningEffort ? { reasoningEffort: opts.reasoningEffort } : {},
24560
24762
  ...opts.scanModel ? { scanModel: opts.scanModel } : {},
24763
+ ...opts.scanReasoningEffort ? { scanReasoningEffort: opts.scanReasoningEffort } : {},
24561
24764
  ...opts.analyzeModel ? { analyzeModel: opts.analyzeModel } : {},
24765
+ ...opts.analyzeReasoningEffort ? { analyzeReasoningEffort: opts.analyzeReasoningEffort } : {},
24562
24766
  ...opts.decideModel ? { decideModel: opts.decideModel } : {},
24767
+ ...opts.decideReasoningEffort ? { decideReasoningEffort: opts.decideReasoningEffort } : {},
24563
24768
  ...opts.exitHeartbeatModel ? { exitHeartbeatModel: opts.exitHeartbeatModel } : {},
24564
- ...opts.exitEventModel ? { exitEventModel: opts.exitEventModel } : {}
24769
+ ...opts.exitHeartbeatReasoningEffort ? { exitHeartbeatReasoningEffort: opts.exitHeartbeatReasoningEffort } : {},
24770
+ ...opts.exitEventModel ? { exitEventModel: opts.exitEventModel } : {},
24771
+ ...opts.exitEventReasoningEffort ? { exitEventReasoningEffort: opts.exitEventReasoningEffort } : {}
24565
24772
  }
24566
24773
  });
24567
24774
  console.log("");
24568
- console.log(source_default.green(" Stage models updated"));
24775
+ console.log(source_default.green(" Stage LLM settings updated"));
24569
24776
  console.log(source_default.gray(" " + "\u2500".repeat(40)));
24570
- console.log(` ${source_default.gray("Default:")} ${result.model}`);
24571
- if (result.scanModel)
24572
- console.log(` ${source_default.gray("Scan:")} ${result.scanModel}`);
24573
- if (result.analyzeModel)
24574
- console.log(` ${source_default.gray("Analyze:")} ${result.analyzeModel}`);
24575
- if (result.decideModel)
24576
- console.log(` ${source_default.gray("Decide:")} ${result.decideModel}`);
24577
- if (result.exitHeartbeatModel)
24578
- console.log(` ${source_default.gray("Exit heartbeat:")} ${result.exitHeartbeatModel}`);
24579
- if (result.exitEventModel)
24580
- 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)}`);
24581
24788
  console.log("");
24582
24789
  console.log(source_default.dim(" Agents will use these models on their next tick."));
24583
24790
  console.log("");
@@ -24640,9 +24847,9 @@ init_source();
24640
24847
  init_credentials();
24641
24848
  init_api_client();
24642
24849
  var logger15 = createLogger("cli-login");
24643
- 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>";
24644
24851
  function validateApiKeyFormat(key) {
24645
- return API_KEY_PATTERN.test(key);
24852
+ return parseCustomerApiKey(key) !== null;
24646
24853
  }
24647
24854
  async function verifyApiKey(apiKey) {
24648
24855
  const apiBase = getApiBase();
@@ -24704,14 +24911,14 @@ function registerLoginCommand(program2) {
24704
24911
  if (!input)
24705
24912
  return "Trading Boy API key is required";
24706
24913
  if (!validateApiKeyFormat(input)) {
24707
- 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}`;
24708
24915
  }
24709
24916
  return true;
24710
24917
  }
24711
24918
  });
24712
24919
  }
24713
24920
  if (!validateApiKeyFormat(apiKey)) {
24714
- 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}`));
24715
24922
  process.exitCode = 1;
24716
24923
  return;
24717
24924
  }
@@ -24843,34 +25050,31 @@ init_credentials();
24843
25050
  init_api_client();
24844
25051
  var logger17 = createLogger("cli-whoami");
24845
25052
  async function executeWhoami() {
24846
- const envKey = process.env.TRADING_BOY_API_KEY;
24847
- if (envKey) {
24848
- return {
24849
- authenticated: true,
24850
- redactedKey: redactApiKey(envKey),
24851
- storageMethod: "TRADING_BOY_API_KEY env var"
24852
- };
24853
- }
24854
- const creds = await loadCredentials();
24855
- if (!creds) {
24856
- 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;
24857
25061
  }
25062
+ const creds = resolved.source === "stored credentials" ? resolved.storedCredentials ?? await loadCredentials() : null;
24858
25063
  const result = {
24859
25064
  authenticated: true,
24860
- email: creds.email,
24861
- plan: creds.plan,
24862
- keyId: creds.keyId,
24863
- redactedKey: redactApiKey(creds.apiKey),
24864
- storageMethod: creds.storageMethod,
24865
- 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
24866
25071
  };
24867
25072
  try {
24868
- const apiKey = await resolveApiKey();
24869
25073
  const response = await fetch(`${getApiBase()}/api/v1/auth/verify`, {
24870
25074
  method: "POST",
24871
25075
  headers: {
24872
25076
  "Content-Type": "application/json",
24873
- "Authorization": `Bearer ${apiKey}`
25077
+ "Authorization": `Bearer ${resolved.apiKey}`
24874
25078
  },
24875
25079
  body: "{}"
24876
25080
  });
@@ -24939,16 +25143,9 @@ function registerWhoamiCommand(program2) {
24939
25143
  try {
24940
25144
  const result = await executeWhoami();
24941
25145
  if (options.showKey && result.authenticated) {
24942
- const envKey = process.env.TRADING_BOY_API_KEY;
24943
- if (envKey) {
24944
- console.log(envKey);
24945
- return;
24946
- }
24947
- const creds = await loadCredentials();
24948
- if (creds) {
24949
- console.log(creds.apiKey);
24950
- return;
24951
- }
25146
+ const resolved = await resolveApiKeyWithSource();
25147
+ console.log(resolved.apiKey);
25148
+ return;
24952
25149
  }
24953
25150
  if (options.format === "json") {
24954
25151
  console.log(JSON.stringify({ ...result, apiUrl: getApiBase() }, null, 2));
@@ -25369,6 +25566,12 @@ async function openBrowser(url) {
25369
25566
  // dist/commands/onboarding.js
25370
25567
  var DEFAULT_FIRST_AGENT_AUTONOMY = "FULLY_AUTONOMOUS";
25371
25568
  var MIN_SCAN_INTERVAL_MS = 6e4;
25569
+ var LEADERBOARD_ALIAS_PATTERN = /^[a-zA-Z0-9_-]{3,30}$/;
25570
+ function defaultLeaderboardAlias(name) {
25571
+ const normalized = name.trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
25572
+ const alias = normalized || "trader";
25573
+ return alias.slice(0, 24);
25574
+ }
25372
25575
  function trackOnboardingEvent(eventType, metadata = {}) {
25373
25576
  try {
25374
25577
  emitCliProductEvent(eventType, {
@@ -25442,17 +25645,37 @@ async function runOnboarding() {
25442
25645
  return true;
25443
25646
  }
25444
25647
  });
25648
+ const benchmarkOptIn = await confirm({
25649
+ message: "Show this trader on the public leaderboard? You can opt in later from the CLI.",
25650
+ default: false
25651
+ });
25652
+ let publicAlias;
25653
+ if (benchmarkOptIn) {
25654
+ publicAlias = await input({
25655
+ message: "Public leaderboard alias (3-30 letters, numbers, underscores, or hyphens)",
25656
+ default: defaultLeaderboardAlias(name),
25657
+ validate: (v) => LEADERBOARD_ALIAS_PATTERN.test(v.trim()) ? true : "Alias must be 3-30 characters using letters, numbers, underscores, or hyphens."
25658
+ });
25659
+ }
25445
25660
  try {
25446
25661
  if (await isRemoteMode()) {
25447
25662
  const result = await apiRequest("/api/v1/traders", {
25448
25663
  method: "POST",
25449
- body: { name: name.trim(), riskToleranceMax: parseFloat(maxDrawdown) }
25664
+ body: {
25665
+ name: name.trim(),
25666
+ maxDrawdown: parseFloat(maxDrawdown),
25667
+ benchmarkOptIn,
25668
+ publicAlias: publicAlias?.trim()
25669
+ }
25450
25670
  });
25451
25671
  console.log(source_default.green(` \u2713 Trader "${result.name}" registered (id: ${result.id})`));
25452
25672
  if (result.publicAlias) {
25453
25673
  console.log(source_default.dim(` Leaderboard alias: ${result.publicAlias}`));
25454
25674
  console.log(source_default.dim(" This alias is public on the leaderboard. Change it anytime:"));
25455
25675
  console.log(source_default.dim(" trading-boy trader set-alias <name> <new-alias>"));
25676
+ } else {
25677
+ console.log(source_default.dim(" Public leaderboard: private. Opt in later with:"));
25678
+ console.log(source_default.dim(" trading-boy trader leaderboard opt-in <name> <alias>"));
25456
25679
  }
25457
25680
  traderName = name.trim();
25458
25681
  traderId = result.id;
@@ -25637,7 +25860,7 @@ function printExploreQuickReference() {
25637
25860
  console.log(` ${source_default.white("trading-boy stats")} ${source_default.dim("Track performance")}`);
25638
25861
  console.log("");
25639
25862
  console.log(source_default.dim(" Build an agent later: trading-boy onboarding"));
25640
- 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"));
25641
25864
  console.log("");
25642
25865
  }
25643
25866
  function printAgentRuntimeOverview() {
@@ -25681,7 +25904,7 @@ function printAgentQuickReference(traderRegistered) {
25681
25904
  console.log(` ${source_default.white("trading-boy behavioral profile --trader <trader-id>")} ${source_default.dim("Behavioral analysis")}`);
25682
25905
  }
25683
25906
  console.log("");
25684
- 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"));
25685
25908
  console.log("");
25686
25909
  }
25687
25910
  async function resolveTraderId(nameOrId) {
@@ -26110,7 +26333,7 @@ async function pollForApiKey(token, onTick) {
26110
26333
  if (result.status === "expired" || result.status === "not_found") {
26111
26334
  return {
26112
26335
  success: false,
26113
- 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."
26114
26337
  };
26115
26338
  }
26116
26339
  if (result.status === "already_retrieved") {
@@ -26127,7 +26350,7 @@ async function pollForApiKey(token, onTick) {
26127
26350
  }
26128
26351
  return {
26129
26352
  success: false,
26130
- 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."
26131
26354
  };
26132
26355
  }
26133
26356
  async function saveApiKey(apiKey, metadata) {
@@ -26439,7 +26662,7 @@ function sleep2(ms) {
26439
26662
  var ALLOWED_CHECKOUT_DOMAINS = /* @__PURE__ */ new Set([
26440
26663
  "checkout.stripe.com",
26441
26664
  "billing.stripe.com",
26442
- "api.cabal.ventures"
26665
+ "api.tradingboy.ai"
26443
26666
  ]);
26444
26667
  function validateBrowserUrl(url) {
26445
26668
  const parsed = new URL(url);
@@ -26595,7 +26818,7 @@ function formatElapsed2(ms) {
26595
26818
  return `${seconds}s`;
26596
26819
  }
26597
26820
  function registerForgotKeyCommand(program2) {
26598
- 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) => {
26599
26822
  const { email } = options;
26600
26823
  if (!isValidEmail2(email)) {
26601
26824
  console.error(source_default.red(" Invalid email address."));
@@ -26610,6 +26833,7 @@ function registerForgotKeyCommand(program2) {
26610
26833
  const recovery = await requestKeyRecovery(email);
26611
26834
  console.log(source_default.white(" Recovery email sent! Check your inbox."));
26612
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."));
26613
26837
  console.log("");
26614
26838
  let pollResult;
26615
26839
  try {
@@ -26744,9 +26968,9 @@ function formatAttributionOutput(data) {
26744
26968
  }
26745
26969
  lines.push(source_default.bold(" Summary"));
26746
26970
  lines.push(` ${source_default.gray("Total PnL:")} ${colorPnl(s.totalPnlUsd ?? 0)}`);
26747
- lines.push(` ${source_default.gray("Signal Alpha:")} ${formatPct2(s.avgSignalAlphaPct ?? 0)}`);
26748
- lines.push(` ${source_default.gray("Execution Cost:")} ${formatPct2(s.avgExecutionCostPct ?? 0)}`);
26749
- 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)}`);
26750
26974
  lines.push(` ${source_default.gray("Trade Count:")} ${s.trades ?? 0}`);
26751
26975
  const setupEntries = Object.entries(data.analysis.bySetupType ?? {});
26752
26976
  if (setupEntries.length > 0) {
@@ -26755,7 +26979,7 @@ function formatAttributionOutput(data) {
26755
26979
  lines.push(` ${source_default.gray("Setup".padEnd(20))}${"Signal%".padStart(10)}${"Exec%".padStart(10)}${"Timing%".padStart(10)}${"N".padStart(5)}`);
26756
26980
  lines.push(source_default.gray(" " + "\u2500".repeat(55)));
26757
26981
  for (const [setup, v] of setupEntries) {
26758
- 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)}`);
26759
26983
  }
26760
26984
  }
26761
26985
  if (data.analysis.attributions.length > 0) {
@@ -26790,13 +27014,17 @@ function colorPnl(usd) {
26790
27014
  }
26791
27015
  function formatUsd4(val) {
26792
27016
  const v = val ?? 0;
26793
- const sign = v >= 0 ? "+" : "";
26794
- return `${sign}$${v.toFixed(2)}`;
27017
+ const sign = v >= 0 ? "+" : "-";
27018
+ return `${sign}$${Math.abs(v).toFixed(2)}`;
26795
27019
  }
26796
27020
  function formatPct2(val) {
26797
27021
  const v = val ?? 0;
26798
27022
  return `${v >= 0 ? "+" : ""}${(v * 100).toFixed(1)}%`;
26799
27023
  }
27024
+ function formatPctPoints(val) {
27025
+ const v = val ?? 0;
27026
+ return `${v >= 0 ? "+" : ""}${v.toFixed(1)}%`;
27027
+ }
26800
27028
  function registerEdgeCommand(program2) {
26801
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) => {
26802
27030
  try {
@@ -28149,6 +28377,8 @@ var AUTONOMY_HELP_UPDATE = "Autonomy mode. Choose Fully auto (FULLY_AUTONOMOUS),
28149
28377
  var MIN_CONFIDENCE_HELP = "Minimum confidence threshold (0-1, so 0.70 = 70%)";
28150
28378
  var EXIT_REASONER_HELP = "Enable LLM-powered exit reasoning for open positions (hold, exit, tighten stop, or extend take profit)";
28151
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";
28152
28382
  var SHARED_SCAN_CACHE_INTERNAL_HELP = "Internal/load-test only: bypass shared scan cache for this agent";
28153
28383
  var ENABLE_SHARED_SCAN_CACHE_HELP = "Internal/load-test only: re-enable shared scan cache for this agent";
28154
28384
  function formatShortDate5(isoString) {
@@ -28213,7 +28443,7 @@ function parseHumanInterval2(value) {
28213
28443
  var MIN_SCAN_INTERVAL_MS2 = 6e4;
28214
28444
  function registerAgentCommand(program2) {
28215
28445
  const agent = program2.command("agent").description("Manage autonomous trading agents");
28216
- 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) => {
28217
28447
  if (!await ensureRemote())
28218
28448
  return;
28219
28449
  if (!options.traderId) {
@@ -28269,6 +28499,14 @@ function registerAgentCommand(program2) {
28269
28499
  body.assetClass = options.assetClass;
28270
28500
  if (options.exitReasoner)
28271
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 };
28272
28510
  if (options.soulFile) {
28273
28511
  const path3 = resolve(options.soulFile);
28274
28512
  if (!existsSync2(path3)) {
@@ -28306,7 +28544,8 @@ function registerAgentCommand(program2) {
28306
28544
  console.log(` ${source_default.gray("Trader:")} ${result.traderId}`);
28307
28545
  console.log(` ${source_default.gray("Strategy:")} ${result.strategyId}${result.autoStrategyCreated ? source_default.dim(" (auto-created)") : ""}`);
28308
28546
  console.log(` ${source_default.gray("Autonomy:")} ${formatAutonomy(result.autonomyLevel)}`);
28309
- 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"}`);
28310
28549
  console.log(` ${source_default.gray("Interval:")} ${formatInterval2(result.scanIntervalMs)}`);
28311
28550
  console.log(` ${source_default.gray("Next scan:")} ${formatShortDate5(result.nextScanAt)}`);
28312
28551
  console.log("");
@@ -28376,7 +28615,8 @@ function registerAgentCommand(program2) {
28376
28615
  if (a.decideModel)
28377
28616
  console.log(` ${source_default.gray("Decide model:")} ${a.decideModel}`);
28378
28617
  console.log(` ${source_default.gray("Shared scan cache:")} ${a.disableSharedScanCache ? source_default.yellow("bypassed") : source_default.dim("enabled")}`);
28379
- 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"}`);
28380
28620
  console.log(` ${source_default.gray("Scan interval:")} ${formatInterval2(a.scanIntervalMs)}`);
28381
28621
  console.log(` ${source_default.gray("Max daily trades:")} ${a.maxDailyTrades}`);
28382
28622
  console.log(` ${source_default.gray("Max daily loss:")} $${a.maxDailyLossUsd}`);
@@ -28500,7 +28740,7 @@ function registerAgentCommand(program2) {
28500
28740
  handleApiError(error2, "Position exit failed", logger30);
28501
28741
  }
28502
28742
  });
28503
- 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) => {
28504
28744
  if (!await ensureRemote())
28505
28745
  return;
28506
28746
  const body = {};
@@ -28558,6 +28798,14 @@ function registerAgentCommand(program2) {
28558
28798
  body.exitReasonerEnabled = true;
28559
28799
  if (options.disableExitReasoner)
28560
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 };
28561
28809
  if (options.assetClass)
28562
28810
  body.assetClass = options.assetClass;
28563
28811
  if (options.soulFile) {
@@ -28603,6 +28851,417 @@ function registerAgentCommand(program2) {
28603
28851
  });
28604
28852
  }
28605
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
+
28606
29265
  // dist/cli.js
28607
29266
  import { readFileSync as readFileSync5 } from "node:fs";
28608
29267
  import { fileURLToPath as fileURLToPath2 } from "node:url";
@@ -28654,6 +29313,7 @@ function createCli() {
28654
29313
  registerSuggestionsCommand(program2);
28655
29314
  registerCronCommand(program2);
28656
29315
  registerAgentCommand(program2);
29316
+ registerPredictionMarketPaperCommand(program2);
28657
29317
  registerConfigCommand(program2);
28658
29318
  const GROUPS = {
28659
29319
  "Core": ["context", "query", "watch"],
@@ -28662,7 +29322,7 @@ function createCli() {
28662
29322
  "Edge & Safety": ["edge", "edge-guard", "coaching", "thesis"],
28663
29323
  "Strategy & Benchmarking": ["strategy", "replay", "benchmark", "suggestions"],
28664
29324
  "Scheduling": ["cron"],
28665
- "Agents": ["agent"],
29325
+ "Agents": ["agent", "prediction-market-paper"],
28666
29326
  "Account": ["login", "logout", "whoami", "billing", "subscribe", "rotate-key", "forgot-key", "connect-chatgpt", "config"]
28667
29327
  };
28668
29328
  program2.addHelpText("before", "");