decharge-scout 4.0.5 → 4.3.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/.env.example +5 -0
- package/index.js +135 -101
- package/package.json +1 -1
package/.env.example
CHANGED
|
@@ -27,3 +27,8 @@ STAKE_AMOUNT=0.01
|
|
|
27
27
|
|
|
28
28
|
# Premium Feature Price (in SOL)
|
|
29
29
|
PREMIUM_PRICE=0.001
|
|
30
|
+
|
|
31
|
+
# Query Cycle Interval (in minutes)
|
|
32
|
+
# How often to fetch weather & submit data (default: 15)
|
|
33
|
+
# For testing, you can set to 2 minutes: CYCLE_INTERVAL_MINUTES=2
|
|
34
|
+
CYCLE_INTERVAL_MINUTES=15
|
package/index.js
CHANGED
|
@@ -40,14 +40,15 @@ import { submitToOracle } from './src/oracle.js';
|
|
|
40
40
|
import { initializePoints, awardPoints, getPoints, savePoints } from './src/points.js';
|
|
41
41
|
import { getLocation } from './src/geolocation.js';
|
|
42
42
|
import { purchasePremiumData } from './src/x402.js';
|
|
43
|
-
import {
|
|
43
|
+
import { parseAlphaContribution, saveAlphaContribution, calculateAlphaBonus, getAlphaInsights, verifyContribution, getInformationSources } from './src/local-alpha.js';
|
|
44
44
|
|
|
45
45
|
const __filename = fileURLToPath(import.meta.url);
|
|
46
46
|
const __dirname = dirname(__filename);
|
|
47
47
|
|
|
48
48
|
// Configuration
|
|
49
49
|
const STAKE_AMOUNT = parseFloat(process.env.STAKE_AMOUNT || '0.01');
|
|
50
|
-
const
|
|
50
|
+
const CYCLE_INTERVAL_MINUTES = parseInt(process.env.CYCLE_INTERVAL_MINUTES || '15'); // Default: 15 minutes
|
|
51
|
+
const CYCLE_INTERVAL_MS = CYCLE_INTERVAL_MINUTES * 60 * 1000;
|
|
51
52
|
|
|
52
53
|
// Global state
|
|
53
54
|
let isRunning = true;
|
|
@@ -353,17 +354,147 @@ async function runQueryCycle(wallet, agentName, location, options) {
|
|
|
353
354
|
}
|
|
354
355
|
}
|
|
355
356
|
|
|
356
|
-
//
|
|
357
|
+
// Ask for special notes before submission (with 10s timeout)
|
|
358
|
+
console.log(chalk.cyan('\n💬 Add special notes to this submission? (optional)'));
|
|
359
|
+
console.log(chalk.gray(' Examples: "High AC usage today", "Local festival", "Grid maintenance"\n'));
|
|
360
|
+
console.log(chalk.gray(' (Will auto-submit in 10 seconds if no input)\n'));
|
|
361
|
+
|
|
362
|
+
const specialNotes = await Promise.race([
|
|
363
|
+
question(chalk.blue('Special notes (or press Enter to skip): ')),
|
|
364
|
+
new Promise((resolve) => {
|
|
365
|
+
setTimeout(() => {
|
|
366
|
+
console.log(chalk.yellow('\n⏱️ Timeout - submitting without notes'));
|
|
367
|
+
resolve('');
|
|
368
|
+
}, 10000); // 10 second timeout
|
|
369
|
+
})
|
|
370
|
+
]);
|
|
371
|
+
|
|
372
|
+
// Local Alpha Contribution - Ask BEFORE every submission for bonus points
|
|
373
|
+
console.log(chalk.cyan('\n💡 Bonus Points: Share Local Peak Time Knowledge!'));
|
|
374
|
+
console.log(chalk.gray(' Help improve global energy data by sharing when electricity is expensive/cheap in your area.\n'));
|
|
375
|
+
|
|
376
|
+
console.log(chalk.blue('💬 Examples:'));
|
|
377
|
+
console.log(chalk.gray(' • "7-9PM peak in Lagos" (evening peak)'));
|
|
378
|
+
console.log(chalk.gray(' • "1-5AM cheap in Berlin" (off-peak)'));
|
|
379
|
+
console.log(chalk.gray(' • "5-8PM peak in Mumbai" (dinner time surge)'));
|
|
380
|
+
console.log(chalk.gray(' • "2-6AM cheap in Texas" (wind energy overnight)'));
|
|
381
|
+
console.log(chalk.gray(' (Will auto-skip in 15 seconds if no input)\n'));
|
|
382
|
+
|
|
383
|
+
const alphaInput = await Promise.race([
|
|
384
|
+
question(chalk.blue('Share local peak times (or press Enter to skip): ')),
|
|
385
|
+
new Promise((resolve) => {
|
|
386
|
+
setTimeout(() => {
|
|
387
|
+
console.log(chalk.yellow('\n⏱️ Timeout - skipping bonus contribution'));
|
|
388
|
+
resolve('');
|
|
389
|
+
}, 15000); // 15 second timeout
|
|
390
|
+
})
|
|
391
|
+
]);
|
|
392
|
+
|
|
393
|
+
// Process alpha contribution if provided
|
|
394
|
+
if (alphaInput.trim()) {
|
|
395
|
+
const parsed = parseAlphaContribution(alphaInput, location);
|
|
396
|
+
if (parsed) {
|
|
397
|
+
// Verify contribution against current pricing data
|
|
398
|
+
const verification = verifyContribution(parsed, energyData);
|
|
399
|
+
|
|
400
|
+
// Save contribution with verification status
|
|
401
|
+
const contribution = saveAlphaContribution(parsed, agentName, location, verification);
|
|
402
|
+
|
|
403
|
+
// Store for dashboard submission
|
|
404
|
+
pendingAlphaContribution = {
|
|
405
|
+
type: parsed.type,
|
|
406
|
+
startHour: parsed.startHour,
|
|
407
|
+
endHour: parsed.endHour,
|
|
408
|
+
location: parsed.location,
|
|
409
|
+
verified: verification.verified,
|
|
410
|
+
confidence: verification.confidence,
|
|
411
|
+
verificationReasons: verification.reasons
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
// Calculate bonus (higher for verified contributions)
|
|
415
|
+
const alphaBonus = calculateAlphaBonus(parsed, verification);
|
|
416
|
+
|
|
417
|
+
awardPoints(wallet, alphaBonus);
|
|
418
|
+
|
|
419
|
+
console.log(chalk.green(`\n✅ Thanks for contributing! Earned ${alphaBonus} bonus points!`));
|
|
420
|
+
console.log(chalk.gray(` Contribution: ${parsed.type} hours ${parsed.startHour}-${parsed.endHour} in ${parsed.location}`));
|
|
421
|
+
|
|
422
|
+
// Show verification results
|
|
423
|
+
if (verification.verified) {
|
|
424
|
+
console.log(chalk.green(` 🎯 Verified! Confidence: ${(verification.confidence * 100).toFixed(0)}%`));
|
|
425
|
+
verification.reasons.forEach(reason => {
|
|
426
|
+
console.log(chalk.gray(` ${reason}`));
|
|
427
|
+
});
|
|
428
|
+
console.log(chalk.green(` +${alphaBonus - 10} extra points for verified contribution!`));
|
|
429
|
+
} else {
|
|
430
|
+
console.log(chalk.yellow(` ⚠️ Low confidence: ${(verification.confidence * 100).toFixed(0)}%`));
|
|
431
|
+
verification.reasons.forEach(reason => {
|
|
432
|
+
console.log(chalk.gray(` ${reason}`));
|
|
433
|
+
});
|
|
434
|
+
console.log(chalk.gray(` Tip: Contributions that match actual price patterns earn more points!`));
|
|
435
|
+
}
|
|
436
|
+
} else {
|
|
437
|
+
console.log(chalk.yellow('⚠️ Could not parse contribution.'));
|
|
438
|
+
console.log(chalk.gray(' Accepted formats:'));
|
|
439
|
+
console.log(chalk.gray(' • "7-9PM peak in Lagos"'));
|
|
440
|
+
console.log(chalk.gray(' • "1-5AM cheap in Berlin"'));
|
|
441
|
+
console.log(chalk.gray(' • "19-21 peak Mumbai" (24-hour format also works)'));
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Prepare comprehensive submission data
|
|
357
446
|
const submissionData = {
|
|
358
447
|
agent_name: agentName,
|
|
359
448
|
location: location,
|
|
360
449
|
timestamp: Date.now(),
|
|
450
|
+
|
|
451
|
+
// Summary results (for quick display)
|
|
361
452
|
results: {
|
|
362
453
|
cheapest_window: cheapestWindow.timeWindow,
|
|
363
454
|
price: cheapestWindow.price,
|
|
364
455
|
savings: savings,
|
|
365
456
|
data_points: energyData.length
|
|
366
|
-
}
|
|
457
|
+
},
|
|
458
|
+
|
|
459
|
+
// Full weather data
|
|
460
|
+
weather: {
|
|
461
|
+
location: weatherData.location,
|
|
462
|
+
forecast_summary: {
|
|
463
|
+
hours: weatherData.forecast.length,
|
|
464
|
+
temp_range: {
|
|
465
|
+
min: Math.min(...weatherData.forecast.map(f => f.temperature)),
|
|
466
|
+
max: Math.max(...weatherData.forecast.map(f => f.temperature))
|
|
467
|
+
},
|
|
468
|
+
avg_wind: weatherData.forecast.reduce((sum, f) => sum + f.windSpeed, 0) / weatherData.forecast.length,
|
|
469
|
+
avg_solar: weatherData.forecast.reduce((sum, f) => sum + f.solarRadiation, 0) / weatherData.forecast.length
|
|
470
|
+
},
|
|
471
|
+
optimal_hour_conditions: cheapestHourData?.weather || null
|
|
472
|
+
},
|
|
473
|
+
|
|
474
|
+
// Pricing insights
|
|
475
|
+
pricing: {
|
|
476
|
+
region: insights.region,
|
|
477
|
+
base_price: insights.basePrice,
|
|
478
|
+
price_range: {
|
|
479
|
+
min: insights.minPrice,
|
|
480
|
+
max: insights.maxPrice,
|
|
481
|
+
variation_percent: insights.priceRange
|
|
482
|
+
},
|
|
483
|
+
savings_potential: insights.savingsPotential,
|
|
484
|
+
country_code: countryCode
|
|
485
|
+
},
|
|
486
|
+
|
|
487
|
+
// Optimization details
|
|
488
|
+
optimization: {
|
|
489
|
+
cheapest_hour: cheapestWindow.hour,
|
|
490
|
+
time_window: cheapestWindow.timeWindow,
|
|
491
|
+
price: cheapestWindow.price,
|
|
492
|
+
savings_percent: savings,
|
|
493
|
+
reasons: cheapestHourData?.reasons || []
|
|
494
|
+
},
|
|
495
|
+
|
|
496
|
+
// Special notes from user
|
|
497
|
+
notes: specialNotes.trim() || null
|
|
367
498
|
};
|
|
368
499
|
|
|
369
500
|
// Submit to oracle
|
|
@@ -434,103 +565,6 @@ async function runQueryCycle(wallet, agentName, location, options) {
|
|
|
434
565
|
console.log(chalk.magenta(`\n⭐ Earned ${totalPointsEarned} points! (${basePoints} base${bonusPoints > 0 ? ` + ${bonusPoints} bonus` : ''})`));
|
|
435
566
|
console.log(chalk.magenta(`⭐ Total Points: ${currentPoints}`));
|
|
436
567
|
|
|
437
|
-
// Local Alpha Contribution (ask once after first run)
|
|
438
|
-
if (totalRuns === 1 && !hasBeenAskedForAlpha()) {
|
|
439
|
-
console.log(chalk.cyan('\n💡 Local Alpha Contribution - Help Improve Global Energy Data!'));
|
|
440
|
-
console.log(chalk.gray(' Share your local electricity peak time knowledge for bonus points.\n'));
|
|
441
|
-
|
|
442
|
-
// Show where to find this information
|
|
443
|
-
console.log(chalk.blue('📚 Where to find peak time information:'));
|
|
444
|
-
const sources = getInformationSources(location);
|
|
445
|
-
|
|
446
|
-
// General sources
|
|
447
|
-
sources.general.forEach(source => {
|
|
448
|
-
console.log(chalk.gray(` ${source}`));
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
// Region-specific sources
|
|
452
|
-
const regionKeys = Object.keys(sources.byRegion);
|
|
453
|
-
if (regionKeys.length > 0) {
|
|
454
|
-
console.log(chalk.blue('\n📍 For your region:'));
|
|
455
|
-
regionKeys.forEach(region => {
|
|
456
|
-
sources.byRegion[region].forEach(source => {
|
|
457
|
-
console.log(chalk.gray(` ${source}`));
|
|
458
|
-
});
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
console.log(chalk.blue('\n💬 Examples of good contributions:'));
|
|
463
|
-
console.log(chalk.gray(' • "7-9PM peak in Lagos" (evening peak)'));
|
|
464
|
-
console.log(chalk.gray(' • "1-5AM cheap in Berlin" (off-peak)'));
|
|
465
|
-
console.log(chalk.gray(' • "5-8PM peak in Mumbai" (dinner time surge)'));
|
|
466
|
-
console.log(chalk.gray(' • "2-6AM cheap in Texas" (wind energy overnight)'));
|
|
467
|
-
console.log(chalk.gray(' (Will auto-skip in 15 seconds if no input)\n'));
|
|
468
|
-
|
|
469
|
-
// Add timeout to prevent hanging
|
|
470
|
-
const alphaInput = await Promise.race([
|
|
471
|
-
question(chalk.blue('Share local peak times (or press Enter to skip): ')),
|
|
472
|
-
new Promise((resolve) => {
|
|
473
|
-
setTimeout(() => {
|
|
474
|
-
console.log(chalk.yellow('\n⏱️ Timeout - skipping alpha contribution'));
|
|
475
|
-
resolve('');
|
|
476
|
-
}, 15000); // 15 second timeout
|
|
477
|
-
})
|
|
478
|
-
]);
|
|
479
|
-
|
|
480
|
-
if (alphaInput.trim()) {
|
|
481
|
-
const parsed = parseAlphaContribution(alphaInput, location);
|
|
482
|
-
if (parsed) {
|
|
483
|
-
// Verify contribution against current pricing data
|
|
484
|
-
const verification = verifyContribution(parsed, energyData);
|
|
485
|
-
|
|
486
|
-
// Save contribution with verification status
|
|
487
|
-
const contribution = saveAlphaContribution(parsed, agentName, location, verification);
|
|
488
|
-
|
|
489
|
-
// Store for dashboard submission
|
|
490
|
-
pendingAlphaContribution = {
|
|
491
|
-
type: parsed.type,
|
|
492
|
-
startHour: parsed.startHour,
|
|
493
|
-
endHour: parsed.endHour,
|
|
494
|
-
location: parsed.location,
|
|
495
|
-
verified: verification.verified,
|
|
496
|
-
confidence: verification.confidence,
|
|
497
|
-
verificationReasons: verification.reasons
|
|
498
|
-
};
|
|
499
|
-
|
|
500
|
-
// Calculate bonus (higher for verified contributions)
|
|
501
|
-
const alphaBonus = calculateAlphaBonus(parsed, verification);
|
|
502
|
-
|
|
503
|
-
awardPoints(wallet, alphaBonus);
|
|
504
|
-
|
|
505
|
-
console.log(chalk.green(`\n✅ Thanks for contributing! Earned ${alphaBonus} bonus points!`));
|
|
506
|
-
console.log(chalk.gray(` Contribution: ${parsed.type} hours ${parsed.startHour}-${parsed.endHour} in ${parsed.location}`));
|
|
507
|
-
|
|
508
|
-
// Show verification results
|
|
509
|
-
if (verification.verified) {
|
|
510
|
-
console.log(chalk.green(` 🎯 Verified! Confidence: ${(verification.confidence * 100).toFixed(0)}%`));
|
|
511
|
-
verification.reasons.forEach(reason => {
|
|
512
|
-
console.log(chalk.gray(` ${reason}`));
|
|
513
|
-
});
|
|
514
|
-
console.log(chalk.green(` +${alphaBonus - 10} extra points for verified contribution!`));
|
|
515
|
-
} else {
|
|
516
|
-
console.log(chalk.yellow(` ⚠️ Low confidence: ${(verification.confidence * 100).toFixed(0)}%`));
|
|
517
|
-
verification.reasons.forEach(reason => {
|
|
518
|
-
console.log(chalk.gray(` ${reason}`));
|
|
519
|
-
});
|
|
520
|
-
console.log(chalk.gray(` Tip: Contributions that match actual price patterns earn more points!`));
|
|
521
|
-
}
|
|
522
|
-
} else {
|
|
523
|
-
console.log(chalk.yellow('⚠️ Could not parse contribution.'));
|
|
524
|
-
console.log(chalk.gray(' Accepted formats:'));
|
|
525
|
-
console.log(chalk.gray(' • "7-9PM peak in Lagos"'));
|
|
526
|
-
console.log(chalk.gray(' • "1-5AM cheap in Berlin"'));
|
|
527
|
-
console.log(chalk.gray(' • "19-21 peak Mumbai" (24-hour format also works)'));
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
markAskedForAlpha();
|
|
532
|
-
}
|
|
533
|
-
|
|
534
568
|
// Show alpha insights if available
|
|
535
569
|
if (totalRuns === 1) {
|
|
536
570
|
const alphaInsights = getAlphaInsights(location);
|