pnpfucius 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.
- package/README.md +396 -0
- package/bin/claude-predict.js +5 -0
- package/bin/pnpfucius.js +8 -0
- package/package.json +71 -0
- package/src/agent.js +1037 -0
- package/src/ai/index.js +6 -0
- package/src/ai/market-generator.js +186 -0
- package/src/ai/resolver.js +172 -0
- package/src/ai/scorer.js +184 -0
- package/src/analytics/aggregator.js +198 -0
- package/src/cli.js +948 -0
- package/src/collateral/privacy-tokens.js +183 -0
- package/src/config.js +128 -0
- package/src/daemon/index.js +321 -0
- package/src/daemon/lifecycle.js +168 -0
- package/src/daemon/scheduler.js +252 -0
- package/src/events/emitter.js +147 -0
- package/src/helius/client.js +221 -0
- package/src/helius/transaction-tracker.js +192 -0
- package/src/helius/webhooks.js +233 -0
- package/src/index.js +139 -0
- package/src/monitoring/news-monitor.js +262 -0
- package/src/monitoring/news-scorer.js +236 -0
- package/src/predict/agent.js +291 -0
- package/src/predict/prompts.js +69 -0
- package/src/predict/slash-commands.js +361 -0
- package/src/predict/tools/analytics-tools.js +83 -0
- package/src/predict/tools/bash-tool.js +87 -0
- package/src/predict/tools/file-tools.js +140 -0
- package/src/predict/tools/index.js +120 -0
- package/src/predict/tools/market-tools.js +851 -0
- package/src/predict/tools/news-tools.js +130 -0
- package/src/predict/ui/renderer.js +215 -0
- package/src/predict/ui/welcome.js +146 -0
- package/src/privacy-markets.js +194 -0
- package/src/storage/market-store.js +418 -0
- package/src/utils/spinner.js +172 -0
package/src/cli.js
ADDED
|
@@ -0,0 +1,948 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import inquirer from 'inquirer';
|
|
6
|
+
import { PrivacyOracleAgent } from './agent.js';
|
|
7
|
+
import { generatePrivacyMarket, generateMultipleMarkets, listCategories, getMarketsByCategory, PRIVACY_CATEGORIES } from './privacy-markets.js';
|
|
8
|
+
import { getConfig, validateConfig } from './config.js';
|
|
9
|
+
import { PrivacyOracleDaemon } from './daemon/index.js';
|
|
10
|
+
import { createMarketStore } from './storage/market-store.js';
|
|
11
|
+
import { createAggregator, formatNumber, formatDuration } from './analytics/aggregator.js';
|
|
12
|
+
import { withSpinner, StepProgress, successLine, errorLine, infoLine } from './utils/spinner.js';
|
|
13
|
+
import { listSupportedTokens, checkConfidentialTransferSupport } from './collateral/privacy-tokens.js';
|
|
14
|
+
import { Connection } from '@solana/web3.js';
|
|
15
|
+
import { AIMarketGenerator } from './ai/market-generator.js';
|
|
16
|
+
import { AIScorer } from './ai/scorer.js';
|
|
17
|
+
import { AIResolver } from './ai/resolver.js';
|
|
18
|
+
|
|
19
|
+
const program = new Command();
|
|
20
|
+
|
|
21
|
+
program
|
|
22
|
+
.name('privacy-oracle')
|
|
23
|
+
.description('AI agent for creating privacy-themed prediction markets on Solana')
|
|
24
|
+
.version('1.1.0');
|
|
25
|
+
|
|
26
|
+
program
|
|
27
|
+
.command('create')
|
|
28
|
+
.description('Create a new privacy-themed prediction market')
|
|
29
|
+
.option('-q, --question <question>', 'Custom market question')
|
|
30
|
+
.option('-d, --days <days>', 'Duration in days', '30')
|
|
31
|
+
.option('-l, --liquidity <amount>', 'Initial liquidity in base units', '1000000')
|
|
32
|
+
.option('--p2p', 'Create as P2P market instead of AMM')
|
|
33
|
+
.option('--side <side>', 'Side for P2P market (yes/no)', 'yes')
|
|
34
|
+
.option('-v, --verbose', 'Verbose output')
|
|
35
|
+
.action(async (options) => {
|
|
36
|
+
try {
|
|
37
|
+
const config = getConfig();
|
|
38
|
+
const validation = validateConfig(config);
|
|
39
|
+
|
|
40
|
+
if (!validation.valid) {
|
|
41
|
+
validation.errors.forEach(e => errorLine(e));
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
validation.warnings.forEach(w => console.log(chalk.yellow('Warning:'), w));
|
|
46
|
+
|
|
47
|
+
const agent = new PrivacyOracleAgent({ verbose: options.verbose });
|
|
48
|
+
|
|
49
|
+
const steps = new StepProgress([
|
|
50
|
+
'Generating market question',
|
|
51
|
+
'Building transaction',
|
|
52
|
+
'Submitting to Solana',
|
|
53
|
+
'Confirming transaction'
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
steps.start();
|
|
57
|
+
|
|
58
|
+
let result;
|
|
59
|
+
if (options.p2p) {
|
|
60
|
+
steps.next('Creating P2P market');
|
|
61
|
+
result = await agent.createP2PMarket({
|
|
62
|
+
question: options.question,
|
|
63
|
+
durationDays: parseInt(options.days, 10),
|
|
64
|
+
amount: BigInt(options.liquidity),
|
|
65
|
+
side: options.side
|
|
66
|
+
});
|
|
67
|
+
} else {
|
|
68
|
+
result = await agent.createPrivacyMarket({
|
|
69
|
+
question: options.question,
|
|
70
|
+
durationDays: parseInt(options.days, 10),
|
|
71
|
+
liquidity: BigInt(options.liquidity)
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
steps.succeed('Market created successfully!');
|
|
76
|
+
|
|
77
|
+
console.log();
|
|
78
|
+
successLine(`Question: ${result.question}`);
|
|
79
|
+
infoLine(`Market: ${result.market}`);
|
|
80
|
+
infoLine(`Signature: ${result.signature}`);
|
|
81
|
+
infoLine(`Duration: ${result.durationDays} days`);
|
|
82
|
+
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.error(chalk.red('\nError:'), error.message);
|
|
85
|
+
if (error.logs) {
|
|
86
|
+
console.error(chalk.gray('Transaction logs:'), error.logs.join('\n'));
|
|
87
|
+
}
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
program
|
|
93
|
+
.command('batch')
|
|
94
|
+
.description('Create multiple privacy-themed markets')
|
|
95
|
+
.option('-c, --count <count>', 'Number of markets to create', '3')
|
|
96
|
+
.option('-v, --verbose', 'Verbose output')
|
|
97
|
+
.action(async (options) => {
|
|
98
|
+
try {
|
|
99
|
+
const config = getConfig();
|
|
100
|
+
const validation = validateConfig(config);
|
|
101
|
+
|
|
102
|
+
if (!validation.valid) {
|
|
103
|
+
validation.errors.forEach(e => errorLine(e));
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const agent = new PrivacyOracleAgent({ verbose: options.verbose });
|
|
108
|
+
const count = parseInt(options.count, 10);
|
|
109
|
+
|
|
110
|
+
console.log(chalk.cyan(`\nCreating ${count} privacy-themed markets...\n`));
|
|
111
|
+
|
|
112
|
+
const results = await withSpinner(
|
|
113
|
+
`Creating ${count} markets`,
|
|
114
|
+
async (spinner) => {
|
|
115
|
+
const results = [];
|
|
116
|
+
for (let i = 0; i < count; i++) {
|
|
117
|
+
spinner.text = `Creating market ${i + 1}/${count}...`;
|
|
118
|
+
try {
|
|
119
|
+
const result = await agent.createPrivacyMarket({});
|
|
120
|
+
results.push({ ...result, success: true });
|
|
121
|
+
} catch (error) {
|
|
122
|
+
results.push({ success: false, error: error.message });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return results;
|
|
126
|
+
},
|
|
127
|
+
{ successText: `Created ${count} markets` }
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
console.log();
|
|
131
|
+
let successCount = 0;
|
|
132
|
+
for (const result of results) {
|
|
133
|
+
if (result.success) {
|
|
134
|
+
successCount++;
|
|
135
|
+
successLine(result.question);
|
|
136
|
+
console.log(chalk.gray(` Market: ${result.market}`));
|
|
137
|
+
} else {
|
|
138
|
+
errorLine(result.error || 'Unknown error');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
console.log(chalk.cyan(`\nCompleted: ${successCount}/${count} markets created successfully`));
|
|
143
|
+
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error(chalk.red('Error:'), error.message);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
program
|
|
151
|
+
.command('generate')
|
|
152
|
+
.description('Generate market ideas without creating them')
|
|
153
|
+
.option('-c, --count <count>', 'Number of ideas to generate', '5')
|
|
154
|
+
.option('-k, --category <category>', 'Filter by category (regulation, technology, adoption, events)')
|
|
155
|
+
.action((options) => {
|
|
156
|
+
const count = parseInt(options.count, 10);
|
|
157
|
+
|
|
158
|
+
let ideas;
|
|
159
|
+
if (options.category) {
|
|
160
|
+
if (!PRIVACY_CATEGORIES[options.category]) {
|
|
161
|
+
errorLine(`Unknown category: ${options.category}`);
|
|
162
|
+
console.log(chalk.gray('Available: regulation, technology, adoption, events'));
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
ideas = getMarketsByCategory(options.category).slice(0, count);
|
|
166
|
+
} else {
|
|
167
|
+
ideas = generateMultipleMarkets(count);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
console.log(chalk.cyan(`\nGenerated ${ideas.length} privacy market ideas:\n`));
|
|
171
|
+
|
|
172
|
+
ideas.forEach((idea, i) => {
|
|
173
|
+
const cat = PRIVACY_CATEGORIES[idea.categoryKey];
|
|
174
|
+
const urgencyColor = cat?.urgency === 'breaking' ? chalk.red :
|
|
175
|
+
cat?.urgency === 'timely' ? chalk.yellow : chalk.gray;
|
|
176
|
+
const sentimentIcon = cat?.sentiment === 'bullish' ? '↑' :
|
|
177
|
+
cat?.sentiment === 'bearish' ? '↓' : '→';
|
|
178
|
+
|
|
179
|
+
console.log(chalk.yellow(`${i + 1}.`), idea.question);
|
|
180
|
+
console.log(chalk.gray(` Category: ${idea.category}`));
|
|
181
|
+
console.log(chalk.gray(` Duration: ${idea.durationDays || 30} days | `),
|
|
182
|
+
urgencyColor(`Urgency: ${cat?.urgency || 'evergreen'}`),
|
|
183
|
+
chalk.gray(` | Sentiment: ${sentimentIcon} ${cat?.sentiment || 'neutral'}`));
|
|
184
|
+
console.log();
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
program
|
|
189
|
+
.command('categories')
|
|
190
|
+
.description('List available market categories')
|
|
191
|
+
.action(() => {
|
|
192
|
+
const cats = listCategories();
|
|
193
|
+
|
|
194
|
+
console.log(chalk.cyan('\nPrivacy Market Categories:\n'));
|
|
195
|
+
|
|
196
|
+
cats.forEach(cat => {
|
|
197
|
+
const category = PRIVACY_CATEGORIES[cat.key];
|
|
198
|
+
const percentage = (cat.weight * 100).toFixed(0);
|
|
199
|
+
const urgencyColor = category?.urgency === 'breaking' ? chalk.red :
|
|
200
|
+
category?.urgency === 'timely' ? chalk.yellow : chalk.gray;
|
|
201
|
+
|
|
202
|
+
console.log(chalk.yellow(cat.name), chalk.gray(`(${percentage}% weight)`));
|
|
203
|
+
console.log(chalk.gray(` Templates: ${cat.templateCount}`),
|
|
204
|
+
chalk.gray(' | '),
|
|
205
|
+
urgencyColor(`Urgency: ${category?.urgency || 'evergreen'}`),
|
|
206
|
+
chalk.gray(' | '),
|
|
207
|
+
chalk.gray(`Sentiment: ${category?.sentiment || 'neutral'}`));
|
|
208
|
+
console.log();
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
program
|
|
213
|
+
.command('list')
|
|
214
|
+
.description('List existing markets')
|
|
215
|
+
.option('-n, --limit <limit>', 'Maximum markets to show', '20')
|
|
216
|
+
.option('-v, --verbose', 'Verbose output')
|
|
217
|
+
.action(async (options) => {
|
|
218
|
+
try {
|
|
219
|
+
const agent = new PrivacyOracleAgent({ verbose: options.verbose });
|
|
220
|
+
const limit = parseInt(options.limit, 10);
|
|
221
|
+
|
|
222
|
+
const addresses = await withSpinner(
|
|
223
|
+
'Fetching market addresses',
|
|
224
|
+
() => agent.getMarketAddresses(),
|
|
225
|
+
{ successText: 'Markets fetched' }
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
const showing = addresses.slice(0, limit);
|
|
229
|
+
console.log(chalk.cyan(`\nFound ${addresses.length} markets. Showing first ${showing.length}:\n`));
|
|
230
|
+
|
|
231
|
+
for (const addr of showing) {
|
|
232
|
+
try {
|
|
233
|
+
const info = await agent.fetchMarketInfo(addr);
|
|
234
|
+
const status = info.resolved ? chalk.green('RESOLVED') :
|
|
235
|
+
info.resolvable ? chalk.yellow('ACTIVE') : chalk.red('NOT RESOLVABLE');
|
|
236
|
+
|
|
237
|
+
console.log(chalk.white(info.question));
|
|
238
|
+
console.log(chalk.gray(` ${addr}`));
|
|
239
|
+
console.log(chalk.gray(` Status: ${status} | Ends: ${info.endTime.toLocaleDateString()}`));
|
|
240
|
+
console.log();
|
|
241
|
+
} catch (e) {
|
|
242
|
+
console.log(chalk.gray(` ${addr} - Error fetching details`));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
} catch (error) {
|
|
247
|
+
console.error(chalk.red('Error:'), error.message);
|
|
248
|
+
process.exit(1);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
program
|
|
253
|
+
.command('info <market>')
|
|
254
|
+
.description('Get detailed info about a specific market')
|
|
255
|
+
.option('-v, --verbose', 'Verbose output')
|
|
256
|
+
.action(async (market, options) => {
|
|
257
|
+
try {
|
|
258
|
+
const agent = new PrivacyOracleAgent({ verbose: options.verbose });
|
|
259
|
+
const info = await agent.fetchMarketInfo(market);
|
|
260
|
+
|
|
261
|
+
console.log(chalk.cyan('\nMarket Information:\n'));
|
|
262
|
+
console.log(chalk.yellow('Question:'), info.question);
|
|
263
|
+
console.log(chalk.yellow('Address:'), info.address);
|
|
264
|
+
console.log(chalk.yellow('Creator:'), info.creator);
|
|
265
|
+
console.log(chalk.yellow('Status:'), info.resolved ? 'Resolved' : (info.resolvable ? 'Active' : 'Not Resolvable'));
|
|
266
|
+
console.log(chalk.yellow('End Time:'), info.endTime.toLocaleString());
|
|
267
|
+
|
|
268
|
+
if (info.winningToken) {
|
|
269
|
+
console.log(chalk.yellow('Winner:'), info.winningToken);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
} catch (error) {
|
|
273
|
+
console.error(chalk.red('Error:'), error.message);
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
program
|
|
279
|
+
.command('config')
|
|
280
|
+
.description('Show current configuration')
|
|
281
|
+
.action(() => {
|
|
282
|
+
const config = getConfig();
|
|
283
|
+
const validation = validateConfig(config);
|
|
284
|
+
|
|
285
|
+
console.log(chalk.cyan('\nCurrent Configuration:\n'));
|
|
286
|
+
|
|
287
|
+
// Network
|
|
288
|
+
console.log(chalk.yellow('Network'));
|
|
289
|
+
infoLine(`Network: ${config.network}`);
|
|
290
|
+
infoLine(`RPC: ${config.heliusKey ? 'Helius (configured)' : chalk.red('Public RPC (rate limited)')}`);
|
|
291
|
+
infoLine(`Wallet: ${config.walletKey ? chalk.green('Configured') : chalk.red('Not configured')}`);
|
|
292
|
+
console.log();
|
|
293
|
+
|
|
294
|
+
// Collateral
|
|
295
|
+
console.log(chalk.yellow('Collateral'));
|
|
296
|
+
infoLine(`Token: ${config.collateralToken}`);
|
|
297
|
+
infoLine(`Mint: ${config.collateralMint.toBase58()}`);
|
|
298
|
+
infoLine(`Prefer Confidential: ${config.preferConfidential ? 'Yes' : 'No'}`);
|
|
299
|
+
console.log();
|
|
300
|
+
|
|
301
|
+
// Markets
|
|
302
|
+
console.log(chalk.yellow('Market Defaults'));
|
|
303
|
+
infoLine(`Liquidity: ${config.defaultLiquidity.toString()}`);
|
|
304
|
+
infoLine(`Duration: ${config.defaultDurationDays} days`);
|
|
305
|
+
console.log();
|
|
306
|
+
|
|
307
|
+
// Daemon
|
|
308
|
+
console.log(chalk.yellow('Daemon Settings'));
|
|
309
|
+
infoLine(`Schedule: ${config.daemon.schedule}`);
|
|
310
|
+
infoLine(`Markets per round: ${config.daemon.marketsPerRound}`);
|
|
311
|
+
infoLine(`Storage: ${config.daemon.storagePath || 'In-memory'}`);
|
|
312
|
+
console.log();
|
|
313
|
+
|
|
314
|
+
// Optional features
|
|
315
|
+
console.log(chalk.yellow('Optional Features'));
|
|
316
|
+
infoLine(`News monitoring: ${config.news.enabled ? chalk.green('Enabled') : 'Disabled'}`);
|
|
317
|
+
infoLine(`Webhook server: ${config.webhook.enabled ? chalk.green('Enabled') : 'Disabled'}`);
|
|
318
|
+
console.log();
|
|
319
|
+
|
|
320
|
+
// Validation
|
|
321
|
+
if (validation.warnings.length > 0) {
|
|
322
|
+
console.log(chalk.yellow('Warnings:'));
|
|
323
|
+
validation.warnings.forEach(w => console.log(chalk.yellow(` ! ${w}`)));
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (!validation.valid) {
|
|
327
|
+
console.log(chalk.red('\nErrors:'));
|
|
328
|
+
validation.errors.forEach(e => errorLine(e));
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// Daemon command
|
|
333
|
+
program
|
|
334
|
+
.command('daemon')
|
|
335
|
+
.description('Run as autonomous daemon, creating markets on a schedule')
|
|
336
|
+
.option('-s, --schedule <schedule>', 'Cron expression or interval (30m, 1h, 24h)', '1h')
|
|
337
|
+
.option('-n, --iterations <count>', 'Max iterations (infinite if omitted)')
|
|
338
|
+
.option('-c, --count <count>', 'Markets per cycle', '1')
|
|
339
|
+
.option('--dry-run', 'Generate markets without creating on-chain')
|
|
340
|
+
.option('--news', 'Enable news monitoring for timely markets')
|
|
341
|
+
.option('--webhooks', 'Enable webhook server for Helius events')
|
|
342
|
+
.option('--webhook-port <port>', 'Webhook server port', '3000')
|
|
343
|
+
.option('-v, --verbose', 'Verbose output')
|
|
344
|
+
.action(async (options) => {
|
|
345
|
+
try {
|
|
346
|
+
const config = getConfig();
|
|
347
|
+
const validation = validateConfig(config);
|
|
348
|
+
|
|
349
|
+
if (!validation.valid && !options.dryRun) {
|
|
350
|
+
validation.errors.forEach(e => errorLine(e));
|
|
351
|
+
process.exit(1);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
console.log(chalk.cyan('\n=== Privacy Oracle Daemon ===\n'));
|
|
355
|
+
|
|
356
|
+
const daemonConfig = {
|
|
357
|
+
...config,
|
|
358
|
+
daemon: {
|
|
359
|
+
...config.daemon,
|
|
360
|
+
schedule: options.schedule,
|
|
361
|
+
marketsPerRound: parseInt(options.count, 10),
|
|
362
|
+
maxIterations: options.iterations ? parseInt(options.iterations, 10) : undefined
|
|
363
|
+
},
|
|
364
|
+
news: {
|
|
365
|
+
...config.news,
|
|
366
|
+
enabled: options.news || config.news.enabled
|
|
367
|
+
},
|
|
368
|
+
webhook: {
|
|
369
|
+
...config.webhook,
|
|
370
|
+
enabled: options.webhooks || config.webhook.enabled,
|
|
371
|
+
port: parseInt(options.webhookPort, 10)
|
|
372
|
+
},
|
|
373
|
+
dryRun: options.dryRun,
|
|
374
|
+
verbose: options.verbose
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
infoLine(`Schedule: ${daemonConfig.daemon.schedule}`);
|
|
378
|
+
infoLine(`Markets per cycle: ${daemonConfig.daemon.marketsPerRound}`);
|
|
379
|
+
infoLine(`Max iterations: ${daemonConfig.daemon.maxIterations || 'Infinite'}`);
|
|
380
|
+
infoLine(`Dry run: ${daemonConfig.dryRun ? 'Yes' : 'No'}`);
|
|
381
|
+
infoLine(`News monitoring: ${daemonConfig.news.enabled ? 'Enabled' : 'Disabled'}`);
|
|
382
|
+
infoLine(`Webhooks: ${daemonConfig.webhook.enabled ? `Enabled (port ${daemonConfig.webhook.port})` : 'Disabled'}`);
|
|
383
|
+
console.log();
|
|
384
|
+
|
|
385
|
+
const daemon = new PrivacyOracleDaemon(daemonConfig);
|
|
386
|
+
|
|
387
|
+
await daemon.start();
|
|
388
|
+
|
|
389
|
+
console.log(chalk.green('\nDaemon started. Press Ctrl+C to stop.\n'));
|
|
390
|
+
|
|
391
|
+
} catch (error) {
|
|
392
|
+
console.error(chalk.red('Error:'), error.message);
|
|
393
|
+
process.exit(1);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
// Stats command
|
|
398
|
+
program
|
|
399
|
+
.command('stats')
|
|
400
|
+
.description('Show market analytics and statistics')
|
|
401
|
+
.option('--period <period>', 'Time period (24h, 7d, 30d)', '7d')
|
|
402
|
+
.action(async (options) => {
|
|
403
|
+
try {
|
|
404
|
+
const config = getConfig();
|
|
405
|
+
const storagePath = config.daemon.storagePath || ':memory:';
|
|
406
|
+
|
|
407
|
+
const store = createMarketStore(storagePath);
|
|
408
|
+
const aggregator = createAggregator(store);
|
|
409
|
+
|
|
410
|
+
const overview = await withSpinner(
|
|
411
|
+
'Loading analytics',
|
|
412
|
+
() => aggregator.getOverview(),
|
|
413
|
+
{ successText: 'Analytics loaded' }
|
|
414
|
+
);
|
|
415
|
+
|
|
416
|
+
console.log(chalk.cyan('\n=== Market Analytics ===\n'));
|
|
417
|
+
|
|
418
|
+
// Summary
|
|
419
|
+
console.log(chalk.yellow('Summary'));
|
|
420
|
+
infoLine(`Total markets: ${overview.summary.totalMarkets}`);
|
|
421
|
+
infoLine(`Active: ${overview.summary.activeMarkets}`);
|
|
422
|
+
infoLine(`Resolved: ${overview.summary.resolvedMarkets}`);
|
|
423
|
+
infoLine(`Cancelled: ${overview.summary.cancelledMarkets}`);
|
|
424
|
+
infoLine(`Created this week: ${overview.summary.recentWeek}`);
|
|
425
|
+
console.log();
|
|
426
|
+
|
|
427
|
+
// Performance
|
|
428
|
+
console.log(chalk.yellow('Performance'));
|
|
429
|
+
infoLine(`Total volume: ${formatNumber(overview.performance.totalVolume || 0)}`);
|
|
430
|
+
infoLine(`Avg duration: ${formatDuration(overview.performance.averageDuration || 0)}`);
|
|
431
|
+
infoLine(`Resolution rate: ${(overview.performance.resolutionRate * 100 || 0).toFixed(1)}%`);
|
|
432
|
+
console.log();
|
|
433
|
+
|
|
434
|
+
// Category breakdown
|
|
435
|
+
if (overview.categoryBreakdown.length > 0) {
|
|
436
|
+
console.log(chalk.yellow('By Category'));
|
|
437
|
+
overview.categoryBreakdown.forEach(cat => {
|
|
438
|
+
const bar = '█'.repeat(Math.ceil(cat.percentage / 5));
|
|
439
|
+
console.log(` ${cat.category}: ${cat.count} (${cat.percentage}%) ${chalk.cyan(bar)}`);
|
|
440
|
+
});
|
|
441
|
+
console.log();
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Recent markets
|
|
445
|
+
if (overview.recentMarkets.length > 0) {
|
|
446
|
+
console.log(chalk.yellow('Recent Markets'));
|
|
447
|
+
overview.recentMarkets.slice(0, 5).forEach(m => {
|
|
448
|
+
const statusColor = m.status === 'active' ? chalk.green :
|
|
449
|
+
m.status === 'resolved' ? chalk.blue : chalk.gray;
|
|
450
|
+
console.log(` ${statusColor('●')} ${m.question.slice(0, 60)}...`);
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
store.close();
|
|
455
|
+
|
|
456
|
+
} catch (error) {
|
|
457
|
+
console.error(chalk.red('Error:'), error.message);
|
|
458
|
+
process.exit(1);
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
// Interactive mode
|
|
463
|
+
program
|
|
464
|
+
.command('interactive')
|
|
465
|
+
.alias('i')
|
|
466
|
+
.description('Interactive market creation wizard')
|
|
467
|
+
.action(async () => {
|
|
468
|
+
try {
|
|
469
|
+
const config = getConfig();
|
|
470
|
+
const validation = validateConfig(config);
|
|
471
|
+
|
|
472
|
+
console.log(chalk.cyan('\n=== Privacy Oracle Interactive Mode ===\n'));
|
|
473
|
+
|
|
474
|
+
if (!validation.valid) {
|
|
475
|
+
validation.errors.forEach(e => errorLine(e));
|
|
476
|
+
console.log(chalk.yellow('\nNote: Wallet not configured. Running in preview mode.\n'));
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Category selection
|
|
480
|
+
const categories = listCategories();
|
|
481
|
+
const { category } = await inquirer.prompt([{
|
|
482
|
+
type: 'list',
|
|
483
|
+
name: 'category',
|
|
484
|
+
message: 'Select market category:',
|
|
485
|
+
choices: [
|
|
486
|
+
{ name: 'Random (AI picks)', value: 'random' },
|
|
487
|
+
...categories.map(c => ({
|
|
488
|
+
name: `${c.name} (${c.templateCount} templates)`,
|
|
489
|
+
value: c.key
|
|
490
|
+
}))
|
|
491
|
+
]
|
|
492
|
+
}]);
|
|
493
|
+
|
|
494
|
+
// Generate question
|
|
495
|
+
let market;
|
|
496
|
+
if (category === 'random') {
|
|
497
|
+
market = generatePrivacyMarket();
|
|
498
|
+
} else {
|
|
499
|
+
const categoryMarkets = getMarketsByCategory(category);
|
|
500
|
+
market = categoryMarkets[Math.floor(Math.random() * categoryMarkets.length)];
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
console.log(chalk.cyan('\nGenerated question:'));
|
|
504
|
+
console.log(chalk.white(` "${market.question}"\n`));
|
|
505
|
+
|
|
506
|
+
// Customize?
|
|
507
|
+
const { customize } = await inquirer.prompt([{
|
|
508
|
+
type: 'confirm',
|
|
509
|
+
name: 'customize',
|
|
510
|
+
message: 'Customize this question?',
|
|
511
|
+
default: false
|
|
512
|
+
}]);
|
|
513
|
+
|
|
514
|
+
let finalQuestion = market.question;
|
|
515
|
+
if (customize) {
|
|
516
|
+
const { customQuestion } = await inquirer.prompt([{
|
|
517
|
+
type: 'input',
|
|
518
|
+
name: 'customQuestion',
|
|
519
|
+
message: 'Enter custom question:',
|
|
520
|
+
default: market.question
|
|
521
|
+
}]);
|
|
522
|
+
finalQuestion = customQuestion;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Duration
|
|
526
|
+
const { duration } = await inquirer.prompt([{
|
|
527
|
+
type: 'list',
|
|
528
|
+
name: 'duration',
|
|
529
|
+
message: 'Market duration:',
|
|
530
|
+
choices: [
|
|
531
|
+
{ name: '14 days', value: 14 },
|
|
532
|
+
{ name: '30 days (recommended)', value: 30 },
|
|
533
|
+
{ name: '60 days', value: 60 },
|
|
534
|
+
{ name: '90 days', value: 90 },
|
|
535
|
+
{ name: 'Custom', value: 'custom' }
|
|
536
|
+
],
|
|
537
|
+
default: 1
|
|
538
|
+
}]);
|
|
539
|
+
|
|
540
|
+
let finalDuration = duration;
|
|
541
|
+
if (duration === 'custom') {
|
|
542
|
+
const { customDuration } = await inquirer.prompt([{
|
|
543
|
+
type: 'number',
|
|
544
|
+
name: 'customDuration',
|
|
545
|
+
message: 'Enter duration in days:',
|
|
546
|
+
default: 30
|
|
547
|
+
}]);
|
|
548
|
+
finalDuration = customDuration;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Liquidity
|
|
552
|
+
const { liquidity } = await inquirer.prompt([{
|
|
553
|
+
type: 'list',
|
|
554
|
+
name: 'liquidity',
|
|
555
|
+
message: 'Initial liquidity:',
|
|
556
|
+
choices: [
|
|
557
|
+
{ name: '500,000 (0.5 USDC)', value: '500000' },
|
|
558
|
+
{ name: '1,000,000 (1 USDC) - recommended', value: '1000000' },
|
|
559
|
+
{ name: '5,000,000 (5 USDC)', value: '5000000' },
|
|
560
|
+
{ name: 'Custom', value: 'custom' }
|
|
561
|
+
],
|
|
562
|
+
default: 1
|
|
563
|
+
}]);
|
|
564
|
+
|
|
565
|
+
let finalLiquidity = liquidity;
|
|
566
|
+
if (liquidity === 'custom') {
|
|
567
|
+
const { customLiquidity } = await inquirer.prompt([{
|
|
568
|
+
type: 'input',
|
|
569
|
+
name: 'customLiquidity',
|
|
570
|
+
message: 'Enter liquidity in base units:',
|
|
571
|
+
default: '1000000'
|
|
572
|
+
}]);
|
|
573
|
+
finalLiquidity = customLiquidity;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Market type
|
|
577
|
+
const { marketType } = await inquirer.prompt([{
|
|
578
|
+
type: 'list',
|
|
579
|
+
name: 'marketType',
|
|
580
|
+
message: 'Market type:',
|
|
581
|
+
choices: [
|
|
582
|
+
{ name: 'AMM (Automated Market Maker) - recommended', value: 'amm' },
|
|
583
|
+
{ name: 'P2P (Peer-to-Peer)', value: 'p2p' }
|
|
584
|
+
],
|
|
585
|
+
default: 0
|
|
586
|
+
}]);
|
|
587
|
+
|
|
588
|
+
// Summary
|
|
589
|
+
console.log(chalk.cyan('\n=== Market Summary ===\n'));
|
|
590
|
+
infoLine(`Question: ${finalQuestion}`);
|
|
591
|
+
infoLine(`Duration: ${finalDuration} days`);
|
|
592
|
+
infoLine(`Liquidity: ${finalLiquidity}`);
|
|
593
|
+
infoLine(`Type: ${marketType.toUpperCase()}`);
|
|
594
|
+
console.log();
|
|
595
|
+
|
|
596
|
+
if (!validation.valid) {
|
|
597
|
+
console.log(chalk.yellow('Cannot create market: wallet not configured'));
|
|
598
|
+
console.log(chalk.gray('Set WALLET_PRIVATE_KEY or PRIVATE_KEY in .env'));
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Confirm
|
|
603
|
+
const { confirm } = await inquirer.prompt([{
|
|
604
|
+
type: 'confirm',
|
|
605
|
+
name: 'confirm',
|
|
606
|
+
message: 'Create this market on Solana?',
|
|
607
|
+
default: true
|
|
608
|
+
}]);
|
|
609
|
+
|
|
610
|
+
if (!confirm) {
|
|
611
|
+
console.log(chalk.yellow('Market creation cancelled.'));
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// Create market
|
|
616
|
+
const agent = new PrivacyOracleAgent({ verbose: true });
|
|
617
|
+
|
|
618
|
+
const result = await withSpinner(
|
|
619
|
+
'Creating market on Solana',
|
|
620
|
+
async () => {
|
|
621
|
+
if (marketType === 'p2p') {
|
|
622
|
+
return agent.createP2PMarket({
|
|
623
|
+
question: finalQuestion,
|
|
624
|
+
durationDays: finalDuration,
|
|
625
|
+
amount: BigInt(finalLiquidity),
|
|
626
|
+
side: 'yes'
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
return agent.createPrivacyMarket({
|
|
630
|
+
question: finalQuestion,
|
|
631
|
+
durationDays: finalDuration,
|
|
632
|
+
liquidity: BigInt(finalLiquidity)
|
|
633
|
+
});
|
|
634
|
+
},
|
|
635
|
+
{ successText: 'Market created!' }
|
|
636
|
+
);
|
|
637
|
+
|
|
638
|
+
console.log();
|
|
639
|
+
successLine('Market created successfully!');
|
|
640
|
+
infoLine(`Address: ${result.market}`);
|
|
641
|
+
infoLine(`Signature: ${result.signature}`);
|
|
642
|
+
|
|
643
|
+
} catch (error) {
|
|
644
|
+
if (error.name === 'ExitPromptError') {
|
|
645
|
+
console.log(chalk.yellow('\nCancelled.'));
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
console.error(chalk.red('Error:'), error.message);
|
|
649
|
+
process.exit(1);
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
// Tokens command
|
|
654
|
+
program
|
|
655
|
+
.command('tokens')
|
|
656
|
+
.description('List supported collateral tokens')
|
|
657
|
+
.option('--check <mint>', 'Check if a mint supports confidential transfers')
|
|
658
|
+
.action(async (options) => {
|
|
659
|
+
try {
|
|
660
|
+
const config = getConfig();
|
|
661
|
+
|
|
662
|
+
if (options.check) {
|
|
663
|
+
console.log(chalk.cyan('\nChecking mint for confidential transfer support...\n'));
|
|
664
|
+
|
|
665
|
+
const connection = new Connection(config.rpcUrl);
|
|
666
|
+
const result = await withSpinner(
|
|
667
|
+
'Checking mint',
|
|
668
|
+
() => checkConfidentialTransferSupport(connection, options.check),
|
|
669
|
+
{ successText: 'Check complete' }
|
|
670
|
+
);
|
|
671
|
+
|
|
672
|
+
console.log();
|
|
673
|
+
infoLine(`Mint: ${options.check}`);
|
|
674
|
+
infoLine(`Token-2022: ${result.isToken2022 ? chalk.green('Yes') : 'No'}`);
|
|
675
|
+
infoLine(`Confidential transfers: ${result.supported ? chalk.green('Supported') : chalk.yellow('Not supported')}`);
|
|
676
|
+
infoLine(`Details: ${result.reason}`);
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
console.log(chalk.cyan('\nSupported Collateral Tokens:\n'));
|
|
681
|
+
|
|
682
|
+
const networks = ['mainnet', 'devnet'];
|
|
683
|
+
|
|
684
|
+
for (const network of networks) {
|
|
685
|
+
console.log(chalk.yellow(`${network.charAt(0).toUpperCase() + network.slice(1)}:`));
|
|
686
|
+
const tokens = listSupportedTokens(network);
|
|
687
|
+
|
|
688
|
+
tokens.forEach(token => {
|
|
689
|
+
console.log(` ${chalk.white(token.symbol)}`);
|
|
690
|
+
console.log(chalk.gray(` ${token.address}`));
|
|
691
|
+
});
|
|
692
|
+
console.log();
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
console.log(chalk.gray('Tip: Use a Token-2022 mint with confidential transfer extension'));
|
|
696
|
+
console.log(chalk.gray(' for privacy-focused collateral.'));
|
|
697
|
+
console.log(chalk.gray(' Set COLLATERAL_TOKEN in .env to use a custom mint.'));
|
|
698
|
+
|
|
699
|
+
} catch (error) {
|
|
700
|
+
console.error(chalk.red('Error:'), error.message);
|
|
701
|
+
process.exit(1);
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
// AI-powered generation
|
|
706
|
+
program
|
|
707
|
+
.command('ai-generate')
|
|
708
|
+
.description('Generate market ideas using Claude AI')
|
|
709
|
+
.option('-c, --count <count>', 'Number of markets to generate', '3')
|
|
710
|
+
.option('-t, --topic <topic>', 'Specific topic to generate markets about')
|
|
711
|
+
.option('--create', 'Actually create the markets on-chain')
|
|
712
|
+
.option('-v, --verbose', 'Verbose output')
|
|
713
|
+
.action(async (options) => {
|
|
714
|
+
try {
|
|
715
|
+
const config = getConfig();
|
|
716
|
+
|
|
717
|
+
if (!config.anthropicApiKey) {
|
|
718
|
+
errorLine('ANTHROPIC_API_KEY not configured');
|
|
719
|
+
console.log(chalk.gray('Set ANTHROPIC_API_KEY in .env to use AI features'));
|
|
720
|
+
process.exit(1);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
const count = parseInt(options.count, 10);
|
|
724
|
+
console.log(chalk.cyan(`\nGenerating ${count} AI-powered market ideas...\n`));
|
|
725
|
+
|
|
726
|
+
const generator = new AIMarketGenerator(config.anthropicApiKey);
|
|
727
|
+
|
|
728
|
+
let results;
|
|
729
|
+
if (options.topic) {
|
|
730
|
+
results = await withSpinner(
|
|
731
|
+
`Generating markets about "${options.topic}"`,
|
|
732
|
+
async () => {
|
|
733
|
+
const markets = [];
|
|
734
|
+
for (let i = 0; i < count; i++) {
|
|
735
|
+
const result = await generator.generateFromTopic(options.topic);
|
|
736
|
+
markets.push({ success: true, market: result });
|
|
737
|
+
}
|
|
738
|
+
return markets;
|
|
739
|
+
},
|
|
740
|
+
{ successText: 'Markets generated' }
|
|
741
|
+
);
|
|
742
|
+
} else {
|
|
743
|
+
results = await withSpinner(
|
|
744
|
+
'Generating diverse markets',
|
|
745
|
+
() => generator.generateDiverseMarkets(count),
|
|
746
|
+
{ successText: 'Markets generated' }
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
console.log();
|
|
751
|
+
results.forEach((result, i) => {
|
|
752
|
+
if (result.success) {
|
|
753
|
+
const m = result.market;
|
|
754
|
+
console.log(chalk.yellow(`${i + 1}.`), m.question);
|
|
755
|
+
console.log(chalk.gray(` Category: ${m.categoryName || m.category}`));
|
|
756
|
+
console.log(chalk.gray(` Duration: ${m.suggestedDurationDays} days | Liquidity: ${m.suggestedLiquidityUSDC} USDC`));
|
|
757
|
+
console.log(chalk.gray(` Urgency: ${m.urgency}`));
|
|
758
|
+
console.log(chalk.dim(` Reasoning: ${m.reasoning}`));
|
|
759
|
+
console.log();
|
|
760
|
+
} else {
|
|
761
|
+
errorLine(`Failed: ${result.error}`);
|
|
762
|
+
}
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
// Optionally create markets
|
|
766
|
+
if (options.create) {
|
|
767
|
+
const validation = validateConfig(config);
|
|
768
|
+
if (!validation.valid) {
|
|
769
|
+
console.log(chalk.yellow('\nCannot create markets: wallet not configured'));
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
const { confirm } = await inquirer.prompt([{
|
|
774
|
+
type: 'confirm',
|
|
775
|
+
name: 'confirm',
|
|
776
|
+
message: `Create ${results.filter(r => r.success).length} markets on Solana?`,
|
|
777
|
+
default: false
|
|
778
|
+
}]);
|
|
779
|
+
|
|
780
|
+
if (confirm) {
|
|
781
|
+
const agent = new PrivacyOracleAgent({ verbose: options.verbose });
|
|
782
|
+
|
|
783
|
+
for (const result of results.filter(r => r.success)) {
|
|
784
|
+
const m = result.market;
|
|
785
|
+
try {
|
|
786
|
+
const created = await withSpinner(
|
|
787
|
+
`Creating: ${m.question.slice(0, 50)}...`,
|
|
788
|
+
() => agent.createPrivacyMarket({
|
|
789
|
+
question: m.question,
|
|
790
|
+
durationDays: m.suggestedDurationDays,
|
|
791
|
+
liquidity: BigInt(m.suggestedLiquidityUSDC * 1000000)
|
|
792
|
+
}),
|
|
793
|
+
{ successText: 'Created' }
|
|
794
|
+
);
|
|
795
|
+
successLine(`Market: ${created.market}`);
|
|
796
|
+
} catch (error) {
|
|
797
|
+
errorLine(`Failed: ${error.message}`);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
} catch (error) {
|
|
804
|
+
console.error(chalk.red('Error:'), error.message);
|
|
805
|
+
process.exit(1);
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
// AI news scoring
|
|
810
|
+
program
|
|
811
|
+
.command('ai-score')
|
|
812
|
+
.description('Score news headlines for privacy relevance using AI')
|
|
813
|
+
.argument('<headlines...>', 'Headlines to score (or use --file)')
|
|
814
|
+
.option('-f, --file <file>', 'Read headlines from file (one per line)')
|
|
815
|
+
.option('--min-score <score>', 'Minimum score to show', '0')
|
|
816
|
+
.action(async (headlines, options) => {
|
|
817
|
+
try {
|
|
818
|
+
const config = getConfig();
|
|
819
|
+
|
|
820
|
+
if (!config.anthropicApiKey) {
|
|
821
|
+
errorLine('ANTHROPIC_API_KEY not configured');
|
|
822
|
+
process.exit(1);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
const scorer = new AIScorer(config.anthropicApiKey);
|
|
826
|
+
const minScore = parseInt(options.minScore, 10);
|
|
827
|
+
|
|
828
|
+
// Prepare news items
|
|
829
|
+
const newsItems = headlines.map(h => ({ title: h }));
|
|
830
|
+
|
|
831
|
+
console.log(chalk.cyan(`\nScoring ${newsItems.length} headlines for privacy relevance...\n`));
|
|
832
|
+
|
|
833
|
+
const results = await withSpinner(
|
|
834
|
+
'Scoring headlines',
|
|
835
|
+
() => scorer.scoreBatch(newsItems),
|
|
836
|
+
{ successText: 'Scoring complete' }
|
|
837
|
+
);
|
|
838
|
+
|
|
839
|
+
console.log();
|
|
840
|
+
results
|
|
841
|
+
.filter(r => r.success && r.score >= minScore)
|
|
842
|
+
.sort((a, b) => b.score - a.score)
|
|
843
|
+
.forEach(r => {
|
|
844
|
+
const scoreColor = r.score >= 70 ? chalk.green :
|
|
845
|
+
r.score >= 50 ? chalk.yellow : chalk.gray;
|
|
846
|
+
|
|
847
|
+
console.log(scoreColor(`[${r.score}]`), r.newsItem.title);
|
|
848
|
+
console.log(chalk.gray(` Category: ${r.category} | Urgency: ${r.urgency}`));
|
|
849
|
+
if (r.marketPotential) {
|
|
850
|
+
console.log(chalk.cyan(` Market idea: ${r.suggestedMarketAngle}`));
|
|
851
|
+
}
|
|
852
|
+
console.log();
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
} catch (error) {
|
|
856
|
+
console.error(chalk.red('Error:'), error.message);
|
|
857
|
+
process.exit(1);
|
|
858
|
+
}
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
// AI market resolution analysis
|
|
862
|
+
program
|
|
863
|
+
.command('ai-resolve')
|
|
864
|
+
.description('Analyze markets for potential resolution using AI')
|
|
865
|
+
.option('-a, --all', 'Check all active markets')
|
|
866
|
+
.option('-m, --market <address>', 'Check specific market')
|
|
867
|
+
.option('--min-confidence <conf>', 'Minimum confidence to show', '0.5')
|
|
868
|
+
.action(async (options) => {
|
|
869
|
+
try {
|
|
870
|
+
const config = getConfig();
|
|
871
|
+
|
|
872
|
+
if (!config.anthropicApiKey) {
|
|
873
|
+
errorLine('ANTHROPIC_API_KEY not configured');
|
|
874
|
+
process.exit(1);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
const resolver = new AIResolver(config.anthropicApiKey);
|
|
878
|
+
const minConfidence = parseFloat(options.minConfidence);
|
|
879
|
+
|
|
880
|
+
let markets = [];
|
|
881
|
+
|
|
882
|
+
if (options.market) {
|
|
883
|
+
// Fetch single market
|
|
884
|
+
const agent = new PrivacyOracleAgent({ verbose: false });
|
|
885
|
+
const info = await agent.fetchMarketInfo(options.market);
|
|
886
|
+
markets = [{
|
|
887
|
+
address: options.market,
|
|
888
|
+
question: info.question,
|
|
889
|
+
creationTime: info.startTime?.getTime() || Date.now(),
|
|
890
|
+
endTime: info.endTime?.getTime(),
|
|
891
|
+
durationDays: Math.ceil((info.endTime - info.startTime) / (1000 * 60 * 60 * 24))
|
|
892
|
+
}];
|
|
893
|
+
} else if (options.all) {
|
|
894
|
+
// Load from storage
|
|
895
|
+
const storagePath = config.daemon.storagePath || ':memory:';
|
|
896
|
+
const store = createMarketStore(storagePath);
|
|
897
|
+
markets = store.getAllMarkets({ status: 'active' });
|
|
898
|
+
store.close();
|
|
899
|
+
} else {
|
|
900
|
+
errorLine('Specify --market <address> or --all');
|
|
901
|
+
process.exit(1);
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
if (markets.length === 0) {
|
|
905
|
+
console.log(chalk.yellow('No markets to analyze'));
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
console.log(chalk.cyan(`\nAnalyzing ${markets.length} markets for resolution...\n`));
|
|
910
|
+
|
|
911
|
+
const results = await withSpinner(
|
|
912
|
+
'Analyzing markets',
|
|
913
|
+
() => resolver.analyzeMarkets(markets),
|
|
914
|
+
{ successText: 'Analysis complete' }
|
|
915
|
+
);
|
|
916
|
+
|
|
917
|
+
console.log();
|
|
918
|
+
results.forEach(r => {
|
|
919
|
+
if (!r.success) {
|
|
920
|
+
errorLine(`Failed to analyze: ${r.error}`);
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
const a = r.analysis;
|
|
925
|
+
if (a.confidence < minConfidence) return;
|
|
926
|
+
|
|
927
|
+
const confColor = a.confidence >= 0.8 ? chalk.green :
|
|
928
|
+
a.confidence >= 0.5 ? chalk.yellow : chalk.gray;
|
|
929
|
+
|
|
930
|
+
console.log(chalk.white(r.market.question));
|
|
931
|
+
console.log(chalk.gray(` Address: ${r.market.address}`));
|
|
932
|
+
console.log(` Can resolve: ${a.canResolve ? chalk.green('Yes') : chalk.yellow('No')}`);
|
|
933
|
+
if (a.canResolve) {
|
|
934
|
+
console.log(` Outcome: ${a.outcome === 'yes' ? chalk.green('YES') : a.outcome === 'no' ? chalk.red('NO') : chalk.gray('Unknown')}`);
|
|
935
|
+
}
|
|
936
|
+
console.log(` Confidence: ${confColor((a.confidence * 100).toFixed(0) + '%')}`);
|
|
937
|
+
console.log(` Action: ${a.suggestedAction}`);
|
|
938
|
+
console.log(chalk.dim(` Reasoning: ${a.reasoning.slice(0, 200)}...`));
|
|
939
|
+
console.log();
|
|
940
|
+
});
|
|
941
|
+
|
|
942
|
+
} catch (error) {
|
|
943
|
+
console.error(chalk.red('Error:'), error.message);
|
|
944
|
+
process.exit(1);
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
|
|
948
|
+
program.parse();
|