@trading-boy/cli 1.11.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +50 -22
  2. package/dist/api-client.d.ts +4 -7
  3. package/dist/api-client.js +8 -13
  4. package/dist/cli.bundle.js +1977 -33976
  5. package/dist/credentials.js +1 -1
  6. package/dist/index.d.ts +0 -28
  7. package/dist/index.js +0 -24
  8. package/dist/logger.d.ts +8 -0
  9. package/dist/logger.js +12 -0
  10. package/dist/utils.js +3 -3
  11. package/package.json +20 -5
  12. package/dist/cli.d.ts +0 -5
  13. package/dist/cli.js +0 -157
  14. package/dist/commands/agent-cmd.d.ts +0 -9
  15. package/dist/commands/agent-cmd.js +0 -572
  16. package/dist/commands/audit.d.ts +0 -18
  17. package/dist/commands/audit.js +0 -73
  18. package/dist/commands/behavioral.d.ts +0 -73
  19. package/dist/commands/behavioral.js +0 -349
  20. package/dist/commands/benchmark-cmd.d.ts +0 -3
  21. package/dist/commands/benchmark-cmd.js +0 -191
  22. package/dist/commands/billing.d.ts +0 -12
  23. package/dist/commands/billing.js +0 -142
  24. package/dist/commands/catalysts.d.ts +0 -17
  25. package/dist/commands/catalysts.js +0 -151
  26. package/dist/commands/coaching-cmd.d.ts +0 -16
  27. package/dist/commands/coaching-cmd.js +0 -222
  28. package/dist/commands/config-cmd.d.ts +0 -30
  29. package/dist/commands/config-cmd.js +0 -515
  30. package/dist/commands/connect-chatgpt.d.ts +0 -5
  31. package/dist/commands/connect-chatgpt.js +0 -293
  32. package/dist/commands/connect-claude.d.ts +0 -5
  33. package/dist/commands/connect-claude.js +0 -280
  34. package/dist/commands/context.d.ts +0 -41
  35. package/dist/commands/context.js +0 -405
  36. package/dist/commands/cron-cmd.d.ts +0 -3
  37. package/dist/commands/cron-cmd.js +0 -305
  38. package/dist/commands/decisions.d.ts +0 -57
  39. package/dist/commands/decisions.js +0 -364
  40. package/dist/commands/edge-cmd.d.ts +0 -78
  41. package/dist/commands/edge-cmd.js +0 -183
  42. package/dist/commands/edge-guard-cmd.d.ts +0 -36
  43. package/dist/commands/edge-guard-cmd.js +0 -169
  44. package/dist/commands/events.d.ts +0 -3
  45. package/dist/commands/events.js +0 -117
  46. package/dist/commands/infra.d.ts +0 -24
  47. package/dist/commands/infra.js +0 -137
  48. package/dist/commands/journal.d.ts +0 -3
  49. package/dist/commands/journal.js +0 -302
  50. package/dist/commands/login.d.ts +0 -18
  51. package/dist/commands/login.js +0 -127
  52. package/dist/commands/logout.d.ts +0 -8
  53. package/dist/commands/logout.js +0 -108
  54. package/dist/commands/narratives.d.ts +0 -3
  55. package/dist/commands/narratives.js +0 -259
  56. package/dist/commands/onboarding.d.ts +0 -7
  57. package/dist/commands/onboarding.js +0 -298
  58. package/dist/commands/query.d.ts +0 -32
  59. package/dist/commands/query.js +0 -135
  60. package/dist/commands/replay-cmd.d.ts +0 -43
  61. package/dist/commands/replay-cmd.js +0 -184
  62. package/dist/commands/review.d.ts +0 -3
  63. package/dist/commands/review.js +0 -443
  64. package/dist/commands/risk.d.ts +0 -47
  65. package/dist/commands/risk.js +0 -158
  66. package/dist/commands/social.d.ts +0 -43
  67. package/dist/commands/social.js +0 -318
  68. package/dist/commands/soul-wizard.d.ts +0 -29
  69. package/dist/commands/soul-wizard.js +0 -155
  70. package/dist/commands/strategy-cmd.d.ts +0 -44
  71. package/dist/commands/strategy-cmd.js +0 -340
  72. package/dist/commands/subscribe.d.ts +0 -78
  73. package/dist/commands/subscribe.js +0 -552
  74. package/dist/commands/suggestions-cmd.d.ts +0 -24
  75. package/dist/commands/suggestions-cmd.js +0 -148
  76. package/dist/commands/thesis-cmd.d.ts +0 -3
  77. package/dist/commands/thesis-cmd.js +0 -129
  78. package/dist/commands/trader.d.ts +0 -30
  79. package/dist/commands/trader.js +0 -971
  80. package/dist/commands/watch.d.ts +0 -16
  81. package/dist/commands/watch.js +0 -104
  82. package/dist/commands/whoami.d.ts +0 -14
  83. package/dist/commands/whoami.js +0 -105
@@ -1,302 +0,0 @@
1
- import { Option } from 'commander';
2
- import { padRight, formatConnectionError } from '../utils.js';
3
- import { apiRequest, ApiError } from '../api-client.js';
4
- import { registerReviewCommand } from './review.js';
5
- // ─── Default Trader ───
6
- const DEFAULT_TRADER_ID = 'default';
7
- // ─── Command Registration ───
8
- export function registerJournalCommand(program) {
9
- const journal = program
10
- .command('journal')
11
- .description('Trade journaling commands');
12
- // Register review subcommands
13
- registerReviewCommand(journal);
14
- const log = journal
15
- .command('log')
16
- .description('Log trade decisions')
17
- .action(() => {
18
- log.help();
19
- });
20
- // ─── journal log entry <symbol> ───
21
- log
22
- .command('entry <symbol>')
23
- .description('Log a trade entry')
24
- .option('--thesis <thesis>', 'The reasoning behind the trade')
25
- .option('--setup <setup>', 'Setup type (BREAKOUT, MOMENTUM, etc.)')
26
- .option('--direction <direction>', 'Trade direction (LONG, SHORT)')
27
- .option('--size <size>', 'Position size in token units', parseFloat)
28
- .option('--size-usd <sizeUsd>', 'Position size in USD', parseFloat)
29
- .option('--emotion <emotion>', 'Emotional tag (CONVICTION, FOMO, REVENGE, IMPULSE, FEAR, GREED, NEUTRAL)')
30
- .option('--confidence <confidence>', 'Confidence level (0-1)', parseFloat)
31
- .option('--trader <traderId>', 'Trader ID', DEFAULT_TRADER_ID)
32
- .option('--mint <mint>', 'Token mint address')
33
- .option('--tx-hash <txHash>', 'Solana transaction signature')
34
- .option('--triggers <triggers...>', 'What initiated the trade')
35
- .option('--plan <planId>', 'Link to a pre-trade plan (auto-fills fields)')
36
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
37
- .action(async (symbol, options) => {
38
- // ─── Input validation ───
39
- if (options.direction !== undefined && !['LONG', 'SHORT'].includes(options.direction.toUpperCase())) {
40
- console.error('Error: --direction must be LONG or SHORT');
41
- process.exitCode = 1;
42
- return;
43
- }
44
- if (options.confidence !== undefined && (!Number.isFinite(options.confidence) || options.confidence < 0 || options.confidence > 1)) {
45
- console.error('Error: --confidence must be a number between 0 and 1');
46
- process.exitCode = 1;
47
- return;
48
- }
49
- try {
50
- const result = await apiRequest('/api/v1/decisions', {
51
- method: 'POST',
52
- body: {
53
- traderId: options.trader,
54
- decisionType: 'TRADE_ENTRY',
55
- tokenSymbol: symbol.toUpperCase(),
56
- ...(options.mint ? { tokenMint: options.mint } : {}),
57
- direction: options.direction?.toUpperCase(),
58
- size: options.size,
59
- sizeUsd: options.sizeUsd,
60
- thesis: options.thesis,
61
- setupType: options.setup,
62
- emotionalTag: options.emotion,
63
- confidence: options.confidence,
64
- triggers: options.triggers,
65
- txHash: options.txHash,
66
- },
67
- });
68
- if (options.format === 'json') {
69
- console.log(JSON.stringify(result, null, 2));
70
- return;
71
- }
72
- console.log(`Entry logged: id=${result.id} hash=${result.hash}`);
73
- }
74
- catch (error) {
75
- const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
76
- const connErr = formatConnectionError(message);
77
- if (connErr) {
78
- console.error(connErr);
79
- process.exitCode = 1;
80
- return;
81
- }
82
- console.error(`Error: ${message}`);
83
- if (error instanceof ApiError && error.body && typeof error.body === 'object') {
84
- const detail = error.body.error ?? error.body.code;
85
- if (detail)
86
- console.error(`Detail: ${detail}`);
87
- }
88
- process.exitCode = error instanceof ApiError ? 2 : 1;
89
- }
90
- });
91
- // ─── journal log exit <symbol> ───
92
- log
93
- .command('exit <symbol>')
94
- .description('Log a trade exit')
95
- .option('--link-entry <entryId>', 'ID of the entry DecisionEvent to link')
96
- .option('--trader <traderId>', 'Trader ID', DEFAULT_TRADER_ID)
97
- .option('--mint <mint>', 'Token mint address')
98
- .option('--tx-hash <txHash>', 'Solana transaction signature')
99
- .option('--thesis <thesis>', 'Exit reasoning')
100
- .option('--size <size>', 'Position size in token units', parseFloat)
101
- .option('--size-usd <sizeUsd>', 'Position size in USD', parseFloat)
102
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
103
- .action(async (symbol, options) => {
104
- try {
105
- const result = await apiRequest('/api/v1/decisions', {
106
- method: 'POST',
107
- body: {
108
- traderId: options.trader,
109
- decisionType: 'TRADE_EXIT',
110
- tokenSymbol: symbol.toUpperCase(),
111
- ...(options.mint ? { tokenMint: options.mint } : {}),
112
- thesis: options.thesis,
113
- size: options.size,
114
- sizeUsd: options.sizeUsd,
115
- txHash: options.txHash,
116
- linkedEntryId: options.linkEntry,
117
- },
118
- });
119
- if (options.format === 'json') {
120
- console.log(JSON.stringify(result, null, 2));
121
- return;
122
- }
123
- console.log(`Exit logged: id=${result.id} hash=${result.hash}`);
124
- }
125
- catch (error) {
126
- const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
127
- const connErr = formatConnectionError(message);
128
- if (connErr) {
129
- console.error(connErr);
130
- process.exitCode = 1;
131
- return;
132
- }
133
- console.error(`Error: ${message}`);
134
- if (error instanceof ApiError && error.body && typeof error.body === 'object') {
135
- const detail = error.body.error ?? error.body.code;
136
- if (detail)
137
- console.error(`Detail: ${detail}`);
138
- }
139
- process.exitCode = error instanceof ApiError ? 2 : 1;
140
- }
141
- });
142
- // ─── journal list ───
143
- journal
144
- .command('list')
145
- .description('List recent trade decisions')
146
- .option('--token <symbol>', 'Filter by token symbol')
147
- .option('--limit <n>', 'Maximum number of results', parseInt, 10)
148
- .option('--trader <traderId>', 'Trader ID', DEFAULT_TRADER_ID)
149
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
150
- .action(async (options) => {
151
- try {
152
- const params = new URLSearchParams();
153
- if (options.trader)
154
- params.set('traderId', options.trader);
155
- if (options.token)
156
- params.set('tokenSymbol', options.token.toUpperCase());
157
- if (options.limit)
158
- params.set('limit', String(options.limit));
159
- const qs = params.toString() ? `?${params.toString()}` : '';
160
- const result = await apiRequest(`/api/v1/decisions${qs}`);
161
- const decisions = result.decisions ?? [];
162
- if (decisions.length === 0) {
163
- if (options.format === 'json') {
164
- console.log(JSON.stringify({ decisions: [] }, null, 2));
165
- }
166
- else {
167
- console.log('No decisions found.');
168
- }
169
- return;
170
- }
171
- if (options.format === 'json') {
172
- console.log(JSON.stringify(result, null, 2));
173
- return;
174
- }
175
- // Print formatted table
176
- console.log('');
177
- console.log(padRight('ID', 38) +
178
- padRight('Type', 20) +
179
- padRight('Token', 8) +
180
- padRight('Direction', 10) +
181
- padRight('Price', 12) +
182
- padRight('Confidence', 12) +
183
- padRight('Hash', 10));
184
- console.log('-'.repeat(110));
185
- for (const d of decisions) {
186
- const price = (d.entryPrice ?? d.exitPrice);
187
- console.log(padRight(String(d.id ?? ''), 38) +
188
- padRight(String(d.decisionType ?? ''), 20) +
189
- padRight(String(d.tokenSymbol ?? ''), 8) +
190
- padRight(String(d.direction ?? '-'), 10) +
191
- padRight(price !== null && price !== undefined ? Number(price).toFixed(2) : '-', 12) +
192
- padRight(d.confidence !== null && d.confidence !== undefined ? Number(d.confidence).toFixed(2) : '-', 12) +
193
- (d.hash ? String(d.hash).slice(0, 8) + '...' : '-'));
194
- }
195
- console.log('');
196
- console.log(`Total: ${decisions.length} decision(s)`);
197
- }
198
- catch (error) {
199
- const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
200
- const connErr = formatConnectionError(message);
201
- if (connErr) {
202
- console.error(connErr);
203
- process.exitCode = 1;
204
- return;
205
- }
206
- console.error(`Error: ${message}`);
207
- if (error instanceof ApiError && error.body && typeof error.body === 'object') {
208
- const detail = error.body.error ?? error.body.code;
209
- if (detail)
210
- console.error(`Detail: ${detail}`);
211
- }
212
- process.exitCode = error instanceof ApiError ? 2 : 1;
213
- }
214
- });
215
- // ─── journal plan ───
216
- journal
217
- .command('plan')
218
- .description('List pre-trade plans')
219
- .option('--trader <traderId>', 'Trader ID', DEFAULT_TRADER_ID)
220
- .option('--token <symbol>', 'Filter by token symbol')
221
- .option('--status <status>', 'Filter by status: PENDING, EXECUTED, CANCELLED, EXPIRED')
222
- .option('--limit <n>', 'Maximum results', parseInt, 50)
223
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
224
- .action(async (options) => {
225
- try {
226
- const params = new URLSearchParams();
227
- params.set('traderId', options.trader);
228
- if (options.token)
229
- params.set('tokenSymbol', options.token);
230
- if (options.status)
231
- params.set('status', options.status);
232
- params.set('limit', String(options.limit));
233
- const data = await apiRequest(`/api/v1/journal/plan?${params.toString()}`);
234
- if (options.format === 'json') {
235
- console.log(JSON.stringify(data, null, 2));
236
- }
237
- else {
238
- if (data.plans.length === 0) {
239
- console.log(' No pre-trade plans found.');
240
- }
241
- else {
242
- console.log(`\n Pre-Trade Plans (${data.count}):\n`);
243
- for (const plan of data.plans) {
244
- console.log(` ${padRight(String(plan.id ?? ''), 14)} ${padRight(String(plan.tokenSymbol ?? ''), 8)} ${padRight(String(plan.status ?? ''), 12)} ${plan.direction ?? ''}`);
245
- }
246
- console.log('');
247
- }
248
- }
249
- }
250
- catch (error) {
251
- const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
252
- const connErr = formatConnectionError(message);
253
- if (connErr) {
254
- console.error(connErr);
255
- process.exitCode = 1;
256
- return;
257
- }
258
- console.error(`Error: ${message}`);
259
- process.exitCode = error instanceof ApiError ? 2 : 1;
260
- }
261
- });
262
- // ─── journal size ───
263
- journal
264
- .command('size')
265
- .description('Kelly criterion position sizing recommendation')
266
- .requiredOption('--trader <traderId>', 'Trader ID')
267
- .option('--capital <usd>', 'Portfolio capital in USD', parseFloat, 10000)
268
- .option('--fraction <f>', 'Kelly fraction (0-1, default 0.5 = Half Kelly)', parseFloat, 0.5)
269
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
270
- .action(async (options) => {
271
- try {
272
- const params = new URLSearchParams();
273
- params.set('traderId', options.trader);
274
- params.set('capitalUsd', String(options.capital));
275
- params.set('fraction', String(options.fraction));
276
- const data = await apiRequest(`/api/v1/journal/size?${params.toString()}`);
277
- if (options.format === 'json') {
278
- console.log(JSON.stringify(data, null, 2));
279
- }
280
- else {
281
- console.log('\n Kelly Position Sizing:\n');
282
- for (const [key, value] of Object.entries(data)) {
283
- console.log(` ${padRight(key, 24)} ${value}`);
284
- }
285
- console.log('');
286
- }
287
- }
288
- catch (error) {
289
- const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
290
- const connErr = formatConnectionError(message);
291
- if (connErr) {
292
- console.error(connErr);
293
- process.exitCode = 1;
294
- return;
295
- }
296
- console.error(`Error: ${message}`);
297
- process.exitCode = error instanceof ApiError ? 2 : 1;
298
- }
299
- });
300
- }
301
- // PlanOptions and SizeOptions removed — commands hidden until API endpoints exist
302
- //# sourceMappingURL=journal.js.map
@@ -1,18 +0,0 @@
1
- import { Command } from 'commander';
2
- interface VerifyResponse {
3
- valid: boolean;
4
- email?: string;
5
- plan?: string;
6
- keyId?: string;
7
- }
8
- export declare function validateApiKeyFormat(key: string): boolean;
9
- export declare function verifyApiKey(apiKey: string): Promise<VerifyResponse>;
10
- export declare function executeLogin(apiKey: string): Promise<{
11
- success: boolean;
12
- email?: string;
13
- plan?: string;
14
- keyId?: string;
15
- }>;
16
- export declare function registerLoginCommand(program: Command): void;
17
- export {};
18
- //# sourceMappingURL=login.d.ts.map
@@ -1,127 +0,0 @@
1
- import { Option } from 'commander';
2
- import chalk from 'chalk';
3
- import { createLogger } from '@trading-boy/core';
4
- import { storeCredentials, redactApiKey } from '../credentials.js';
5
- import { getApiBase } from '../api-client.js';
6
- const logger = createLogger('cli-login');
7
- // ─── API Key Validation ───
8
- const API_KEY_PATTERN = /^tb_(live|test|free)_[a-f0-9]{32}$/;
9
- export function validateApiKeyFormat(key) {
10
- return API_KEY_PATTERN.test(key);
11
- }
12
- // ─── Key Verification ───
13
- export async function verifyApiKey(apiKey) {
14
- const apiBase = getApiBase();
15
- const response = await fetch(`${apiBase}/api/v1/auth/verify`, {
16
- method: 'POST',
17
- headers: {
18
- 'Content-Type': 'application/json',
19
- 'Authorization': `Bearer ${apiKey}`,
20
- },
21
- body: '{}',
22
- });
23
- if (!response.ok) {
24
- if (response.status === 401) {
25
- return { valid: false };
26
- }
27
- throw new Error(`Verification failed: ${response.status}`);
28
- }
29
- return (await response.json());
30
- }
31
- // ─── Login Logic ───
32
- export async function executeLogin(apiKey) {
33
- // Validate format
34
- if (!validateApiKeyFormat(apiKey)) {
35
- return { success: false };
36
- }
37
- // Verify with API
38
- const result = await verifyApiKey(apiKey);
39
- if (!result.valid) {
40
- return { success: false };
41
- }
42
- // Store credentials
43
- await storeCredentials(apiKey, {
44
- keyId: result.keyId,
45
- email: result.email,
46
- plan: result.plan,
47
- });
48
- return {
49
- success: true,
50
- email: result.email,
51
- plan: result.plan,
52
- keyId: result.keyId,
53
- };
54
- }
55
- // ─── Command Registration ───
56
- export function registerLoginCommand(program) {
57
- program
58
- .command('login')
59
- .description('Authenticate with your Trading Boy API key')
60
- .addOption(new Option('--api-key <key>', 'API key (deprecated — use TRADING_BOY_API_KEY env var)').hideHelp())
61
- .action(async (opts) => {
62
- try {
63
- let apiKey;
64
- if (opts.apiKey) {
65
- // Non-interactive mode — warn about shell history exposure
66
- process.stderr.write(chalk.yellow('Warning: --api-key is visible in shell history. Prefer TRADING_BOY_API_KEY env var.\n'));
67
- apiKey = opts.apiKey;
68
- }
69
- else {
70
- // Interactive mode — dynamic import to avoid loading inquirer for non-interactive use
71
- const { password } = await import('@inquirer/prompts');
72
- console.log('');
73
- console.log(chalk.bold(' Trading Boy — Login'));
74
- console.log(chalk.gray(' ' + '─'.repeat(40)));
75
- console.log(chalk.dim(' Don\'t have a key? Run: trading-boy subscribe'));
76
- console.log('');
77
- apiKey = await password({
78
- message: 'Enter your API key',
79
- mask: '*',
80
- validate: (input) => {
81
- if (!input)
82
- return 'API key is required';
83
- if (!validateApiKeyFormat(input)) {
84
- return 'Invalid key format. Expected: tb_live_<32hex>, tb_test_<32hex>, or tb_free_<32hex>';
85
- }
86
- return true;
87
- },
88
- });
89
- }
90
- // Validate format before making network request
91
- if (!validateApiKeyFormat(apiKey)) {
92
- console.error(chalk.red(' Invalid key format. Expected: tb_live_<32hex>, tb_test_<32hex>, or tb_free_<32hex>'));
93
- process.exitCode = 1;
94
- return;
95
- }
96
- // Verify with spinner (silent when piped)
97
- const { createSpinner } = await import('../utils.js');
98
- const spinner = (await createSpinner(' Verifying key...')).start();
99
- const result = await executeLogin(apiKey);
100
- if (!result.success) {
101
- spinner.fail(' Invalid API key');
102
- process.exitCode = 1;
103
- return;
104
- }
105
- spinner.succeed(' Authenticated successfully');
106
- console.log('');
107
- if (result.email) {
108
- console.log(` ${chalk.gray('Account:')} ${result.email}`);
109
- }
110
- if (result.plan) {
111
- console.log(` ${chalk.gray('Plan:')} ${result.plan}`);
112
- }
113
- if (result.keyId) {
114
- console.log(` ${chalk.gray('Key ID:')} ${result.keyId}`);
115
- }
116
- console.log(` ${chalk.gray('Key:')} ${redactApiKey(apiKey)}`);
117
- console.log('');
118
- }
119
- catch (error) {
120
- const message = error instanceof Error ? error.message : String(error);
121
- logger.error({ error: message }, 'Login failed');
122
- console.error(chalk.red(` Error: ${message}`));
123
- process.exitCode = 1;
124
- }
125
- });
126
- }
127
- //# sourceMappingURL=login.js.map
@@ -1,8 +0,0 @@
1
- import { Command } from 'commander';
2
- export declare function executeLogout(): Promise<{
3
- wasAuthenticated: boolean;
4
- redactedKey?: string;
5
- keychainCleared: boolean;
6
- }>;
7
- export declare function registerLogoutCommand(program: Command): void;
8
- //# sourceMappingURL=logout.d.ts.map
@@ -1,108 +0,0 @@
1
- import { Option } from 'commander';
2
- import chalk from 'chalk';
3
- import { createLogger } from '@trading-boy/core';
4
- import { clearCredentials, loadCredentials, redactApiKey } from '../credentials.js';
5
- import { confirm } from '@inquirer/prompts';
6
- const logger = createLogger('cli-logout');
7
- // ─── Logout Logic ───
8
- export async function executeLogout() {
9
- const existing = await loadCredentials();
10
- const wasAuthenticated = existing !== null;
11
- const redactedKey = existing ? redactApiKey(existing.apiKey) : undefined;
12
- const { keychainCleared } = await clearCredentials();
13
- return { wasAuthenticated, redactedKey, keychainCleared };
14
- }
15
- // ─── Command Registration ───
16
- export function registerLogoutCommand(program) {
17
- program
18
- .command('logout')
19
- .description('Clear stored API key and credentials')
20
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
21
- .addOption(new Option('--force', 'Skip confirmation prompt'))
22
- .addOption(new Option('--reveal-key', 'Include full API key in JSON output (requires --force --format json)'))
23
- .action(async (options) => {
24
- try {
25
- const jsonMode = options.format === 'json';
26
- const existing = await loadCredentials();
27
- if (!existing) {
28
- if (jsonMode) {
29
- console.log(JSON.stringify({ loggedOut: false, reason: 'no credentials' }));
30
- }
31
- else {
32
- console.log('');
33
- console.log(chalk.dim(' No credentials found — already logged out.'));
34
- console.log('');
35
- }
36
- return;
37
- }
38
- // JSON mode requires --force to skip interactive prompts
39
- if (jsonMode && !options.force) {
40
- console.error(JSON.stringify({ error: '--force is required with --format json' }));
41
- process.exitCode = 1;
42
- return;
43
- }
44
- if (jsonMode) {
45
- // Non-interactive JSON path
46
- const fullKey = existing.apiKey;
47
- const storageMethod = existing.storageMethod;
48
- const result = await executeLogout();
49
- const output = {
50
- loggedOut: true,
51
- redactedKey: result.redactedKey,
52
- };
53
- if (options.revealKey) {
54
- output.apiKey = fullKey;
55
- }
56
- if (!result.keychainCleared && storageMethod === 'keychain') {
57
- output.keychainWarning = 'Could not remove key from OS keychain. You may need to manually remove the \'trading-boy\' entry from your keychain.';
58
- }
59
- console.log(JSON.stringify(output));
60
- return;
61
- }
62
- // Interactive text path
63
- console.log('');
64
- console.log(chalk.yellow(' Warning: Your API key will be cleared from this machine.'));
65
- console.log(chalk.yellow(' You will need your API key to log back in.'));
66
- console.log(chalk.yellow(' There is no way to recover a lost key — a new one must be provisioned.'));
67
- console.log('');
68
- const proceed = options.force || await confirm({ message: 'Are you sure you want to logout?' });
69
- if (!proceed) {
70
- console.log(chalk.dim(' Logout cancelled.'));
71
- return;
72
- }
73
- // Offer to reveal the full key before deletion
74
- const revealKey = await confirm({
75
- message: 'Would you like to see your full API key before it\'s deleted? (This is your last chance to copy it)',
76
- });
77
- if (revealKey) {
78
- console.log('');
79
- console.log(chalk.cyan(' Your API key:'));
80
- console.log(` ${chalk.bold(existing.apiKey)}`);
81
- console.log('');
82
- }
83
- const storageMethod = existing.storageMethod;
84
- const result = await executeLogout();
85
- console.log('');
86
- console.log(chalk.green(` Logged out successfully.`));
87
- console.log(` ${chalk.gray('Cleared key:')} ${result.redactedKey}`);
88
- if (!result.keychainCleared && storageMethod === 'keychain') {
89
- console.log('');
90
- console.log(chalk.yellow(' Warning: Could not remove key from OS keychain.'));
91
- console.log(chalk.yellow(' You may need to manually remove the \'trading-boy\' entry from your keychain.'));
92
- }
93
- console.log('');
94
- }
95
- catch (error) {
96
- const message = error instanceof Error ? error.message : String(error);
97
- logger.error({ error: message }, 'Logout failed');
98
- if (options.format === 'json') {
99
- console.error(JSON.stringify({ error: message }));
100
- }
101
- else {
102
- console.error(chalk.red(` Error: ${message}`));
103
- }
104
- process.exitCode = 1;
105
- }
106
- });
107
- }
108
- //# sourceMappingURL=logout.js.map
@@ -1,3 +0,0 @@
1
- import { Command } from 'commander';
2
- export declare function registerNarrativeCommand(program: Command): void;
3
- //# sourceMappingURL=narratives.d.ts.map