decharge-scout 4.0.2 → 4.0.5

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/index.js CHANGED
@@ -33,7 +33,7 @@ dotenv.config({ path: path.join(process.cwd(), '.env') });
33
33
  // Import modules
34
34
  import { startWalletServer, openWalletConnection, waitForWalletConnection, getConnectedWallet } from './src/wallet-server.js';
35
35
  import { setConnectedWallet, getBalance, checkBalance, mockStake, refundStake } from './src/browser-wallet.js';
36
- import { getWeatherForLocation } from './src/weather-data.js';
36
+ import { getWeatherForLocation, getFallbackCoordinates } from './src/weather-data.js';
37
37
  import { generateSmartPricing, getPricingInsights, getRegionalPricing } from './src/smart-pricing.js';
38
38
  import { findCheapestWindow, calculateSavings } from './src/optimizer.js';
39
39
  import { submitToOracle } from './src/oracle.js';
@@ -288,7 +288,38 @@ async function runQueryCycle(wallet, agentName, location, options) {
288
288
 
289
289
  } catch (error) {
290
290
  weatherSpinner.fail(chalk.red('Failed to fetch weather data'));
291
- throw new Error(`Unable to generate pricing simulation: ${error.message}`);
291
+
292
+ // Check if we have fallback coordinates available
293
+ const fallbackCoords = getFallbackCoordinates(location);
294
+
295
+ console.log(chalk.yellow(`\n⚠️ Could not geocode location: "${location}"`));
296
+ console.log(chalk.yellow(` Error: ${error.message}`));
297
+
298
+ if (fallbackCoords) {
299
+ console.log(chalk.blue(`\n💡 We have fallback coordinates for: ${fallbackCoords.name}, ${fallbackCoords.country}`));
300
+ }
301
+
302
+ console.log(chalk.blue(`\nOptions:`));
303
+ console.log(chalk.gray(` 1. Try a different location (e.g., just "Hyderabad" or "Hyderabad, India")`));
304
+ if (fallbackCoords) {
305
+ console.log(chalk.gray(` 2. Use fallback coordinates for ${fallbackCoords.name}`));
306
+ }
307
+ console.log(chalk.gray(` 3. Exit and restart\n`));
308
+
309
+ const userChoice = await question(chalk.blue('Enter new location (or press Enter to exit): '));
310
+
311
+ if (!userChoice.trim()) {
312
+ console.log(chalk.yellow('Exiting...'));
313
+ throw new Error('User chose to exit');
314
+ }
315
+
316
+ // Update location and retry this cycle
317
+ location = userChoice.trim();
318
+ console.log(chalk.green(`✓ Updated location to: ${location}`));
319
+ console.log(chalk.gray('Retrying...\n'));
320
+
321
+ // Retry with new location - this will loop back and try again
322
+ continue;
292
323
  }
293
324
 
294
325
  // Run optimization
@@ -432,9 +463,19 @@ async function runQueryCycle(wallet, agentName, location, options) {
432
463
  console.log(chalk.gray(' • "7-9PM peak in Lagos" (evening peak)'));
433
464
  console.log(chalk.gray(' • "1-5AM cheap in Berlin" (off-peak)'));
434
465
  console.log(chalk.gray(' • "5-8PM peak in Mumbai" (dinner time surge)'));
435
- console.log(chalk.gray(' • "2-6AM cheap in Texas" (wind energy overnight)\n'));
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'));
436
468
 
437
- const alphaInput = await question(chalk.blue('Share local peak times (or press Enter to skip): '));
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
+ ]);
438
479
 
439
480
  if (alphaInput.trim()) {
440
481
  const parsed = parseAlphaContribution(alphaInput, location);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "decharge-scout",
3
- "version": "4.0.2",
3
+ "version": "4.0.5",
4
4
  "description": "Global Energy Scout - Weather-powered intelligent energy price forecasting with Solana integration",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -12,34 +12,57 @@ import fetch from 'node-fetch';
12
12
  /**
13
13
  * Get coordinates from location string
14
14
  * Simple geocoding using Open-Meteo's geocoding API (also free!)
15
+ * Tries multiple search strategies for better success rate
15
16
  */
16
17
  export async function getCoordinatesFromLocation(location) {
17
- try {
18
- const locationEncoded = encodeURIComponent(location);
19
- const url = `https://geocoding-api.open-meteo.com/v1/search?name=${locationEncoded}&count=1&language=en&format=json`;
20
-
21
- const response = await fetch(url);
22
- if (!response.ok) {
23
- throw new Error(`Geocoding failed: ${response.status}`);
18
+ // Try multiple search strategies
19
+ const searchTerms = [
20
+ location, // Original: "Hyderabad, TS, IN"
21
+ location.split(',')[0].trim(), // Just city: "Hyderabad"
22
+ location.replace(/,\s*[A-Z]{2}\s*,/, ','), // Remove state: "Hyderabad, IN"
23
+ ];
24
+
25
+ // Remove duplicates
26
+ const uniqueSearchTerms = [...new Set(searchTerms)];
27
+
28
+ let lastError = null;
29
+
30
+ for (const searchTerm of uniqueSearchTerms) {
31
+ try {
32
+ const locationEncoded = encodeURIComponent(searchTerm);
33
+ const url = `https://geocoding-api.open-meteo.com/v1/search?name=${locationEncoded}&count=5&language=en&format=json`;
34
+
35
+ const response = await fetch(url);
36
+ if (!response.ok) {
37
+ lastError = new Error(`Geocoding failed: ${response.status}`);
38
+ continue;
39
+ }
40
+
41
+ const data = await response.json();
42
+
43
+ if (!data.results || data.results.length === 0) {
44
+ lastError = new Error(`Location "${searchTerm}" not found`);
45
+ continue;
46
+ }
47
+
48
+ // Use first result
49
+ const result = data.results[0];
50
+ console.log(`✓ Geocoded "${searchTerm}" → ${result.name}, ${result.country} (${result.latitude}, ${result.longitude})`);
51
+
52
+ return {
53
+ latitude: result.latitude,
54
+ longitude: result.longitude,
55
+ name: result.name,
56
+ country: result.country,
57
+ timezone: result.timezone || 'auto'
58
+ };
59
+ } catch (error) {
60
+ lastError = error;
61
+ continue;
24
62
  }
25
-
26
- const data = await response.json();
27
-
28
- if (!data.results || data.results.length === 0) {
29
- throw new Error('Location not found');
30
- }
31
-
32
- const result = data.results[0];
33
- return {
34
- latitude: result.latitude,
35
- longitude: result.longitude,
36
- name: result.name,
37
- country: result.country,
38
- timezone: result.timezone || 'UTC'
39
- };
40
- } catch (error) {
41
- throw new Error(`Failed to geocode location: ${error.message}`);
42
63
  }
64
+
65
+ throw new Error(`Failed to geocode location after trying: ${uniqueSearchTerms.join(', ')}. Last error: ${lastError?.message}`);
43
66
  }
44
67
 
45
68
  /**
@@ -95,23 +118,47 @@ export async function fetchWeatherForecast(latitude, longitude, timezone = 'auto
95
118
  }
96
119
  }
97
120
 
121
+ /**
122
+ * Known coordinates for common cities (fallback)
123
+ */
124
+ const KNOWN_CITIES = {
125
+ 'hyderabad': { latitude: 17.385, longitude: 78.487, name: 'Hyderabad', country: 'India', timezone: 'Asia/Kolkata' },
126
+ 'mumbai': { latitude: 19.076, longitude: 72.877, name: 'Mumbai', country: 'India', timezone: 'Asia/Kolkata' },
127
+ 'delhi': { latitude: 28.704, longitude: 77.102, name: 'Delhi', country: 'India', timezone: 'Asia/Kolkata' },
128
+ 'bangalore': { latitude: 12.972, longitude: 77.594, name: 'Bangalore', country: 'India', timezone: 'Asia/Kolkata' },
129
+ 'chennai': { latitude: 13.083, longitude: 80.270, name: 'Chennai', country: 'India', timezone: 'Asia/Kolkata' },
130
+ 'lagos': { latitude: 6.524, longitude: 3.379, name: 'Lagos', country: 'Nigeria', timezone: 'Africa/Lagos' },
131
+ 'london': { latitude: 51.509, longitude: -0.118, name: 'London', country: 'United Kingdom', timezone: 'Europe/London' },
132
+ 'new york': { latitude: 40.713, longitude: -74.006, name: 'New York', country: 'United States', timezone: 'America/New_York' },
133
+ 'los angeles': { latitude: 34.052, longitude: -118.244, name: 'Los Angeles', country: 'United States', timezone: 'America/Los_Angeles' },
134
+ 'chicago': { latitude: 41.878, longitude: -87.630, name: 'Chicago', country: 'United States', timezone: 'America/Chicago' },
135
+ 'houston': { latitude: 29.760, longitude: -95.369, name: 'Houston', country: 'United States', timezone: 'America/Chicago' },
136
+ 'dallas': { latitude: 32.776, longitude: -96.797, name: 'Dallas', country: 'United States', timezone: 'America/Chicago' },
137
+ };
138
+
98
139
  /**
99
140
  * Get weather data for a location
141
+ * Throws error if geocoding fails - caller should handle user interaction
100
142
  */
101
143
  export async function getWeatherForLocation(location) {
102
- try {
103
- // Get coordinates
104
- const coords = await getCoordinatesFromLocation(location);
105
- console.log(`📍 Location: ${coords.name}, ${coords.country} (${coords.latitude}, ${coords.longitude})`);
144
+ // Try geocoding - throw error if fails
145
+ const coords = await getCoordinatesFromLocation(location);
146
+ console.log(`📍 Location: ${coords.name}, ${coords.country} (${coords.latitude}, ${coords.longitude})`);
106
147
 
107
- // Get weather forecast
108
- const forecast = await fetchWeatherForecast(coords.latitude, coords.longitude, coords.timezone);
148
+ // Get weather forecast
149
+ const forecast = await fetchWeatherForecast(coords.latitude, coords.longitude, coords.timezone);
109
150
 
110
- return {
111
- location: coords,
112
- forecast
113
- };
114
- } catch (error) {
115
- throw new Error(`Failed to get weather for location: ${error.message}`);
116
- }
151
+ return {
152
+ location: coords,
153
+ forecast
154
+ };
155
+ }
156
+
157
+ /**
158
+ * Get fallback coordinates for known cities
159
+ * Returns null if city not in database
160
+ */
161
+ export function getFallbackCoordinates(location) {
162
+ const cityKey = location.split(',')[0].trim().toLowerCase();
163
+ return KNOWN_CITIES[cityKey] || null;
117
164
  }