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/src/index.js ADDED
@@ -0,0 +1,139 @@
1
+ // PNPFUCIUS - The PNP Exchange SDK
2
+ // Create, trade, and settle prediction markets on Solana
3
+ // Uses Helius RPC + PNP SDK
4
+
5
+ // Core agent
6
+ export { PrivacyOracleAgent, createAgent } from './agent.js';
7
+ export { getConfig, validateConfig, getHeliusWsUrl, getHeliusApiUrl } from './config.js';
8
+ export {
9
+ generatePrivacyMarket,
10
+ generateMultipleMarkets,
11
+ getMarketsByCategory,
12
+ listCategories,
13
+ PRIVACY_CATEGORIES
14
+ } from './privacy-markets.js';
15
+
16
+ // Helius integration
17
+ export { HeliusClient, createHeliusClient } from './helius/client.js';
18
+ export { TransactionTracker, createTransactionTracker } from './helius/transaction-tracker.js';
19
+ export { WebhookServer, createWebhookServer } from './helius/webhooks.js';
20
+
21
+ // Daemon mode
22
+ export { PrivacyOracleDaemon, createDaemon } from './daemon/index.js';
23
+ export { Scheduler, createScheduler } from './daemon/scheduler.js';
24
+ export { setupGracefulShutdown, HealthMonitor } from './daemon/lifecycle.js';
25
+
26
+ // Storage
27
+ export { MarketStore, createMarketStore } from './storage/market-store.js';
28
+
29
+ // Events
30
+ export { agentEvents, AgentEvents } from './events/emitter.js';
31
+
32
+ // Monitoring
33
+ export { NewsMonitor, createNewsMonitor, DEFAULT_NEWS_SOURCES } from './monitoring/news-monitor.js';
34
+ export { scoreRelevance, generateMarketFromNews, PRIVACY_KEYWORDS } from './monitoring/news-scorer.js';
35
+
36
+ // Analytics
37
+ export { DashboardAggregator, createAggregator, formatNumber, formatDuration } from './analytics/aggregator.js';
38
+
39
+ // Privacy tokens
40
+ export {
41
+ TOKENS,
42
+ PRIVACY_TOKEN_INFO,
43
+ getCollateralMint,
44
+ listSupportedTokens,
45
+ checkConfidentialTransferSupport,
46
+ getTokenInfo,
47
+ formatTokenAmount,
48
+ parseTokenAmount,
49
+ getCollateralConfig
50
+ } from './collateral/privacy-tokens.js';
51
+
52
+ // CLI utilities
53
+ export {
54
+ createSpinner,
55
+ withSpinner,
56
+ withProgress,
57
+ createIndeterminateProgress,
58
+ StepProgress,
59
+ statusLine,
60
+ successLine,
61
+ errorLine,
62
+ infoLine
63
+ } from './utils/spinner.js';
64
+
65
+ // PNPFUCIUS - Interactive CLI Agent
66
+ export { PnpfuciusAgent, createPnpfuciusAgent } from './predict/agent.js';
67
+ export { tools as pnpTools, executeTool } from './predict/tools/index.js';
68
+
69
+ // Quick start function for programmatic use
70
+ export async function quickCreate(question, options = {}) {
71
+ const { createAgent } = await import('./agent.js');
72
+ const agent = await createAgent({ verbose: options.verbose });
73
+
74
+ if (question) {
75
+ return agent.createMarket({ question, ...options });
76
+ } else {
77
+ return agent.createPrivacyMarket(options);
78
+ }
79
+ }
80
+
81
+ // Run agent as daemon (recommended over runAutonomous)
82
+ export async function startDaemon(config = {}) {
83
+ const { PrivacyOracleDaemon } = await import('./daemon/index.js');
84
+ const { getConfig } = await import('./config.js');
85
+
86
+ const baseConfig = getConfig();
87
+ const mergedConfig = {
88
+ ...baseConfig,
89
+ ...config,
90
+ daemon: {
91
+ ...baseConfig.daemon,
92
+ ...config.daemon
93
+ }
94
+ };
95
+
96
+ const daemon = new PrivacyOracleDaemon(mergedConfig);
97
+ await daemon.start();
98
+
99
+ return daemon;
100
+ }
101
+
102
+ // Legacy autonomous mode (use startDaemon instead)
103
+ export async function runAutonomous(config = {}) {
104
+ console.warn('runAutonomous is deprecated. Use startDaemon for better functionality.');
105
+
106
+ const { createAgent } = await import('./agent.js');
107
+ const { generateMultipleMarkets } = await import('./privacy-markets.js');
108
+
109
+ const count = config.count || 1;
110
+ const interval = config.intervalMs || 3600000;
111
+
112
+ const agent = await createAgent({ verbose: true });
113
+
114
+ console.log(`Starting autonomous mode: ${count} markets every ${interval / 60000} minutes`);
115
+
116
+ const createRound = async () => {
117
+ const ideas = generateMultipleMarkets(count);
118
+
119
+ for (const idea of ideas) {
120
+ try {
121
+ const result = await agent.createMarket({
122
+ question: idea.question,
123
+ durationDays: idea.durationDays,
124
+ liquidity: idea.suggestedLiquidity
125
+ });
126
+
127
+ console.log(`Created: ${result.market}`);
128
+ } catch (error) {
129
+ console.error(`Failed: ${error.message}`);
130
+ }
131
+ }
132
+ };
133
+
134
+ await createRound();
135
+
136
+ if (config.continuous) {
137
+ setInterval(createRound, interval);
138
+ }
139
+ }
@@ -0,0 +1,262 @@
1
+ // RSS feed parsing for privacy-related news monitoring
2
+ // Provides context for timely market generation
3
+
4
+ import Parser from 'rss-parser';
5
+ import { agentEvents, AgentEvents } from '../events/emitter.js';
6
+ import { scoreRelevance, PRIVACY_KEYWORDS } from './news-scorer.js';
7
+
8
+ // Default privacy news sources
9
+ const DEFAULT_SOURCES = [
10
+ {
11
+ name: 'EFF',
12
+ url: 'https://www.eff.org/rss/updates.xml',
13
+ keywords: ['privacy', 'encryption', 'surveillance', 'data protection', 'FISA'],
14
+ weight: 1.0
15
+ },
16
+ {
17
+ name: 'Decrypt',
18
+ url: 'https://decrypt.co/feed',
19
+ keywords: ['privacy', 'zk', 'zero-knowledge', 'tornado', 'zcash', 'monero', 'mixer'],
20
+ weight: 0.9
21
+ },
22
+ {
23
+ name: 'CoinDesk',
24
+ url: 'https://www.coindesk.com/arc/outboundfeeds/rss/',
25
+ keywords: ['privacy', 'regulation', 'sanctions', 'compliance', 'OFAC'],
26
+ weight: 0.7
27
+ },
28
+ {
29
+ name: 'The Block',
30
+ url: 'https://www.theblock.co/rss.xml',
31
+ keywords: ['privacy', 'zk-rollup', 'confidential', 'anonymous'],
32
+ weight: 0.8
33
+ }
34
+ ];
35
+
36
+ export class NewsMonitor {
37
+ constructor(config = {}) {
38
+ this.sources = config.sources || DEFAULT_SOURCES;
39
+ this.parser = new Parser({
40
+ timeout: 10000,
41
+ headers: {
42
+ 'User-Agent': 'PrivacyOracleAgent/1.0'
43
+ }
44
+ });
45
+ this.checkInterval = config.checkInterval || 300000; // 5 minutes
46
+ this.recentEvents = [];
47
+ this.maxEvents = config.maxEvents || 100;
48
+ this.seenIds = new Set();
49
+ this.timer = null;
50
+ this.isRunning = false;
51
+ this.lastCheck = null;
52
+ this.errorCount = 0;
53
+ }
54
+
55
+ async start() {
56
+ if (this.isRunning) return this;
57
+
58
+ this.isRunning = true;
59
+
60
+ // Initial check
61
+ await this.checkFeeds();
62
+
63
+ // Schedule periodic checks
64
+ this.timer = setInterval(() => this.checkFeeds(), this.checkInterval);
65
+
66
+ return this;
67
+ }
68
+
69
+ async stop() {
70
+ this.isRunning = false;
71
+
72
+ if (this.timer) {
73
+ clearInterval(this.timer);
74
+ this.timer = null;
75
+ }
76
+
77
+ return this;
78
+ }
79
+
80
+ async checkFeeds() {
81
+ if (!this.isRunning) return;
82
+
83
+ this.lastCheck = Date.now();
84
+ const newEvents = [];
85
+
86
+ for (const source of this.sources) {
87
+ try {
88
+ const feed = await this.parser.parseURL(source.url);
89
+
90
+ for (const item of feed.items || []) {
91
+ const id = item.guid || item.link || item.title;
92
+
93
+ if (this.seenIds.has(id)) continue;
94
+
95
+ const event = this.processItem(item, source);
96
+
97
+ // Only keep events with relevance score >= 30
98
+ if (event.relevanceScore >= 30) {
99
+ this.addEvent(event);
100
+ newEvents.push(event);
101
+
102
+ agentEvents.emitTyped(AgentEvents.NEWS_EVENT, event);
103
+ }
104
+
105
+ this.seenIds.add(id);
106
+
107
+ // Limit seen IDs to prevent memory growth
108
+ if (this.seenIds.size > 10000) {
109
+ const idsArray = Array.from(this.seenIds);
110
+ this.seenIds = new Set(idsArray.slice(-5000));
111
+ }
112
+ }
113
+ } catch (error) {
114
+ this.errorCount++;
115
+ console.error(`Failed to fetch ${source.name}:`, error.message);
116
+ }
117
+ }
118
+
119
+ agentEvents.emitTyped(AgentEvents.NEWS_CHECK_COMPLETE, {
120
+ sourcesChecked: this.sources.length,
121
+ newEventsFound: newEvents.length,
122
+ totalEvents: this.recentEvents.length,
123
+ errorCount: this.errorCount
124
+ });
125
+
126
+ return newEvents;
127
+ }
128
+
129
+ processItem(item, source) {
130
+ const title = item.title || '';
131
+ const content = item.contentSnippet || item.content || '';
132
+ const text = `${title} ${content}`.toLowerCase();
133
+
134
+ const { score, matchedKeywords, suggestedCategory, urgency } = scoreRelevance(
135
+ text,
136
+ source.keywords,
137
+ source.weight
138
+ );
139
+
140
+ return {
141
+ id: item.guid || item.link || `${source.name}-${Date.now()}`,
142
+ title: title,
143
+ link: item.link || '',
144
+ source: source.name,
145
+ publishedAt: item.pubDate ? new Date(item.pubDate).getTime() : Date.now(),
146
+ relevanceScore: score,
147
+ matchedKeywords,
148
+ suggestedCategory,
149
+ urgency,
150
+ snippet: content.slice(0, 200)
151
+ };
152
+ }
153
+
154
+ addEvent(event) {
155
+ this.recentEvents.unshift(event);
156
+
157
+ if (this.recentEvents.length > this.maxEvents) {
158
+ this.recentEvents = this.recentEvents.slice(0, this.maxEvents);
159
+ }
160
+ }
161
+
162
+ getRecentEvents(limit = 10) {
163
+ return this.recentEvents.slice(0, limit);
164
+ }
165
+
166
+ getEventsByCategory(category, limit = 10) {
167
+ return this.recentEvents
168
+ .filter(e => e.suggestedCategory === category)
169
+ .slice(0, limit);
170
+ }
171
+
172
+ getHighUrgencyEvents(limit = 5) {
173
+ return this.recentEvents
174
+ .filter(e => e.urgency === 'breaking' || e.urgency === 'timely')
175
+ .slice(0, limit);
176
+ }
177
+
178
+ getStatus() {
179
+ return {
180
+ isRunning: this.isRunning,
181
+ sourcesCount: this.sources.length,
182
+ eventsCount: this.recentEvents.length,
183
+ lastCheck: this.lastCheck,
184
+ errorCount: this.errorCount,
185
+ seenIdsCount: this.seenIds.size
186
+ };
187
+ }
188
+
189
+ // Add a custom news source
190
+ addSource(source) {
191
+ if (!source.name || !source.url) {
192
+ throw new Error('Source requires name and url');
193
+ }
194
+
195
+ this.sources.push({
196
+ keywords: PRIVACY_KEYWORDS,
197
+ weight: 0.5,
198
+ ...source
199
+ });
200
+
201
+ return this;
202
+ }
203
+
204
+ // Remove a news source
205
+ removeSource(name) {
206
+ this.sources = this.sources.filter(s => s.name !== name);
207
+ return this;
208
+ }
209
+ }
210
+
211
+ export function createNewsMonitor(config) {
212
+ return new NewsMonitor(config);
213
+ }
214
+
215
+ // Mock news source for testing
216
+ export class MockNewsSource {
217
+ constructor() {
218
+ this.events = [
219
+ {
220
+ title: 'EU Proposes New Digital Privacy Framework with Strict Encryption Rules',
221
+ relevanceScore: 85,
222
+ suggestedCategory: 'regulation',
223
+ urgency: 'timely'
224
+ },
225
+ {
226
+ title: 'Major ZK Protocol Reaches $1B TVL Milestone',
227
+ relevanceScore: 90,
228
+ suggestedCategory: 'technology',
229
+ urgency: 'breaking'
230
+ },
231
+ {
232
+ title: 'Signal Reports Record User Growth Amid Privacy Concerns',
233
+ relevanceScore: 75,
234
+ suggestedCategory: 'adoption',
235
+ urgency: 'timely'
236
+ },
237
+ {
238
+ title: 'Data Breach Affects 50M Users at Major Tech Company',
239
+ relevanceScore: 80,
240
+ suggestedCategory: 'events',
241
+ urgency: 'breaking'
242
+ },
243
+ {
244
+ title: 'Tornado Cash Developer Case Reaches New Development',
245
+ relevanceScore: 95,
246
+ suggestedCategory: 'regulation',
247
+ urgency: 'breaking'
248
+ }
249
+ ];
250
+ }
251
+
252
+ async getEvents(limit = 5) {
253
+ return this.events.slice(0, limit).map(e => ({
254
+ ...e,
255
+ id: `mock-${Math.random().toString(36).slice(2)}`,
256
+ link: 'https://example.com',
257
+ source: 'mock',
258
+ publishedAt: Date.now(),
259
+ matchedKeywords: ['privacy']
260
+ }));
261
+ }
262
+ }
@@ -0,0 +1,236 @@
1
+ // Relevance scoring algorithm for privacy news
2
+ // Determines how relevant a news item is for market generation
3
+
4
+ // Core privacy-related keywords with weights
5
+ export const PRIVACY_KEYWORDS = {
6
+ // High relevance - direct privacy tech
7
+ 'zero-knowledge': 3,
8
+ 'zk-proof': 3,
9
+ 'zk-snark': 3,
10
+ 'zk-stark': 3,
11
+ 'zkrollup': 3,
12
+ 'confidential': 2.5,
13
+ 'encryption': 2.5,
14
+ 'encrypted': 2.5,
15
+ 'privacy-preserving': 3,
16
+ 'private transaction': 3,
17
+
18
+ // Privacy protocols
19
+ 'tornado cash': 3,
20
+ 'zcash': 2.5,
21
+ 'monero': 2.5,
22
+ 'light protocol': 3,
23
+ 'elusiv': 3,
24
+ 'aztec': 2.5,
25
+ 'railgun': 2.5,
26
+ 'secret network': 2.5,
27
+
28
+ // Regulatory
29
+ 'gdpr': 2,
30
+ 'privacy law': 2.5,
31
+ 'data protection': 2,
32
+ 'surveillance': 2.5,
33
+ 'sanctions': 2,
34
+ 'ofac': 2.5,
35
+ 'compliance': 1.5,
36
+ 'kyc': 1.5,
37
+ 'aml': 1.5,
38
+
39
+ // General privacy
40
+ 'privacy': 1.5,
41
+ 'anonymous': 2,
42
+ 'anonymity': 2,
43
+ 'pseudonymous': 1.5,
44
+ 'private': 1,
45
+ 'mixer': 2,
46
+ 'mixing': 2,
47
+ 'shielded': 2.5,
48
+
49
+ // Tech terms
50
+ 'homomorphic': 3,
51
+ 'mpc': 2.5,
52
+ 'secure enclave': 2,
53
+ 'tee': 2,
54
+ 'trusted execution': 2,
55
+
56
+ // Events
57
+ 'data breach': 2.5,
58
+ 'leak': 1.5,
59
+ 'hack': 1.5,
60
+ 'compromised': 1.5,
61
+
62
+ // Solana specific
63
+ 'solana privacy': 3,
64
+ 'spl confidential': 3,
65
+ 'token-2022 confidential': 3
66
+ };
67
+
68
+ // Category mappings
69
+ const CATEGORY_KEYWORDS = {
70
+ regulation: ['law', 'regulation', 'gdpr', 'sanctions', 'ofac', 'compliance', 'legislation', 'ban', 'restrict'],
71
+ technology: ['zk', 'protocol', 'launch', 'release', 'upgrade', 'mainnet', 'testnet', 'tvl', 'smart contract'],
72
+ adoption: ['users', 'growth', 'adoption', 'enterprise', 'mainstream', 'wallet', 'integration'],
73
+ events: ['breach', 'hack', 'leak', 'scandal', 'arrest', 'raid', 'lawsuit', 'verdict']
74
+ };
75
+
76
+ // Urgency indicators
77
+ const URGENCY_KEYWORDS = {
78
+ breaking: ['breaking', 'just in', 'urgent', 'alert', 'confirmed', 'arrested', 'breached'],
79
+ timely: ['announces', 'launches', 'releases', 'proposes', 'reaches', 'exceeds', 'surpasses']
80
+ };
81
+
82
+ export function scoreRelevance(text, sourceKeywords = [], sourceWeight = 1.0) {
83
+ const lowerText = text.toLowerCase();
84
+ let score = 0;
85
+ const matchedKeywords = [];
86
+
87
+ // Score based on privacy keywords
88
+ for (const [keyword, weight] of Object.entries(PRIVACY_KEYWORDS)) {
89
+ if (lowerText.includes(keyword)) {
90
+ score += weight * 10;
91
+ matchedKeywords.push(keyword);
92
+ }
93
+ }
94
+
95
+ // Additional score from source-specific keywords
96
+ for (const keyword of sourceKeywords) {
97
+ if (lowerText.includes(keyword.toLowerCase())) {
98
+ score += 5;
99
+ if (!matchedKeywords.includes(keyword)) {
100
+ matchedKeywords.push(keyword);
101
+ }
102
+ }
103
+ }
104
+
105
+ // Apply source weight
106
+ score = Math.round(score * sourceWeight);
107
+
108
+ // Cap at 100
109
+ score = Math.min(score, 100);
110
+
111
+ // Determine suggested category
112
+ const suggestedCategory = determineCategory(lowerText);
113
+
114
+ // Determine urgency
115
+ const urgency = determineUrgency(lowerText);
116
+
117
+ return {
118
+ score,
119
+ matchedKeywords,
120
+ suggestedCategory,
121
+ urgency
122
+ };
123
+ }
124
+
125
+ function determineCategory(text) {
126
+ const scores = {
127
+ regulation: 0,
128
+ technology: 0,
129
+ adoption: 0,
130
+ events: 0
131
+ };
132
+
133
+ for (const [category, keywords] of Object.entries(CATEGORY_KEYWORDS)) {
134
+ for (const keyword of keywords) {
135
+ if (text.includes(keyword)) {
136
+ scores[category] += 1;
137
+ }
138
+ }
139
+ }
140
+
141
+ // Find highest scoring category
142
+ let maxScore = 0;
143
+ let bestCategory = 'technology'; // default
144
+
145
+ for (const [category, score] of Object.entries(scores)) {
146
+ if (score > maxScore) {
147
+ maxScore = score;
148
+ bestCategory = category;
149
+ }
150
+ }
151
+
152
+ return bestCategory;
153
+ }
154
+
155
+ function determineUrgency(text) {
156
+ for (const keyword of URGENCY_KEYWORDS.breaking) {
157
+ if (text.includes(keyword)) {
158
+ return 'breaking';
159
+ }
160
+ }
161
+
162
+ for (const keyword of URGENCY_KEYWORDS.timely) {
163
+ if (text.includes(keyword)) {
164
+ return 'timely';
165
+ }
166
+ }
167
+
168
+ return 'evergreen';
169
+ }
170
+
171
+ // Generate a market question from a news event
172
+ export function generateMarketFromNews(newsEvent) {
173
+ const { title, suggestedCategory, urgency } = newsEvent;
174
+
175
+ // Extract key entities from title
176
+ const entities = extractEntities(title);
177
+
178
+ // Generate appropriate duration based on urgency
179
+ const durationDays = urgency === 'breaking' ? 14 :
180
+ urgency === 'timely' ? 30 : 90;
181
+
182
+ // Generate question based on category
183
+ let question;
184
+
185
+ switch (suggestedCategory) {
186
+ case 'regulation':
187
+ question = `Will regulatory action be taken regarding "${entities.topic || 'this development'}" within ${durationDays} days?`;
188
+ break;
189
+ case 'technology':
190
+ question = `Will the technology mentioned in "${entities.topic || 'this news'}" see significant adoption by ${formatFutureDate(durationDays)}?`;
191
+ break;
192
+ case 'adoption':
193
+ question = `Will user adoption metrics exceed expectations for "${entities.topic || 'this platform'}" by ${formatFutureDate(durationDays)}?`;
194
+ break;
195
+ case 'events':
196
+ question = `Will there be follow-up developments on "${entities.topic || 'this event'}" within ${durationDays} days?`;
197
+ break;
198
+ default:
199
+ question = `Will "${entities.topic || 'this development'}" have significant impact by ${formatFutureDate(durationDays)}?`;
200
+ }
201
+
202
+ return {
203
+ question,
204
+ category: suggestedCategory,
205
+ durationDays,
206
+ urgency,
207
+ sourceEvent: newsEvent
208
+ };
209
+ }
210
+
211
+ function extractEntities(title) {
212
+ // Simple entity extraction - could be enhanced with NLP
213
+ const words = title.split(' ');
214
+
215
+ // Look for capitalized words as potential entities
216
+ const entities = words.filter(w =>
217
+ w.length > 2 &&
218
+ w[0] === w[0].toUpperCase() &&
219
+ !['The', 'And', 'For', 'New', 'With'].includes(w)
220
+ );
221
+
222
+ return {
223
+ topic: title.slice(0, 50),
224
+ entities
225
+ };
226
+ }
227
+
228
+ function formatFutureDate(daysFromNow) {
229
+ const date = new Date();
230
+ date.setDate(date.getDate() + daysFromNow);
231
+
232
+ const months = ['January', 'February', 'March', 'April', 'May', 'June',
233
+ 'July', 'August', 'September', 'October', 'November', 'December'];
234
+
235
+ return `${months[date.getMonth()]} ${date.getFullYear()}`;
236
+ }