@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.
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 = {