@trading-boy/cli 1.12.0 → 2.0.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/LICENSE +22 -0
- package/README.md +64 -29
- package/dist/api-client.d.ts +4 -7
- package/dist/api-client.js +8 -13
- package/dist/cli.bundle.js +2314 -33711
- package/dist/credentials.js +1 -1
- package/dist/index.d.ts +0 -28
- package/dist/index.js +0 -24
- package/dist/logger.d.ts +8 -0
- package/dist/logger.js +12 -0
- package/dist/utils.js +3 -3
- package/package.json +30 -16
- package/dist/cli.d.ts +0 -5
- package/dist/cli.js +0 -157
- package/dist/commands/agent-cmd.d.ts +0 -9
- package/dist/commands/agent-cmd.js +0 -567
- package/dist/commands/audit.d.ts +0 -18
- package/dist/commands/audit.js +0 -73
- package/dist/commands/behavioral.d.ts +0 -73
- package/dist/commands/behavioral.js +0 -349
- package/dist/commands/benchmark-cmd.d.ts +0 -3
- package/dist/commands/benchmark-cmd.js +0 -191
- package/dist/commands/billing.d.ts +0 -12
- package/dist/commands/billing.js +0 -142
- package/dist/commands/catalysts.d.ts +0 -17
- package/dist/commands/catalysts.js +0 -151
- package/dist/commands/coaching-cmd.d.ts +0 -16
- package/dist/commands/coaching-cmd.js +0 -222
- package/dist/commands/config-cmd.d.ts +0 -30
- package/dist/commands/config-cmd.js +0 -515
- package/dist/commands/connect-chatgpt.d.ts +0 -5
- package/dist/commands/connect-chatgpt.js +0 -293
- package/dist/commands/connect-claude.d.ts +0 -5
- package/dist/commands/connect-claude.js +0 -280
- package/dist/commands/context.d.ts +0 -41
- package/dist/commands/context.js +0 -405
- package/dist/commands/cron-cmd.d.ts +0 -3
- package/dist/commands/cron-cmd.js +0 -305
- package/dist/commands/decisions.d.ts +0 -57
- package/dist/commands/decisions.js +0 -364
- package/dist/commands/edge-cmd.d.ts +0 -78
- package/dist/commands/edge-cmd.js +0 -183
- package/dist/commands/edge-guard-cmd.d.ts +0 -36
- package/dist/commands/edge-guard-cmd.js +0 -169
- package/dist/commands/events.d.ts +0 -3
- package/dist/commands/events.js +0 -117
- package/dist/commands/infra.d.ts +0 -24
- package/dist/commands/infra.js +0 -137
- package/dist/commands/journal.d.ts +0 -3
- package/dist/commands/journal.js +0 -302
- package/dist/commands/login.d.ts +0 -18
- package/dist/commands/login.js +0 -127
- package/dist/commands/logout.d.ts +0 -8
- package/dist/commands/logout.js +0 -108
- package/dist/commands/narratives.d.ts +0 -3
- package/dist/commands/narratives.js +0 -259
- package/dist/commands/onboarding.d.ts +0 -7
- package/dist/commands/onboarding.js +0 -281
- package/dist/commands/query.d.ts +0 -32
- package/dist/commands/query.js +0 -135
- package/dist/commands/replay-cmd.d.ts +0 -43
- package/dist/commands/replay-cmd.js +0 -184
- package/dist/commands/review.d.ts +0 -3
- package/dist/commands/review.js +0 -443
- package/dist/commands/risk.d.ts +0 -47
- package/dist/commands/risk.js +0 -158
- package/dist/commands/social.d.ts +0 -43
- package/dist/commands/social.js +0 -318
- package/dist/commands/soul-wizard.d.ts +0 -29
- package/dist/commands/soul-wizard.js +0 -155
- package/dist/commands/strategy-cmd.d.ts +0 -44
- package/dist/commands/strategy-cmd.js +0 -335
- package/dist/commands/subscribe.d.ts +0 -78
- package/dist/commands/subscribe.js +0 -552
- package/dist/commands/suggestions-cmd.d.ts +0 -24
- package/dist/commands/suggestions-cmd.js +0 -148
- package/dist/commands/thesis-cmd.d.ts +0 -3
- package/dist/commands/thesis-cmd.js +0 -129
- package/dist/commands/trader.d.ts +0 -30
- package/dist/commands/trader.js +0 -971
- package/dist/commands/watch.d.ts +0 -16
- package/dist/commands/watch.js +0 -104
- package/dist/commands/whoami.d.ts +0 -14
- package/dist/commands/whoami.js +0 -105
package/dist/commands/context.js
DELETED
|
@@ -1,405 +0,0 @@
|
|
|
1
|
-
import { Option } from 'commander';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { createLogger } from '@trading-boy/core';
|
|
4
|
-
import { formatConnectionError, colorChange, colorSentiment, colorRiskScore, formatUsd, ensureRemote } from '../utils.js';
|
|
5
|
-
import { apiRequest, ApiError } from '../api-client.js';
|
|
6
|
-
// ─── Logger ───
|
|
7
|
-
const logger = createLogger('cli-context');
|
|
8
|
-
// ─── Formatters ───
|
|
9
|
-
/**
|
|
10
|
-
* Format the market section of a ContextPackage.
|
|
11
|
-
*/
|
|
12
|
-
export function formatMarketSection(market) {
|
|
13
|
-
const lines = [];
|
|
14
|
-
lines.push(chalk.bold(' Market'));
|
|
15
|
-
if (!market) {
|
|
16
|
-
lines.push(` ${chalk.dim('No market data available.')}`);
|
|
17
|
-
return lines.join('\n');
|
|
18
|
-
}
|
|
19
|
-
const priceStr = formatUsd(market.price);
|
|
20
|
-
lines.push(` ${chalk.gray('Price:')} ${chalk.white.bold(priceStr)}`);
|
|
21
|
-
lines.push(` ${chalk.gray('24h Change:')} ${colorChange(market.priceChange24h)}`);
|
|
22
|
-
lines.push(` ${chalk.gray('Volume (24h):')} ${formatUsd(market.volume24h)}`);
|
|
23
|
-
lines.push(` ${chalk.gray('Market Cap:')} ${formatUsd(market.marketCap)}`);
|
|
24
|
-
if (market.fundingRate !== null) {
|
|
25
|
-
const rateStr = `${market.fundingRate >= 0 ? '+' : ''}${(market.fundingRate * 100).toFixed(4)}%`;
|
|
26
|
-
const colorFn = market.fundingRate >= 0 ? chalk.green : chalk.red;
|
|
27
|
-
lines.push(` ${chalk.gray('Funding Rate:')} ${colorFn(rateStr)}`);
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
lines.push(` ${chalk.gray('Funding Rate:')} ${chalk.dim('no perp data')}`);
|
|
31
|
-
}
|
|
32
|
-
if (market.openInterest !== null) {
|
|
33
|
-
lines.push(` ${chalk.gray('Open Interest:')} ${formatUsd(market.openInterest)}`);
|
|
34
|
-
}
|
|
35
|
-
if (market.openInterestChange24h !== null) {
|
|
36
|
-
lines.push(` ${chalk.gray('OI Change (24h):')} ${colorChange(market.openInterestChange24h)}`);
|
|
37
|
-
}
|
|
38
|
-
return lines.join('\n');
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Format the onchain section of a ContextPackage.
|
|
42
|
-
*/
|
|
43
|
-
export function formatOnchainSection(onchain) {
|
|
44
|
-
const lines = [];
|
|
45
|
-
lines.push(chalk.bold(' On-Chain'));
|
|
46
|
-
if (!onchain) {
|
|
47
|
-
lines.push(` ${chalk.dim('No on-chain data available.')}`);
|
|
48
|
-
return lines.join('\n');
|
|
49
|
-
}
|
|
50
|
-
if (onchain.mvrvZscore !== null) {
|
|
51
|
-
lines.push(` ${chalk.gray('MVRV Z-Score:')} ${chalk.white(onchain.mvrvZscore.toFixed(2))}`);
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
lines.push(` ${chalk.gray('MVRV Z-Score:')} ${chalk.dim('n/a')}`);
|
|
55
|
-
}
|
|
56
|
-
if (onchain.sopr !== null) {
|
|
57
|
-
lines.push(` ${chalk.gray('SOPR:')} ${chalk.white(onchain.sopr.toFixed(4))}`);
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
lines.push(` ${chalk.gray('SOPR:')} ${chalk.dim('n/a')}`);
|
|
61
|
-
}
|
|
62
|
-
const netflowStr = onchain.exchangeNetflow24h >= 0 ? `+${formatUsd(onchain.exchangeNetflow24h)}` : `-${formatUsd(Math.abs(onchain.exchangeNetflow24h))}`;
|
|
63
|
-
const netflowColor = onchain.exchangeNetflow24h >= 0 ? chalk.red : chalk.green;
|
|
64
|
-
lines.push(` ${chalk.gray('Exchange Netflow:')} ${netflowColor(netflowStr)}`);
|
|
65
|
-
lines.push(` ${chalk.gray('Whale Txns (24h):')} ${chalk.yellow(String(onchain.whaleTransactions24h))}`);
|
|
66
|
-
lines.push(` ${chalk.gray('Active Addr Trend:')} ${chalk.white(onchain.activeAddressesTrend)}`);
|
|
67
|
-
return lines.join('\n');
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Format the DeFi risk section of a ContextPackage.
|
|
71
|
-
*/
|
|
72
|
-
export function formatRiskSection(defiRisk) {
|
|
73
|
-
const lines = [];
|
|
74
|
-
lines.push(chalk.bold(' DeFi Risk'));
|
|
75
|
-
if (!defiRisk) {
|
|
76
|
-
lines.push(` ${chalk.dim('No DeFi risk data available.')}`);
|
|
77
|
-
return lines.join('\n');
|
|
78
|
-
}
|
|
79
|
-
lines.push(` ${chalk.gray('Risk Score:')} ${colorRiskScore(defiRisk.riskScore)}`);
|
|
80
|
-
lines.push(` ${chalk.gray('TVL Trend:')} ${chalk.white(defiRisk.protocolTvlTrend)}`);
|
|
81
|
-
if (defiRisk.dependencyChain.length > 0) {
|
|
82
|
-
lines.push(` ${chalk.gray('Dependencies:')}`);
|
|
83
|
-
for (const dep of defiRisk.dependencyChain) {
|
|
84
|
-
const crit = `${(dep.criticality * 100).toFixed(0)}%`;
|
|
85
|
-
lines.push(` ${chalk.dim('\u2022')} ${dep.name} ${chalk.dim(`(${dep.type})`)} criticality: ${crit}`);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
if (defiRisk.concentrationRisks.length > 0) {
|
|
89
|
-
lines.push(` ${chalk.gray('Concentration Risks:')}`);
|
|
90
|
-
for (const risk of defiRisk.concentrationRisks) {
|
|
91
|
-
lines.push(` ${chalk.red('\u26A0')} ${risk}`);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return lines.join('\n');
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Format the catalysts section of a ContextPackage.
|
|
98
|
-
*/
|
|
99
|
-
export function formatCatalystsSection(catalysts) {
|
|
100
|
-
const lines = [];
|
|
101
|
-
lines.push(chalk.bold(' Catalysts'));
|
|
102
|
-
if (!catalysts) {
|
|
103
|
-
lines.push(` ${chalk.dim('No catalyst data available.')}`);
|
|
104
|
-
return lines.join('\n');
|
|
105
|
-
}
|
|
106
|
-
// Upcoming events
|
|
107
|
-
if (catalysts.upcomingEvents.length > 0) {
|
|
108
|
-
lines.push(` ${chalk.gray('Upcoming Events:')}`);
|
|
109
|
-
for (const event of catalysts.upcomingEvents.slice(0, 5)) {
|
|
110
|
-
const date = event.scheduledDate
|
|
111
|
-
? new Date(event.scheduledDate).toISOString().slice(0, 10)
|
|
112
|
-
: 'TBD';
|
|
113
|
-
lines.push(` ${chalk.dim('\u2022')} ${chalk.white(date)} ${event.title ?? event.type} ${chalk.dim(`(${event.severity})`)}`);
|
|
114
|
-
}
|
|
115
|
-
if (catalysts.upcomingEvents.length > 5) {
|
|
116
|
-
lines.push(` ${chalk.dim(`... and ${catalysts.upcomingEvents.length - 5} more`)}`);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
lines.push(` ${chalk.gray('Upcoming Events:')} ${chalk.dim('none')}`);
|
|
121
|
-
}
|
|
122
|
-
// Unlock pressure
|
|
123
|
-
const unlock = catalysts.unlockPressure;
|
|
124
|
-
if (unlock.nextUnlockDate) {
|
|
125
|
-
const unlockDate = new Date(unlock.nextUnlockDate).toISOString().slice(0, 10);
|
|
126
|
-
const pctStr = unlock.nextUnlockPct !== null
|
|
127
|
-
? `${unlock.nextUnlockPct.toFixed(2)}%`
|
|
128
|
-
: 'unknown %';
|
|
129
|
-
lines.push(` ${chalk.gray('Next Unlock:')} ${unlockDate} (${pctStr}) ${chalk.dim(`[${unlock.materiality}]`)}`);
|
|
130
|
-
}
|
|
131
|
-
else {
|
|
132
|
-
lines.push(` ${chalk.gray('Next Unlock:')} ${chalk.dim('none scheduled')}`);
|
|
133
|
-
}
|
|
134
|
-
return lines.join('\n');
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Format the social section of a ContextPackage.
|
|
138
|
-
*/
|
|
139
|
-
export function formatSocialSection(social) {
|
|
140
|
-
const lines = [];
|
|
141
|
-
lines.push(chalk.bold(' Social'));
|
|
142
|
-
if (!social) {
|
|
143
|
-
lines.push(` ${chalk.dim('No social data available.')}`);
|
|
144
|
-
return lines.join('\n');
|
|
145
|
-
}
|
|
146
|
-
lines.push(` ${chalk.gray('Sentiment (24h):')} ${colorSentiment(social.sentiment24h)}`);
|
|
147
|
-
lines.push(` ${chalk.gray('Sentiment Trend:')} ${chalk.white(social.sentimentTrend)}`);
|
|
148
|
-
if (social.mindshareRank !== null) {
|
|
149
|
-
lines.push(` ${chalk.gray('Mindshare Rank:')} #${social.mindshareRank}`);
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
lines.push(` ${chalk.gray('Mindshare Rank:')} ${chalk.dim('n/a')}`);
|
|
153
|
-
}
|
|
154
|
-
if (social.narrativeMembership.length > 0) {
|
|
155
|
-
lines.push(` ${chalk.gray('Narratives:')}`);
|
|
156
|
-
for (const n of social.narrativeMembership) {
|
|
157
|
-
const conf = `${(n.membership * 100).toFixed(0)}%`;
|
|
158
|
-
lines.push(` ${chalk.dim('\u2022')} ${n.narrative} ${chalk.dim(`(${n.status})`)} confidence: ${conf}`);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
if (social.notableMentions.length > 0) {
|
|
162
|
-
lines.push(` ${chalk.gray('Notable Mentions:')}`);
|
|
163
|
-
for (const mention of social.notableMentions.slice(0, 3)) {
|
|
164
|
-
const truncated = mention.length > 60 ? mention.slice(0, 57) + '...' : mention;
|
|
165
|
-
lines.push(` ${chalk.dim('\u2022')} ${truncated}`);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return lines.join('\n');
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Format the synthesis section of a ContextPackage.
|
|
172
|
-
*/
|
|
173
|
-
export function formatSynthesisSection(synthesis) {
|
|
174
|
-
const lines = [];
|
|
175
|
-
lines.push(chalk.bold(' Synthesis'));
|
|
176
|
-
if (!synthesis) {
|
|
177
|
-
lines.push(` ${chalk.dim('No synthesis available.')}`);
|
|
178
|
-
return lines.join('\n');
|
|
179
|
-
}
|
|
180
|
-
lines.push(` ${chalk.gray('Regime:')} ${chalk.white.bold(synthesis.regime)}`);
|
|
181
|
-
if (synthesis.regimeSummary) {
|
|
182
|
-
lines.push(` ${chalk.gray('Regime Summary:')} ${chalk.white(synthesis.regimeSummary)}`);
|
|
183
|
-
}
|
|
184
|
-
lines.push(` ${chalk.gray('Signal Confluence:')} ${chalk.white(synthesis.signalConfluence)}`);
|
|
185
|
-
if (synthesis.outlook) {
|
|
186
|
-
lines.push(` ${chalk.gray('Outlook:')} ${chalk.white(synthesis.outlook)}`);
|
|
187
|
-
}
|
|
188
|
-
if (synthesis.confidenceAssessment) {
|
|
189
|
-
lines.push(` ${chalk.gray('Confidence:')} ${chalk.white(synthesis.confidenceAssessment)}`);
|
|
190
|
-
}
|
|
191
|
-
if (synthesis.detectedSignals.length > 0) {
|
|
192
|
-
lines.push(` ${chalk.gray('Detected Signals:')}`);
|
|
193
|
-
for (const signal of synthesis.detectedSignals) {
|
|
194
|
-
const confStr = `${(signal.confidence * 100).toFixed(0)}%`;
|
|
195
|
-
lines.push(` ${chalk.dim('\u2022')} ${chalk.white(signal.type)} ${chalk.dim(`(${confStr})`)} ${signal.description}`);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
if (synthesis.keyRisks.length > 0) {
|
|
199
|
-
lines.push(` ${chalk.gray('Key Risks:')}`);
|
|
200
|
-
for (const risk of synthesis.keyRisks) {
|
|
201
|
-
lines.push(` ${chalk.red('\u26A0')} ${risk}`);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
if (synthesis.keyOpportunities.length > 0) {
|
|
205
|
-
lines.push(` ${chalk.gray('Opportunities:')}`);
|
|
206
|
-
for (const opp of synthesis.keyOpportunities) {
|
|
207
|
-
lines.push(` ${chalk.green('\u2713')} ${opp}`);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
return lines.join('\n');
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Format the _meta section of a ContextPackage.
|
|
214
|
-
*/
|
|
215
|
-
export function formatMetaSection(meta) {
|
|
216
|
-
const lines = [];
|
|
217
|
-
lines.push(chalk.bold(' Meta'));
|
|
218
|
-
lines.push(` ${chalk.gray('Assembled At:')} ${chalk.dim(meta.assembledAt)}`);
|
|
219
|
-
lines.push(` ${chalk.gray('Cached:')} ${meta.cached ? chalk.green('yes') : chalk.dim('no')}`);
|
|
220
|
-
lines.push(` ${chalk.gray('Cache TTL:')} ${chalk.dim(`${meta.cacheTtlMs}ms`)}`);
|
|
221
|
-
if (meta.missingLayers.length > 0) {
|
|
222
|
-
lines.push(` ${chalk.gray('Missing Layers:')} ${chalk.yellow(meta.missingLayers.join(', '))}`);
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
lines.push(` ${chalk.gray('Missing Layers:')} ${chalk.green('none')}`);
|
|
226
|
-
}
|
|
227
|
-
if (Object.keys(meta.dataFreshness).length > 0) {
|
|
228
|
-
lines.push(` ${chalk.gray('Data Freshness:')}`);
|
|
229
|
-
for (const [layer, ts] of Object.entries(meta.dataFreshness)) {
|
|
230
|
-
const freshStr = ts ?? 'n/a';
|
|
231
|
-
lines.push(` ${chalk.dim(layer)}: ${chalk.dim(freshStr)}`);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
return lines.join('\n');
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* Format a full AssembledContextPackage for colored terminal output.
|
|
238
|
-
*/
|
|
239
|
-
export function formatContextOutput(pkg) {
|
|
240
|
-
const lines = [];
|
|
241
|
-
// Header
|
|
242
|
-
lines.push('');
|
|
243
|
-
lines.push(chalk.bold.cyan(` Context Package: ${pkg.token.symbol}`) + (pkg.token.name ? chalk.gray(` (${pkg.token.name})`) : ''));
|
|
244
|
-
lines.push(chalk.gray(' ' + '\u2500'.repeat(60)));
|
|
245
|
-
// Token summary
|
|
246
|
-
const chains = Array.isArray(pkg.token.chains) ? pkg.token.chains : [];
|
|
247
|
-
const tags = Array.isArray(pkg.token.narrativeTags) ? pkg.token.narrativeTags : [];
|
|
248
|
-
lines.push(` ${chalk.gray('Chains:')} ${chains.length > 0 ? chains.join(', ') : chalk.dim('none')}`);
|
|
249
|
-
lines.push(` ${chalk.gray('Narrative Tags:')} ${tags.length > 0 ? tags.join(', ') : chalk.dim('none')}`);
|
|
250
|
-
lines.push(` ${chalk.gray('Timestamp:')} ${chalk.dim(pkg.timestamp)}`);
|
|
251
|
-
lines.push('');
|
|
252
|
-
// Sections
|
|
253
|
-
lines.push(formatMarketSection(pkg.market));
|
|
254
|
-
lines.push('');
|
|
255
|
-
lines.push(formatOnchainSection(pkg.onchain));
|
|
256
|
-
lines.push('');
|
|
257
|
-
lines.push(formatRiskSection(pkg.defiRisk));
|
|
258
|
-
lines.push('');
|
|
259
|
-
lines.push(formatCatalystsSection(pkg.catalysts));
|
|
260
|
-
lines.push('');
|
|
261
|
-
lines.push(formatSocialSection(pkg.social));
|
|
262
|
-
lines.push('');
|
|
263
|
-
lines.push(formatSynthesisSection(pkg.synthesis));
|
|
264
|
-
lines.push('');
|
|
265
|
-
lines.push(formatMetaSection(pkg._meta));
|
|
266
|
-
lines.push('');
|
|
267
|
-
return lines.join('\n');
|
|
268
|
-
}
|
|
269
|
-
// ─── Historical Context Helpers ───
|
|
270
|
-
function formatHistoricalHeader(symbol, snapshotTime) {
|
|
271
|
-
return chalk.bold.cyan(` Historical Context: ${symbol}`) +
|
|
272
|
-
chalk.gray(` (snapshot: ${snapshotTime})`);
|
|
273
|
-
}
|
|
274
|
-
export function formatSnapshotRange(snapshots, symbol) {
|
|
275
|
-
const lines = [];
|
|
276
|
-
lines.push('');
|
|
277
|
-
lines.push(chalk.bold.cyan(` Context History: ${symbol}`));
|
|
278
|
-
lines.push(chalk.gray(' ' + '\u2500'.repeat(60)));
|
|
279
|
-
lines.push(` ${chalk.gray('Snapshots:')} ${snapshots.length}`);
|
|
280
|
-
lines.push('');
|
|
281
|
-
if (snapshots.length === 0) {
|
|
282
|
-
lines.push(` ${chalk.dim('No snapshots found in the requested range.')}`);
|
|
283
|
-
lines.push('');
|
|
284
|
-
return lines.join('\n');
|
|
285
|
-
}
|
|
286
|
-
lines.push(` ${chalk.gray('Time'.padEnd(26))} ${chalk.gray('Regime'.padEnd(16))} ${chalk.gray('Price'.padEnd(14))} ${chalk.gray('Sentiment')}`);
|
|
287
|
-
lines.push(chalk.gray(' ' + '\u2500'.repeat(60)));
|
|
288
|
-
for (const snap of snapshots) {
|
|
289
|
-
const time = snap.time.slice(0, 19).replace('T', ' ');
|
|
290
|
-
const regime = snap.regime ?? snap.context.synthesis?.regime ?? 'unknown';
|
|
291
|
-
const price = snap.context.market?.price != null ? formatUsd(snap.context.market.price) : 'n/a';
|
|
292
|
-
const sentiment = snap.context.social?.sentiment24h != null
|
|
293
|
-
? colorSentiment(snap.context.social.sentiment24h)
|
|
294
|
-
: chalk.dim('n/a');
|
|
295
|
-
lines.push(` ${chalk.white(time.padEnd(26))} ${chalk.white(regime.padEnd(16))} ${price.padEnd(14)} ${sentiment}`);
|
|
296
|
-
}
|
|
297
|
-
lines.push('');
|
|
298
|
-
return lines.join('\n');
|
|
299
|
-
}
|
|
300
|
-
function buildHistoryQueryParams(options) {
|
|
301
|
-
const params = new URLSearchParams();
|
|
302
|
-
if (options.at)
|
|
303
|
-
params.set('timestamp', new Date(options.at).toISOString());
|
|
304
|
-
if (options.from)
|
|
305
|
-
params.set('from', new Date(options.from).toISOString());
|
|
306
|
-
if (options.to)
|
|
307
|
-
params.set('to', new Date(options.to).toISOString());
|
|
308
|
-
if (options.interval)
|
|
309
|
-
params.set('interval', options.interval);
|
|
310
|
-
if (options.traderId)
|
|
311
|
-
params.set('traderId', options.traderId);
|
|
312
|
-
return params.toString();
|
|
313
|
-
}
|
|
314
|
-
// ─── Command Registration ───
|
|
315
|
-
export function registerContextCommand(program) {
|
|
316
|
-
program
|
|
317
|
-
.command('context <symbol>')
|
|
318
|
-
.description('Assemble and display a full ContextPackage for a token')
|
|
319
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
320
|
-
.option('--at <date>', 'Fetch historical context at a specific date/time (e.g., 2026-01-15 or 2026-01-15T12:00:00Z)')
|
|
321
|
-
.option('--from <date>', 'Start of time range for context history')
|
|
322
|
-
.option('--to <date>', 'End of time range for context history')
|
|
323
|
-
.addOption(new Option('--interval <interval>', 'Sampling interval for range queries').choices(['15m', '1h', '4h', '1d']))
|
|
324
|
-
.option('--trader-id <id>', 'Trader ID for personalized context')
|
|
325
|
-
.action(async (symbol, options) => {
|
|
326
|
-
try {
|
|
327
|
-
// ─── Auth pre-flight ───
|
|
328
|
-
if (!(await ensureRemote()))
|
|
329
|
-
return;
|
|
330
|
-
// ─── Validate range options ───
|
|
331
|
-
if ((options.from && !options.to) || (!options.from && options.to)) {
|
|
332
|
-
console.error(chalk.red('Error: Both --from and --to must be provided for range queries.'));
|
|
333
|
-
process.exitCode = 1;
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
if (options.at && (options.from || options.to)) {
|
|
337
|
-
console.error(chalk.red('Error: Cannot use --at with --from/--to. Use either point-in-time or range query.'));
|
|
338
|
-
process.exitCode = 1;
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
if (options.interval && !options.from) {
|
|
342
|
-
console.error(chalk.red('Error: --interval requires --from and --to.'));
|
|
343
|
-
process.exitCode = 1;
|
|
344
|
-
return;
|
|
345
|
-
}
|
|
346
|
-
if (options.at) {
|
|
347
|
-
// Point-in-time historical query
|
|
348
|
-
const qs = buildHistoryQueryParams(options);
|
|
349
|
-
const data = await apiRequest(`/api/v1/tokens/${encodeURIComponent(symbol.toUpperCase())}/context/at?${qs}`);
|
|
350
|
-
if (options.format === 'json') {
|
|
351
|
-
console.log(JSON.stringify(data, null, 2));
|
|
352
|
-
}
|
|
353
|
-
else {
|
|
354
|
-
console.log('');
|
|
355
|
-
console.log(formatHistoricalHeader(data.symbol, data.snapshotTime));
|
|
356
|
-
console.log(formatContextOutput(data.context));
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
else if (options.from && options.to) {
|
|
360
|
-
// Range query
|
|
361
|
-
const qs = buildHistoryQueryParams(options);
|
|
362
|
-
const data = await apiRequest(`/api/v1/tokens/${encodeURIComponent(symbol.toUpperCase())}/context/history?${qs}`);
|
|
363
|
-
if (options.format === 'json') {
|
|
364
|
-
console.log(JSON.stringify(data, null, 2));
|
|
365
|
-
}
|
|
366
|
-
else {
|
|
367
|
-
console.log(formatSnapshotRange(data.snapshots, data.symbol));
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
else {
|
|
371
|
-
// Live context
|
|
372
|
-
const traderQs = options.traderId ? `?traderId=${encodeURIComponent(options.traderId)}` : '';
|
|
373
|
-
const pkg = await apiRequest(`/api/v1/tokens/${encodeURIComponent(symbol.toUpperCase())}/context${traderQs}`);
|
|
374
|
-
// Check if the token was actually found
|
|
375
|
-
if (pkg.token.name == null && (!Array.isArray(pkg.token.chains) || pkg.token.chains.length === 0) && pkg.market?.price == null) {
|
|
376
|
-
console.error(chalk.red(`Error: Token not found: ${symbol.toUpperCase()}`));
|
|
377
|
-
console.error(chalk.dim(' Check the symbol and try again. Use a symbol like SOL, JUP, or BONK.'));
|
|
378
|
-
process.exitCode = 1;
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
if (options.format === 'json') {
|
|
382
|
-
console.log(JSON.stringify(pkg, null, 2));
|
|
383
|
-
}
|
|
384
|
-
else {
|
|
385
|
-
console.log(formatContextOutput(pkg));
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
catch (error) {
|
|
390
|
-
if (error instanceof ApiError && error.status === 422) {
|
|
391
|
-
console.error(chalk.red('Error: This feature requires a subscription. Run `trading-boy subscribe` to get started.'));
|
|
392
|
-
process.exitCode = 2;
|
|
393
|
-
return;
|
|
394
|
-
}
|
|
395
|
-
const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
|
|
396
|
-
logger.error({ error: message }, 'Context assembly failed');
|
|
397
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
398
|
-
const guidance = formatConnectionError(message);
|
|
399
|
-
if (guidance)
|
|
400
|
-
console.error(guidance);
|
|
401
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
402
|
-
}
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
//# sourceMappingURL=context.js.map
|