openclaw-smartmeter 0.4.0 → 0.4.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.
@@ -111,16 +111,17 @@ function normalizeApiData(api) {
111
111
  /* ─── Render All ─── */
112
112
  function renderAll() {
113
113
  if (!analysisData) return;
114
- updateKPIs();
115
- updateMetrics();
116
- updateCharts();
117
- updateRecommendations();
118
- updateModelRecommendations();
119
- updateOtherRecommendations();
120
- updateBudgetControls();
121
- updateModelDetails();
122
- updateLastUpdated();
123
- checkCostDataNotice();
114
+ const safe = fn => { try { fn(); } catch (e) { console.error(`[SmartMeter] ${fn.name} error:`, e); } };
115
+ safe(updateKPIs);
116
+ safe(updateMetrics);
117
+ safe(updateCharts);
118
+ safe(updateRecommendations);
119
+ safe(updateModelRecommendations);
120
+ safe(updateOtherRecommendations);
121
+ safe(updateBudgetControls);
122
+ safe(updateModelDetails);
123
+ safe(updateLastUpdated);
124
+ safe(checkCostDataNotice);
124
125
  }
125
126
 
126
127
  /* ─── KPIs ─── */
@@ -440,7 +441,7 @@ function updateModelRecommendations() {
440
441
  ${alt.isRecommended ? '<span class="model-tag best-tag">RECOMMENDED</span>' : ''}
441
442
  </div>
442
443
  <div class="model-alt-meta">
443
- Quality: ${renderQualityDots(alt.quality_score)} · ${escHtml(alt.speed)} · Best for: ${alt.best_for.join(', ')}
444
+ Quality: ${renderQualityDots(alt.quality_score)} · ${escHtml(alt.speed || '')} · Best for: ${(alt.best_for || []).join(', ')}
444
445
  </div>
445
446
  </div>
446
447
  <div class="model-alt-cost">
@@ -877,10 +878,13 @@ async function fetchOpenRouterUsage() {
877
878
  /* ─── Config Modal ─── */
878
879
  function openConfigModal() {
879
880
  document.getElementById('configModal').style.display = 'flex';
880
- document.getElementById('apiKeyInput').focus();
881
+ const input = document.getElementById('apiKeyInput');
882
+ const stored = localStorage.getItem('smartmeter_openrouter_key');
883
+ if (stored && !input.value) input.value = stored;
884
+ input.focus();
881
885
  const status = document.getElementById('configStatus');
882
886
  status.className = 'config-status';
883
- status.style.display = 'none';
887
+ status.style.removeProperty('display');
884
888
  }
885
889
  function closeConfigModal() {
886
890
  document.getElementById('configModal').style.display = 'none';
@@ -890,22 +894,28 @@ async function saveApiKey() {
890
894
  const key = document.getElementById('apiKeyInput').value.trim();
891
895
  const status = document.getElementById('configStatus');
892
896
 
897
+ function showStatus(msg, type) {
898
+ status.textContent = msg;
899
+ status.className = 'config-status ' + type;
900
+ status.style.removeProperty('display');
901
+ }
902
+
893
903
  if (!key) {
894
- status.textContent = 'Please enter an API key.';
895
- status.className = 'config-status error';
904
+ showStatus('Please enter an API key.', 'error');
896
905
  return;
897
906
  }
898
907
 
899
908
  if (!key.startsWith('sk-or-')) {
900
- status.textContent = 'Invalid format — key should start with sk-or-';
901
- status.className = 'config-status error';
909
+ showStatus('Invalid format — key should start with sk-or-', 'error');
902
910
  return;
903
911
  }
904
912
 
905
- status.textContent = 'Validating…';
906
- status.className = 'config-status';
907
- status.style.display = 'block';
913
+ showStatus('Validating…', 'validating');
914
+
915
+ let validated = false;
916
+ let errorMsg = '';
908
917
 
918
+ // Try API server first
909
919
  try {
910
920
  const res = await fetch(`${API_BASE_URL}/api/config/openrouter-key`, {
911
921
  method: 'POST',
@@ -914,20 +924,40 @@ async function saveApiKey() {
914
924
  });
915
925
  const json = await res.json();
916
926
  if (json.success) {
917
- status.textContent = '✅ API key saved and validated!';
918
- status.className = 'config-status success';
919
- setTimeout(() => {
920
- closeConfigModal();
921
- fetchOpenRouterUsage();
922
- navigateTo('openrouter');
923
- }, 1200);
927
+ validated = true;
924
928
  } else {
925
- status.textContent = `❌ ${json.error || 'Validation failed'}`;
926
- status.className = 'config-status error';
929
+ errorMsg = json.error || 'Validation failed';
927
930
  }
928
- } catch (err) {
929
- status.textContent = `❌ Network error: ${err.message}`;
930
- status.className = 'config-status error';
931
+ } catch (_) {
932
+ // API server not available — validate directly against OpenRouter
933
+ try {
934
+ const res = await fetch('https://openrouter.ai/api/v1/auth/key', {
935
+ headers: { 'Authorization': `Bearer ${key}` }
936
+ });
937
+ if (res.ok) {
938
+ const data = await res.json();
939
+ validated = !!(data && data.data);
940
+ if (!validated) errorMsg = 'Key not recognized by OpenRouter';
941
+ } else if (res.status === 401 || res.status === 403) {
942
+ errorMsg = 'Invalid API key — authentication failed';
943
+ } else {
944
+ errorMsg = `OpenRouter returned status ${res.status}`;
945
+ }
946
+ } catch (e2) {
947
+ errorMsg = 'Could not reach OpenRouter to validate — check your connection';
948
+ }
949
+ }
950
+
951
+ if (validated) {
952
+ localStorage.setItem('smartmeter_openrouter_key', key);
953
+ showStatus('✅ API key saved and validated!', 'success');
954
+ setTimeout(() => {
955
+ closeConfigModal();
956
+ fetchOpenRouterUsage();
957
+ navigateTo('openrouter');
958
+ }, 1200);
959
+ } else {
960
+ showStatus(`❌ ${errorMsg}`, 'error');
931
961
  }
932
962
  }
933
963
 
@@ -860,6 +860,11 @@ a:hover { color: var(--accent-hover); }
860
860
  background: var(--green-subtle);
861
861
  color: var(--green);
862
862
  }
863
+ .config-status.validating {
864
+ display: block;
865
+ background: var(--indigo-subtle, rgba(99,102,241,.08));
866
+ color: var(--text-secondary);
867
+ }
863
868
 
864
869
  /* Code block */
865
870
  .code-block {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-smartmeter",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "AI cost optimization for OpenClaw - analyze usage and reduce costs by 48%",
5
5
  "main": "src/cli/index.js",
6
6
  "bin": {