openclaw-smartmeter 0.5.2 → 0.5.3

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.
@@ -5,6 +5,7 @@
5
5
 
6
6
  /* ─── Globals ─── */
7
7
  const API_BASE_URL = `http://localhost:${window.__SMARTMETER_API_PORT || 3001}`;
8
+ const STORAGE_PREFIX = window.__SMARTMETER_USER ? `smartmeter_${window.__SMARTMETER_USER}_` : 'smartmeter_';
8
9
  let analysisData = null;
9
10
  let modelChart = null;
10
11
  let taskChart = null;
@@ -901,7 +902,7 @@ async function validateInlineApiKey() {
901
902
  btn.textContent = 'Save & Validate';
902
903
 
903
904
  if (validated) {
904
- localStorage.setItem('smartmeter_openrouter_key', key);
905
+ localStorage.setItem(STORAGE_PREFIX + 'openrouter_key', key);
905
906
  showStatus('✅ API key saved and validated!', 'success');
906
907
 
907
908
  // Show balance section, hide key input
@@ -959,7 +960,7 @@ function showApiKeyInput() {
959
960
 
960
961
  /** On init, check if key is already stored and auto-show balance */
961
962
  async function initGetStartedCard() {
962
- const stored = localStorage.getItem('smartmeter_openrouter_key');
963
+ const stored = localStorage.getItem(STORAGE_PREFIX + 'openrouter_key');
963
964
  if (!stored) return;
964
965
 
965
966
  // Pre-fill the input
@@ -1274,7 +1275,7 @@ async function fetchOpenRouterUsage() {
1274
1275
  function openConfigModal() {
1275
1276
  document.getElementById('configModal').style.display = 'flex';
1276
1277
  const input = document.getElementById('apiKeyInput');
1277
- const stored = localStorage.getItem('smartmeter_openrouter_key');
1278
+ const stored = localStorage.getItem(STORAGE_PREFIX + 'openrouter_key');
1278
1279
  if (stored && !input.value) input.value = stored;
1279
1280
  input.focus();
1280
1281
  const status = document.getElementById('configStatus');
@@ -1344,7 +1345,7 @@ async function saveApiKey() {
1344
1345
  }
1345
1346
 
1346
1347
  if (validated) {
1347
- localStorage.setItem('smartmeter_openrouter_key', key);
1348
+ localStorage.setItem(STORAGE_PREFIX + 'openrouter_key', key);
1348
1349
  showStatus('✅ API key saved and validated!', 'success');
1349
1350
  setTimeout(() => {
1350
1351
  closeConfigModal();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-smartmeter",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
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": {
@@ -92,9 +92,9 @@ export async function cmdAnalyze(opts = {}) {
92
92
  const apiServer = await startApiServer({ port: apiPort });
93
93
  const actualApiPort = apiServer.server.address().port;
94
94
 
95
- // Start static file server in background
95
+ // Start static file server in background (pass API port for injection)
96
96
  console.log("✓ Starting dashboard server...");
97
- const staticServer = await startStaticFileServer(deployer.canvasDir, port);
97
+ const staticServer = await startStaticFileServer(deployer.canvasDir, port, { apiPort: actualApiPort });
98
98
  const actualPort = staticServer.port;
99
99
 
100
100
  const url = `http://localhost:${actualPort}`;
@@ -885,10 +885,15 @@ async function findAvailablePort(startPort, maxAttempts = 10) {
885
885
  return null;
886
886
  }
887
887
 
888
- async function startStaticFileServer(directory, port) {
888
+ async function startStaticFileServer(directory, port, options = {}) {
889
889
  const { createServer } = await import("node:http");
890
890
  const { readFile, stat } = await import("node:fs/promises");
891
891
  const { join, extname } = await import("node:path");
892
+ const { userInfo } = await import("node:os");
893
+
894
+ const apiPort = options.apiPort || 3001;
895
+ const osUser = (() => { try { return userInfo().username; } catch { return ''; } })();
896
+ const portScript = `<script>window.__SMARTMETER_API_PORT=${apiPort};window.__SMARTMETER_USER=${JSON.stringify(osUser)};</script>`;
892
897
 
893
898
  const mimeTypes = {
894
899
  '.html': 'text/html',
@@ -916,10 +921,20 @@ async function startStaticFileServer(directory, port) {
916
921
  }
917
922
 
918
923
  // Read and serve file
919
- const content = await readFile(filePath);
924
+ let content = await readFile(filePath);
920
925
  const ext = extname(filePath);
921
926
  const mimeType = mimeTypes[ext] || 'application/octet-stream';
922
927
 
928
+ // Inject API port and OS username into index.html so dashboard connects
929
+ // to the correct API server and scopes localStorage per user
930
+ if (ext === '.html') {
931
+ let html = content.toString('utf-8');
932
+ html = html.replace('</head>', `${portScript}\n</head>`);
933
+ res.writeHead(200, { 'Content-Type': mimeType });
934
+ res.end(html);
935
+ return;
936
+ }
937
+
923
938
  res.writeHead(200, { 'Content-Type': mimeType });
924
939
  res.end(content);
925
940
  } catch (error) {