iobroker.eos-admin 7.9.95 → 7.9.96

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.
Files changed (51) hide show
  1. package/INSTALL_TEST_V7.9.96.md +11 -0
  2. package/MERGE_UPDATE_README_V7.9.96.md +5 -0
  3. package/NEXOWATT_EOS_BUILD_INFO.json +18 -23
  4. package/NexoWatt_EOS_Admin_EMS_OVERVIEW_MERGE_VALIDATION.md +44 -0
  5. package/PUBLISH_STABLE_V7.9.96.md +10 -0
  6. package/RC73_EOS_ADMIN_MERGE_VALIDATION.md +78 -0
  7. package/README_EMS_OVERVIEW_MERGE.md +45 -0
  8. package/README_STABILITY_V7.9.96.md +11 -0
  9. package/RELEASE_ACCEPTANCE_V7.9.96.md +10 -0
  10. package/RELEASE_NOTES_V7.9.96.md +12 -0
  11. package/VALIDATION_EMS_OVERVIEW_MERGE.md +35 -0
  12. package/adminWww/assets/Intro-DkwRiz1n-v84.js +2 -2
  13. package/adminWww/assets/index-D2ymscJA-v84.js +1 -1
  14. package/adminWww/css/eos-ems-overview.css +62 -0
  15. package/adminWww/css/nexowatt-native-shell.css +111 -13
  16. package/adminWww/index.html +15 -13
  17. package/adminWww/js/eos-assistant.js +1 -1
  18. package/adminWww/js/eos-ems-overview.js +140 -0
  19. package/adminWww/js/eos-role-bootstrap.js +4 -4
  20. package/adminWww/js/eos-role-ui.js +34 -38
  21. package/adminWww/js/nexowatt-native-shell.js +11 -6
  22. package/adminWww/static/js/nexowatt-stable-login.js +12 -12
  23. package/adminWww/static/js/nexowatt-stable-v96.js +82 -0
  24. package/io-package.json +10 -5
  25. package/package.json +38 -12
  26. package/tools/nexowatt-account-management-selftest.cjs +60 -0
  27. package/tools/nexowatt-apply-ems-overview.cjs +115 -0
  28. package/tools/nexowatt-clean-legacy-runtime.cjs +151 -0
  29. package/tools/nexowatt-ems-overview-selftest.cjs +35 -0
  30. package/tools/nexowatt-ensure-release-defaults.cjs +72 -0
  31. package/tools/nexowatt-generate-eos-admin-repo-entry.cjs +37 -0
  32. package/tools/nexowatt-generate-repo-entry.cjs +3 -0
  33. package/tools/nexowatt-internal-reserve-selftest.cjs +55 -0
  34. package/tools/nexowatt-login-layout-selftest.cjs +83 -0
  35. package/tools/nexowatt-merge-update-selftest.cjs +69 -0
  36. package/tools/nexowatt-modern-ui-selftest.cjs +49 -0
  37. package/tools/nexowatt-patch-repo.cjs +54 -0
  38. package/tools/nexowatt-publish-channel-guard.cjs +58 -0
  39. package/tools/nexowatt-release-defaults-selftest.cjs +29 -0
  40. package/tools/nexowatt-runtime-cleanup-selftest.cjs +47 -0
  41. package/tools/nexowatt-stable-v96-selftest.cjs +13 -0
  42. package/tools/nexowatt-sync-release-version.cjs +181 -0
  43. package/tools/nexowatt-ui-overview-runtime-selftest.cjs +16 -0
  44. package/tools/nexowatt-validate-package.cjs +277 -0
  45. package/tools/nexowatt-version-sync-selftest.cjs +86 -0
  46. package/INSTALL_TEST_V7.9.95.md +0 -15
  47. package/NEXOWATT_EOS_STABLE_7.9.94.md +0 -16
  48. package/PUBLISH_STABLE_V7.9.95.md +0 -16
  49. package/README_STABILITY_V7.9.95.md +0 -12
  50. package/RELEASE_ACCEPTANCE_V7.9.95.md +0 -11
  51. package/RELEASE_NOTES_V7.9.95.md +0 -12
@@ -1,4 +1,4 @@
1
- /* NexoWatt EOS stable runtime v94 */
1
+ /* NexoWatt EOS stable runtime v96 */
2
2
  (() => {
3
3
  'use strict';
4
4
  const MANAGED_USERS = new Set(['installer', 'guest', 'user']);
@@ -21,8 +21,8 @@
21
21
  };
22
22
  const markLogin = () => {
23
23
  if (!isLogin()) return;
24
- document.documentElement.classList.add('nw-eos-login-v94');
25
- document.body?.classList.add('nw-eos-login-v94');
24
+ document.documentElement.classList.add('nw-eos-login-v96');
25
+ document.body?.classList.add('nw-eos-login-v96');
26
26
  const u=findInput('user'), p=findInput('password'), submit=findSubmit();
27
27
  if (u && p) {
28
28
  let card=commonAncestor(u,p);
@@ -44,7 +44,7 @@
44
44
  submit.setAttribute('aria-disabled','true');
45
45
  }
46
46
  };
47
- if(!u.dataset.nwV94){u.dataset.nwV94='1';u.addEventListener('input',sync);p.addEventListener('input',sync);sync();}
47
+ if(!u.dataset.nwV96){u.dataset.nwV96='1';u.addEventListener('input',sync);p.addEventListener('input',sync);sync();}
48
48
  }
49
49
  };
50
50
  const hideLegacyUi = () => {
@@ -60,16 +60,16 @@
60
60
  };
61
61
  const apply=()=>{markLogin();hideLegacyUi();};
62
62
  const style=document.createElement('style');
63
- style.id='nw-eos-stable-v94-css';
63
+ style.id='nw-eos-stable-v96-css';
64
64
  style.textContent=`
65
- html.nw-eos-login-v94,body.nw-eos-login-v94{min-height:100%;overflow-x:hidden!important;overflow-y:auto!important}
66
- .nw-eos-login-v94 [data-nw-login-shell="true"]{width:min(410px,calc(100vw - 28px))!important;min-height:540px!important;height:auto!important;max-height:none!important;overflow:visible!important;box-sizing:border-box!important;padding:22px!important}
67
- .nw-eos-login-v94 [data-nw-login-card="true"]{width:min(354px,calc(100vw - 56px))!important;min-height:474px!important;height:auto!important;max-height:none!important;overflow:visible!important;box-sizing:border-box!important;padding-bottom:26px!important}
68
- .nw-eos-login-v94 [data-nw-login-card="true"] *{scrollbar-width:none}
69
- .nw-eos-login-v94 [data-nw-login-card="true"] *::-webkit-scrollbar{display:none!important;width:0!important;height:0!important}
70
- .nw-eos-login-v94 [data-nw-login-card="true"] [role="alert"]{position:static!important;max-height:none!important;overflow:visible!important;white-space:normal!important}
65
+ html.nw-eos-login-v96,body.nw-eos-login-v96{min-height:100%;margin:0!important;overflow-x:hidden!important;overflow-y:auto!important}
66
+ .nw-eos-login-v96 [data-nw-login-shell="true"]{width:min(520px,calc(100vw - 28px))!important;min-height:590px!important;height:auto!important;max-height:none!important;overflow:visible!important;box-sizing:border-box!important;padding:22px!important}
67
+ .nw-eos-login-v96 [data-nw-login-card="true"]{width:min(460px,calc(100vw - 56px))!important;min-height:540px!important;height:auto!important;max-height:none!important;overflow:visible!important;box-sizing:border-box!important;padding-bottom:26px!important}
68
+ .nw-eos-login-v96 [data-nw-login-card="true"] *{scrollbar-width:none}
69
+ .nw-eos-login-v96 [data-nw-login-card="true"] *::-webkit-scrollbar{display:none!important;width:0!important;height:0!important}
70
+ .nw-eos-login-v96 [data-nw-login-card="true"] [role="alert"]{position:static!important;max-height:none!important;overflow:visible!important;white-space:normal!important}
71
71
  [data-eos-assist],.eos-assist,.nexowatt-eos-assist{display:none!important}
72
- @media(max-height:620px){.nw-eos-login-v94 [data-nw-login-shell="true"]{min-height:0!important;margin:16px auto!important}.nw-eos-login-v94 [data-nw-login-card="true"]{min-height:0!important}}
72
+ @media(max-height:620px){.nw-eos-login-v96 [data-nw-login-shell="true"]{min-height:0!important;margin:16px auto!important}.nw-eos-login-v96 [data-nw-login-card="true"]{min-height:0!important}}
73
73
  `;
74
74
  (document.head||document.documentElement).appendChild(style);
75
75
  const observer=new MutationObserver(()=>requestAnimationFrame(apply));
@@ -0,0 +1,82 @@
1
+ (() => {
2
+ 'use strict';
3
+ const VERSION = 'v96-spacious-login-first-password-fix';
4
+ const MANAGED_USERS = new Set(['installer', 'guest', 'user']);
5
+ const ASSIST_TEXT = /EOS\s*Assist/i;
6
+ const normalizeUser = value => String(value || '').trim().toLowerCase().replace(/^system\.user\./, '');
7
+ const styleId = 'nexowatt-stable-v96-style';
8
+
9
+ const ensureStyle = () => {
10
+ if (document.getElementById(styleId)) return;
11
+ const style = document.createElement('style');
12
+ style.id = styleId;
13
+ style.textContent = `
14
+ html.eos-login,html.eos-login body{margin:0!important;overflow-x:hidden!important;overflow-y:auto!important}
15
+ html.eos-login main.MuiPaper-root{box-sizing:border-box!important}
16
+ html.eos-login main.MuiPaper-root::before{width:min(520px,calc(100vw - 24px))!important;min-height:590px!important;height:min(640px,calc(100dvh - 24px))!important}
17
+ html.eos-login .eos-login-card-modern,
18
+ html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#username),
19
+ html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#password),
20
+ html.eos-login [data-nw-login-card="true"]{
21
+ width:min(460px,calc(100vw - 56px))!important;
22
+ min-height:540px!important;
23
+ height:auto!important;
24
+ max-width:460px!important;
25
+ max-height:none!important;
26
+ padding:34px 36px 32px!important;
27
+ overflow:visible!important;
28
+ box-sizing:border-box!important
29
+ }
30
+ html.eos-login .MuiAlert-root,html.eos-login [role="alert"]{position:static!important;width:100%!important;max-width:100%!important;max-height:none!important;overflow:visible!important;white-space:normal!important;overflow-wrap:anywhere!important;flex-shrink:0!important}
31
+ html.eos-login .eos-login-card-modern *,html.eos-login [data-nw-login-card="true"] *{box-sizing:border-box;max-width:100%}
32
+ html.eos-assist-disabled .eos-assist-root,html.eos-assist-disabled .eos-assist-header-root,[data-eos-assist-root]{display:none!important}
33
+ @media(max-width:560px){html.eos-login .eos-login-card-modern,html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#username),html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#password),html.eos-login [data-nw-login-card="true"]{width:calc(100vw - 38px)!important;max-width:none!important;min-height:0!important;padding:26px 22px!important}}
34
+ @media(max-height:690px){html.eos-login .eos-login-card-modern,html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#username),html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#password),html.eos-login [data-nw-login-card="true"]{min-height:0!important}}
35
+ `;
36
+ document.head.appendChild(style);
37
+ };
38
+
39
+ const findCard = (username, password) => username?.closest?.('.MuiPaper-root,form,main,section')
40
+ || password?.closest?.('.MuiPaper-root,form,main,section');
41
+ const findSubmit = card => card?.querySelector?.('button[type="submit"]')
42
+ || Array.from(card?.querySelectorAll?.('button') || []).find(button => /anmelden|login|sign in|aanmelden/i.test(button.textContent || ''));
43
+
44
+ const apply = () => {
45
+ ensureStyle();
46
+ document.documentElement.classList.add('eos-assist-disabled');
47
+ document.querySelectorAll('.eos-assist-root,.eos-assist-header-root,[data-eos-assist-root],.eos-passwordless-launcher')
48
+ .forEach(element => element.remove());
49
+ const username = document.querySelector('input#username,input[name="username"]');
50
+ const password = document.querySelector('input#password,input[name="password"]');
51
+ const name = normalizeUser(username?.value);
52
+ const pass = String(password?.value || '');
53
+ const card = findCard(username, password);
54
+ const submit = findSubmit(card);
55
+ if (submit && MANAGED_USERS.has(name) && !pass) {
56
+ submit.disabled = true;
57
+ submit.classList.add('Mui-disabled');
58
+ submit.setAttribute('aria-disabled', 'true');
59
+ }
60
+ document.querySelectorAll('body *').forEach(element => {
61
+ if (element.children.length) return;
62
+ const value = element.textContent || '';
63
+ if (/\[iobroker\.eos-admin@[^\]]+\]/i.test(value)) element.textContent = value.replace(/\[iobroker\.eos-admin@[^\]]+\]/ig, '[NexoWatt]');
64
+ if (ASSIST_TEXT.test(value) && element.closest?.('.eos-assist-root,.eos-assist-header-root')) element.closest('.eos-assist-root,.eos-assist-header-root')?.remove();
65
+ });
66
+ };
67
+
68
+ let scheduled = false;
69
+ const schedule = () => {
70
+ if (scheduled) return;
71
+ scheduled = true;
72
+ queueMicrotask(() => { scheduled = false; apply(); });
73
+ };
74
+ const start = () => {
75
+ apply();
76
+ new MutationObserver(schedule).observe(document.documentElement, { subtree: true, childList: true, characterData: true });
77
+ document.addEventListener('input', schedule, true);
78
+ document.addEventListener('change', schedule, true);
79
+ };
80
+ document.readyState === 'loading' ? document.addEventListener('DOMContentLoaded', start, { once: true }) : start();
81
+ window.NEXOWATT_EOS_STABLE_V96 = { version: VERSION, refresh: apply };
82
+ })();
package/io-package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "eos-admin",
4
- "version": "7.9.95",
4
+ "version": "7.9.96",
5
5
  "titleLang": {
6
6
  "en": "NexoWatt EOS Admin",
7
7
  "de": "NexoWatt EOS Admin",
@@ -282,6 +282,11 @@
282
282
  "en": "Fixes release validation for the modern EOS navigation icon update: package, lockfile and src-admin versions are synchronized, branding cache versioning is separated from the stable v79 React runtime, and future package checks verify both values explicitly.",
283
283
  "de": "Korrigiert die Release-Validierung des modernen EOS-Navigations-Icon-Updates: Paket-, Lockfile- und src-admin-Versionen sind synchron, die Branding-Cache-Version ist sauber von der stabilen v79-React-Runtime getrennt und zukünftige Paketprüfungen kontrollieren beide Werte ausdrücklich.",
284
284
  "nl": "Herstelt de releasevalidatie van de moderne EOS-navigatie-iconen: pakket-, lockfile- en src-admin-versies zijn gesynchroniseerd, de branding-cacheversie is gescheiden van de stabiele v79 React-runtime en toekomstige pakketcontroles valideren beide waarden expliciet."
285
+ },
286
+ "7.9.96": {
287
+ "de": "Native Übersicht für Admin, Installateur und Endkunde, read-only EMS-Live-Diagnose aus dem NexoWatt UI Adapter, größere scrollfreie Anmeldung sowie reparierte verpflichtende persönliche Kennwortvergabe nach der ersten Anmeldung mit nexowatt.",
288
+ "en": "Native overview for Admin, Installer and End User, read-only EMS live diagnosis from the NexoWatt UI adapter, a larger scroll-free login card, and repaired mandatory personal password setup after first sign-in with nexowatt.",
289
+ "nl": "Native overzicht voor Admin, Installateur en Eindgebruiker, read-only EMS-live diagnose uit de NexoWatt UI-adapter, een groter aanmeldvenster zonder interne schuifbalken en herstelde verplichte persoonlijke wachtwoordinstelling na de eerste aanmelding met nexowatt."
285
290
  }
286
291
  },
287
292
  "desc": {
@@ -319,7 +324,7 @@
319
324
  "icon": "admin.svg",
320
325
  "messagebox": true,
321
326
  "enabled": true,
322
- "extIcon": "https://unpkg.com/iobroker.eos-admin@7.9.95/admin/admin.svg",
327
+ "extIcon": "https://unpkg.com/iobroker.eos-admin@7.9.96/admin/admin.svg",
323
328
  "keywords": [
324
329
  "NexoWatt",
325
330
  "EOS",
@@ -330,7 +335,7 @@
330
335
  "licensed"
331
336
  ],
332
337
  "compact": true,
333
- "readme": "https://unpkg.com/iobroker.eos-admin@7.9.95/README.md",
338
+ "readme": "https://unpkg.com/iobroker.eos-admin@7.9.96/README.md",
334
339
  "authors": [
335
340
  "NexoWatt"
336
341
  ],
@@ -398,7 +403,7 @@
398
403
  "nondeletable": false,
399
404
  "allowAdapterUpdate": true,
400
405
  "allowAdapterDelete": false,
401
- "meta": "https://unpkg.com/iobroker.eos-admin@7.9.95/io-package.json",
406
+ "meta": "https://unpkg.com/iobroker.eos-admin@7.9.96/io-package.json",
402
407
  "npmPackage": "iobroker.eos-admin",
403
408
  "packetName": "iobroker.eos-admin",
404
409
  "publisher": "NexoWatt"
@@ -1107,5 +1112,5 @@
1107
1112
  }
1108
1113
  }
1109
1114
  },
1110
- "version": "7.9.95"
1115
+ "version": "7.9.96"
1111
1116
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "iobroker.eos-admin",
3
3
  "description": "NexoWatt EOS Admin stable product release with compact login, role-safe services and self-healing sales defaults",
4
- "version": "7.9.95",
4
+ "version": "7.9.96",
5
5
  "contributors": [
6
6
  "bluefox <dogafox@gmail.com>",
7
7
  "apollon77",
@@ -86,7 +86,7 @@
86
86
  "main": "build/main.js",
87
87
  "files": [
88
88
  "admin/",
89
- "build/",
89
+ "build",
90
90
  "docs/",
91
91
  "adminWww/",
92
92
  "public/",
@@ -94,17 +94,43 @@
94
94
  "LICENSE",
95
95
  "THIRD_PARTY_NOTICES.md",
96
96
  "NEXOWATT_PROPRIETARY_LICENSE.md",
97
+ "admin/LICENSE-NEXOWATT.md",
98
+ "adminWww/LICENSE-NEXOWATT.md",
97
99
  "NEXOWATT_PUBLIC_NPM_DISTRIBUTION.md",
100
+ "tools/nexowatt-publish-channel-guard.cjs",
101
+ "tools/nexowatt-validate-package.cjs",
98
102
  "README.md",
103
+ "tools/nexowatt-generate-repo-entry.cjs",
104
+ "tools/nexowatt-patch-repo.cjs",
99
105
  "NEXOWATT_EOS_BUILD_INFO.json",
100
106
  "NEXOWATT_EOS_STANDALONE.md",
101
- "NEXOWATT_EOS_STABLE_7.9.94.md",
102
- "README_STABILITY_V7.9.95.md",
103
- "RELEASE_NOTES_V7.9.95.md",
104
- "PUBLISH_STABLE_V7.9.95.md",
105
- "INSTALL_TEST_V7.9.95.md",
106
- "RELEASE_ACCEPTANCE_V7.9.95.md",
107
- "tools/nexowatt-repair-eos-admin-update.cjs"
107
+ "tools/nexowatt-generate-eos-admin-repo-entry.cjs",
108
+ "tools/nexowatt-repair-eos-admin-update.cjs",
109
+ "tools/nexowatt-clean-legacy-runtime.cjs",
110
+ "tools/nexowatt-runtime-cleanup-selftest.cjs",
111
+ "tools/nexowatt-account-management-selftest.cjs",
112
+ "tools/nexowatt-modern-ui-selftest.cjs",
113
+ "tools/nexowatt-login-layout-selftest.cjs",
114
+ "tools/nexowatt-internal-reserve-selftest.cjs",
115
+ "tools/nexowatt-ensure-release-defaults.cjs",
116
+ "tools/nexowatt-release-defaults-selftest.cjs",
117
+ "tools/nexowatt-sync-release-version.cjs",
118
+ "tools/nexowatt-version-sync-selftest.cjs",
119
+ "tools/nexowatt-merge-update-selftest.cjs",
120
+ "tools/nexowatt-ems-overview-selftest.cjs",
121
+ "README_STABILITY_V7.9.96.md",
122
+ "RELEASE_NOTES_V7.9.96.md",
123
+ "PUBLISH_STABLE_V7.9.96.md",
124
+ "INSTALL_TEST_V7.9.96.md",
125
+ "RELEASE_ACCEPTANCE_V7.9.96.md",
126
+ "MERGE_UPDATE_README_V7.9.96.md",
127
+ "README_EMS_OVERVIEW_MERGE.md",
128
+ "RC73_EOS_ADMIN_MERGE_VALIDATION.md",
129
+ "VALIDATION_EMS_OVERVIEW_MERGE.md",
130
+ "NexoWatt_EOS_Admin_EMS_OVERVIEW_MERGE_VALIDATION.md",
131
+ "tools/nexowatt-apply-ems-overview.cjs",
132
+ "tools/nexowatt-ui-overview-runtime-selftest.cjs",
133
+ "tools/nexowatt-stable-v96-selftest.cjs"
108
134
  ],
109
135
  "scripts": {
110
136
  "npm": "npm i -f && cd src-admin && npm i -f",
@@ -139,7 +165,7 @@
139
165
  "nexowatt:repo-entry": "node tools/nexowatt-generate-repo-entry.cjs",
140
166
  "nexowatt:patch-repo": "node tools/nexowatt-patch-repo.cjs",
141
167
  "nexowatt:repair-update": "node tools/nexowatt-repair-eos-admin-update.cjs",
142
- "check:eos-stability": "node tools/nexowatt-stability-check.cjs && node tools/nexowatt-esm-syntax-selftest.cjs && node tools/nexowatt-import-integrity-selftest.cjs && node tools/nexowatt-entrypoint-smoke-selftest.cjs && node tools/nexowatt-frontend-graph-check.cjs && node tools/nexowatt-policy-selftest.cjs && node tools/nexowatt-publish-channel-selftest.cjs && node tools/nexowatt-release-defaults-selftest.cjs && node tools/nexowatt-role-access-selftest.cjs && node tools/nexowatt-internal-reserve-selftest.cjs && node tools/nexowatt-first-login-selftest.cjs && node tools/nexowatt-account-management-selftest.cjs && node tools/nexowatt-modern-ui-selftest.cjs && node tools/nexowatt-login-layout-selftest.cjs && node tools/nexowatt-branding-selftest.cjs && node tools/nexowatt-default-port-selftest.cjs && node tools/nexowatt-module-popup-selftest.cjs && node tools/nexowatt-datapoint-selftest.cjs && node tools/nexowatt-update-selftest.cjs && node tools/nexowatt-datapoint-interaction-selftest.cjs && node tools/nexowatt-manual-write-policy-selftest.cjs && node tools/nexowatt-value-column-selftest.cjs && node tools/nexowatt-native-shell-selftest.cjs && node tools/nexowatt-assistant-separation-selftest.cjs && node tools/nexowatt-runtime-cleanup-selftest.cjs && node tools/nexowatt-version-sync-selftest.cjs && node tools/nexowatt-merge-update-selftest.cjs && npm run check:eos-stable-v95",
168
+ "check:eos-stability": "node tools/nexowatt-stability-check.cjs && node tools/nexowatt-esm-syntax-selftest.cjs && node tools/nexowatt-import-integrity-selftest.cjs && node tools/nexowatt-entrypoint-smoke-selftest.cjs && node tools/nexowatt-frontend-graph-check.cjs && node tools/nexowatt-policy-selftest.cjs && node tools/nexowatt-publish-channel-selftest.cjs && node tools/nexowatt-release-defaults-selftest.cjs && node tools/nexowatt-role-access-selftest.cjs && node tools/nexowatt-internal-reserve-selftest.cjs && node tools/nexowatt-first-login-selftest.cjs && node tools/nexowatt-account-management-selftest.cjs && node tools/nexowatt-modern-ui-selftest.cjs && node tools/nexowatt-login-layout-selftest.cjs && node tools/nexowatt-branding-selftest.cjs && node tools/nexowatt-default-port-selftest.cjs && node tools/nexowatt-module-popup-selftest.cjs && node tools/nexowatt-datapoint-selftest.cjs && node tools/nexowatt-update-selftest.cjs && node tools/nexowatt-datapoint-interaction-selftest.cjs && node tools/nexowatt-manual-write-policy-selftest.cjs && node tools/nexowatt-value-column-selftest.cjs && node tools/nexowatt-native-shell-selftest.cjs && node tools/nexowatt-assistant-separation-selftest.cjs && node tools/nexowatt-runtime-cleanup-selftest.cjs && node tools/nexowatt-version-sync-selftest.cjs && node tools/nexowatt-merge-update-selftest.cjs && npm run check:eos-stable-v96 && node tools/nexowatt-ems-overview-selftest.cjs && node tools/nexowatt-ui-overview-runtime-selftest.cjs",
143
169
  "nexowatt:patch-built-frontend": "node tools/nexowatt-patch-built-frontend.cjs",
144
170
  "clean:eos-runtime": "node tools/nexowatt-clean-legacy-runtime.cjs",
145
171
  "precheck:eos-package": "npm run sync:eos-version && npm run prepare:eos-release-defaults && npm run clean:eos-runtime",
@@ -163,7 +189,7 @@
163
189
  "sync:eos-version": "node tools/nexowatt-sync-release-version.cjs",
164
190
  "test:eos-version-sync": "node tools/nexowatt-version-sync-selftest.cjs",
165
191
  "verify:eos-merge": "node tools/nexowatt-merge-update-selftest.cjs",
166
- "check:eos-stable-v95": "node tools/nexowatt-stable-v95-selftest.cjs"
192
+ "check:eos-stable-v96": "node tools/nexowatt-stable-v96-selftest.cjs"
167
193
  },
168
194
  "author": "NexoWatt",
169
195
  "private": false,
@@ -174,7 +200,7 @@
174
200
  },
175
201
  "nexowattReleasePolicy": {
176
202
  "distTag": "latest",
177
- "purpose": "accepted stable NexoWatt EOS sales-product release 7.9.95"
203
+ "purpose": "accepted stable NexoWatt EOS sales-product release 7.9.96"
178
204
  },
179
205
  "publisher": "NexoWatt"
180
206
  }
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const root = path.resolve(__dirname, '..');
6
+ const read = rel => fs.readFileSync(path.join(root, rel), 'utf8');
7
+ const json = rel => JSON.parse(read(rel));
8
+ let bad = false;
9
+ const fail = msg => { console.error(`[NexoWatt EOS account management] ${msg}`); bad = true; };
10
+
11
+ const mainSource = read('src/main.ts');
12
+ const mainBuilt = read('build/main.js');
13
+ const webSource = read('src/lib/web.ts');
14
+ const webBuilt = read('build/lib/web.js');
15
+ const sourceUi = read('src-admin/public/js/eos-account-management.js');
16
+ const builtUi = read('adminWww/js/eos-account-management.js');
17
+ const roleUi = read('adminWww/js/eos-role-ui.js');
18
+ const css = read('adminWww/css/nexowatt-native-shell.css');
19
+ const index = read('adminWww/index.html');
20
+ const info = json('NEXOWATT_EOS_BUILD_INFO.json');
21
+ const shellTag = String(info.shellCacheTag || info.shellCacheVersion);
22
+
23
+ for (const code of [mainSource, mainBuilt]) {
24
+ for (const marker of [
25
+ 'system.user.installer', 'system.user.guest', 'ensureEosDefaultRoleUsers', 'unknown random bootstrap secret',
26
+ 'eosPasswordSetupRequired', 'personal password setup is required',
27
+ ]) if (!code.includes(marker)) fail(`main account marker missing: ${marker}`);
28
+ }
29
+ for (const code of [webSource, webBuilt]) {
30
+ for (const marker of [
31
+ 'getEosManagedAccounts', 'sendEosAccountManagement', 'resetEosAccountPassword',
32
+ '/nexowatt/account/manage', '/nexowatt/account/reset', 'x-nexowatt-eos-account-reset',
33
+ 'canResetInstaller', 'canResetEndUser', "targetUserId === 'system.user.admin'", 'targetUserId === access.userId',
34
+ "access.role === 'installer' && explicitlyManagedRole !== 'enduser'",
35
+ 'explicit member of a managed EOS installer/end-user group', 'passwordResetAt', 'passwordResetBy',
36
+ "passwordResetMode = 'initial-password'", "setEosUserPassword(targetUserId, 'nexowatt')", 'passwordlessFirstLoginAllowed = false', 'initialPasswordRequired: true', 'accountDisabled',
37
+ ]) if (!code.includes(marker)) fail(`web account marker missing: ${marker}`);
38
+ if (!code.includes('const explicitInstaller = targetAccess.groups.some')) fail('reset target is not restricted to explicit installer membership');
39
+ if (!code.includes('const explicitEndUser = targetAccess.groups.some')) fail('reset target is not restricted to explicit end-user membership');
40
+ }
41
+
42
+ for (const marker of [
43
+ 'v91-account-management-under-access-rights', "['admin', 'installer']", 'nexowatt/account/manage',
44
+ 'nexowatt/account/reset', 'X-NexoWatt-EOS-Account-Reset', 'data.canResetInstaller === true',
45
+ "account.role === 'enduser'", 'Admin/Service', 'Installateure', 'NEXOWATT_EOS_ACCOUNT_MANAGEMENT',
46
+ 'ensureEntrySurface', "currentRoute() !== 'tab-users'", 'eos-account-management-entry',
47
+ ]) if (!sourceUi.includes(marker)) fail(`account UI marker missing: ${marker}`);
48
+ if (sourceUi.includes("launcher.className = 'eos-account-management-launcher'")) fail('standalone account launcher is still created');
49
+ if (sourceUi.includes('new MutationObserver')) fail('account management adds a second broad MutationObserver');
50
+ if (sourceUi !== builtUi) fail('account management source/build drift');
51
+ if (!roleUi.includes("route === 'tab-users'")) fail('installer cannot reach Zugänge & Rechte');
52
+ if (roleUi.includes('NEXOWATT_EOS_ACCOUNT_MANAGEMENT.open')) fail('role overview opens account management outside Zugänge & Rechte');
53
+ if (!index.includes(`eos-account-management.js?v=${shellTag}`)) fail('account management asset is not active with the current cache tag');
54
+ for (const marker of [
55
+ '.eos-account-management-entry', '.eos-account-management-overlay', '.eos-account-management-dialog',
56
+ '.eos-account-row', '.eos-account-reset', 'html.eos-account-page-installer',
57
+ ]) if (!css.includes(marker)) fail(`account management CSS marker missing: ${marker}`);
58
+
59
+ if (bad) process.exit(1);
60
+ console.log('[NexoWatt EOS account management] OK');
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+
6
+ const root = path.resolve(__dirname, '..');
7
+ const introPath = path.join(root, 'src-admin', 'src', 'tabs', 'Intro.tsx');
8
+ const componentPath = path.join(root, 'src-admin', 'src', 'components', 'Intro', 'NexoWattEmsOverview.tsx');
9
+ const cssPath = path.join(root, 'src-admin', 'src', 'index.css');
10
+ const packagePath = path.join(root, 'package.json');
11
+
12
+ function fail(message) {
13
+ console.error(`[NexoWatt EMS Overview Merge] ERROR: ${message}`);
14
+ process.exit(1);
15
+ }
16
+ function read(file) {
17
+ try { return fs.readFileSync(file, 'utf8'); } catch (error) { fail(`${file} nicht lesbar: ${error.message}`); }
18
+ }
19
+ function write(file, content) { fs.writeFileSync(file, content, 'utf8'); }
20
+
21
+ if (!fs.existsSync(componentPath)) fail('NexoWattEmsOverview.tsx fehlt im Merge-Paket.');
22
+ let intro = read(introPath);
23
+ if (!intro.includes("import NexoWattEmsOverview from '@/components/Intro/NexoWattEmsOverview';")) {
24
+ const importAnchor = "import IntroCardCamera from '@/components/Intro/IntroCardCamera';\n";
25
+ if (!intro.includes(importAnchor)) fail('Intro.tsx Importanker nicht gefunden. Der Admin-Quellstand ist nicht kompatibel.');
26
+ intro = intro.replace(importAnchor, `${importAnchor}import NexoWattEmsOverview from '@/components/Intro/NexoWattEmsOverview';\n`);
27
+ }
28
+ if (!intro.includes('<NexoWattEmsOverview')) {
29
+ const renderAnchor = ` {this.getInstancesCards()}\n {this.getLinkCards()}\n`;
30
+ if (!intro.includes(renderAnchor)) fail('Intro.tsx Kartenanker nicht gefunden. Der Admin-Quellstand ist nicht kompatibel.');
31
+ intro = intro.replace(renderAnchor, `${renderAnchor} <NexoWattEmsOverview\n socket={this.props.socket}\n t={this.props.t}\n lang={this.props.lang}\n theme={this.props.theme}\n />\n`);
32
+ }
33
+ write(introPath, intro);
34
+
35
+ const cssMarker = '/* nexowatt-ems-overview-v1 */';
36
+ let css = read(cssPath);
37
+ if (!css.includes(cssMarker)) {
38
+ css += `\n\n${cssMarker}\n` + String.raw`
39
+ .eos-ems-overview-card {
40
+ --eos-ems-status-color: #48b9ff;
41
+ flex: 1 1 720px;
42
+ min-width: min(100%, 420px);
43
+ max-width: 1180px;
44
+ min-height: 390px;
45
+ margin: 10px;
46
+ padding: 22px;
47
+ border: 1px solid color-mix(in srgb, var(--eos-ems-status-color) 38%, rgba(255,255,255,.08));
48
+ border-radius: 20px;
49
+ background: linear-gradient(145deg, rgba(4,24,35,.97), rgba(3,16,29,.98));
50
+ color: #ecf8ff;
51
+ box-shadow: 0 18px 55px rgba(0,0,0,.26), inset 0 0 0 1px rgba(255,255,255,.025);
52
+ overflow: hidden;
53
+ }
54
+ .eos-ems-overview-card * { box-sizing: border-box; }
55
+ .eos-ems-overview-header { display:flex; align-items:flex-start; justify-content:space-between; gap:20px; }
56
+ .eos-ems-overview-header h2 { margin:5px 0 6px; font-size:clamp(1.2rem,2vw,1.65rem); color:#f5fbff; }
57
+ .eos-ems-overview-header p { margin:0; max-width:760px; color:#9eb2c2; line-height:1.45; }
58
+ .eos-ems-overview-eyebrow { color:#49e39e; font-size:.72rem; font-weight:900; letter-spacing:.13em; text-transform:uppercase; }
59
+ .eos-ems-overview-state { min-width:126px; display:grid; grid-template-columns:auto 1fr; align-items:center; gap:3px 8px; padding:9px 12px; border:1px solid rgba(255,255,255,.09); border-radius:999px; background:rgba(255,255,255,.035); }
60
+ .eos-ems-overview-state-dot { width:9px; height:9px; border-radius:50%; background:var(--eos-ems-status-color); box-shadow:0 0 15px var(--eos-ems-status-color); }
61
+ .eos-ems-overview-state strong { color:var(--eos-ems-status-color); }
62
+ .eos-ems-overview-state small { grid-column:2; color:#8095a6; }
63
+ .eos-ems-overview-metrics { display:grid; grid-template-columns:repeat(4,minmax(130px,1fr)); gap:10px; margin-top:19px; }
64
+ .eos-ems-overview-metric { min-width:0; padding:12px; border:1px solid rgba(255,255,255,.075); border-radius:14px; background:rgba(255,255,255,.025); }
65
+ .eos-ems-overview-metric span,.eos-ems-overview-metric small { display:block; color:#879cad; font-size:.72rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
66
+ .eos-ems-overview-metric strong { display:block; margin:4px 0 2px; color:#f4fbff; font-size:1rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
67
+ .eos-ems-overview-budget { height:7px; margin:12px 0 10px; border-radius:999px; background:rgba(255,255,255,.06); overflow:hidden; }
68
+ .eos-ems-overview-budget span { display:block; height:100%; border-radius:inherit; background:linear-gradient(90deg,#01bc69,var(--eos-ems-status-color)); box-shadow:0 0 15px color-mix(in srgb,var(--eos-ems-status-color) 50%,transparent); transition:width .25s ease; }
69
+ .eos-ems-overview-tags { display:flex; flex-wrap:wrap; gap:7px; margin-bottom:14px; }
70
+ .eos-ems-overview-tags span { padding:5px 9px; border:1px solid rgba(72,185,255,.18); border-radius:999px; background:rgba(72,185,255,.07); color:#b9deef; font-size:.72rem; font-weight:750; }
71
+ .eos-ems-overview-tags span.is-warning { border-color:rgba(255,189,89,.3); background:rgba(255,189,89,.09); color:#ffd484; }
72
+ .eos-ems-overview-columns { display:grid; grid-template-columns:1fr 1fr; gap:12px; }
73
+ .eos-ems-overview-panel { min-width:0; padding:14px; border:1px solid rgba(255,255,255,.065); border-radius:16px; background:rgba(0,0,0,.12); }
74
+ .eos-ems-overview-panel h3 { margin:0 0 10px; color:#dfeef6; font-size:.9rem; }
75
+ .eos-ems-overview-decision,.eos-ems-overview-event { position:relative; display:grid; gap:2px; margin-top:7px; padding:8px 9px 8px 12px; border-radius:10px; background:rgba(255,255,255,.025); overflow:hidden; }
76
+ .eos-ems-overview-decision::before,.eos-ems-overview-event::before { content:''; position:absolute; inset:0 auto 0 0; width:3px; background:#48b9ff; }
77
+ .eos-ems-overview-decision--warning::before,.eos-ems-overview-event--warning::before,.eos-ems-overview-event--warn::before { background:#ffbd59; }
78
+ .eos-ems-overview-decision--error::before,.eos-ems-overview-event--error::before { background:#ff5f72; }
79
+ .eos-ems-overview-decision strong,.eos-ems-overview-event strong { color:#e7f4fa; font-size:.78rem; }
80
+ .eos-ems-overview-decision span,.eos-ems-overview-event span { color:#91a5b5; font-size:.72rem; line-height:1.35; }
81
+ .eos-ems-overview-decision small { color:#6fcba1; font-size:.68rem; }
82
+ .eos-ems-overview-event { grid-template-columns:64px 1fr; align-items:start; }
83
+ .eos-ems-overview-event time { color:#6f8799; font-size:.68rem; font-variant-numeric:tabular-nums; }
84
+ .eos-ems-overview-event div { display:grid; gap:2px; min-width:0; }
85
+ .eos-ems-overview-empty { color:#718797; font-size:.76rem; }
86
+ .eos-ems-overview-footer { display:flex; justify-content:space-between; gap:12px; margin-top:12px; color:#60798a; font-size:.67rem; }
87
+ .eos-ems-overview-footer a { color:#5ee0c2; font-weight:800; text-decoration:none; }
88
+ .eos-ems-overview-footer a:hover,.eos-ems-overview-footer a:focus-visible { color:#9af2dd; text-decoration:underline; }
89
+ @media (max-width: 1100px) {
90
+ .eos-ems-overview-card { flex-basis:100%; max-width:none; }
91
+ }
92
+ @media (max-width: 760px) {
93
+ .eos-ems-overview-card { min-width:100%; margin:8px 0; padding:16px; }
94
+ .eos-ems-overview-header { display:grid; }
95
+ .eos-ems-overview-state { width:max-content; }
96
+ .eos-ems-overview-metrics { grid-template-columns:1fr 1fr; }
97
+ .eos-ems-overview-columns { grid-template-columns:1fr; }
98
+ .eos-ems-overview-footer { display:grid; }
99
+ }
100
+ `;
101
+ write(cssPath, css);
102
+ }
103
+
104
+ const packageJson = JSON.parse(read(packagePath));
105
+ packageJson.files = Array.isArray(packageJson.files) ? packageJson.files : [];
106
+ const toolFile = 'tools/nexowatt-ems-overview-selftest.cjs';
107
+ if (!packageJson.files.includes(toolFile)) packageJson.files.push(toolFile);
108
+ packageJson.scripts = packageJson.scripts || {};
109
+ const stability = String(packageJson.scripts['check:eos-stability'] || '');
110
+ if (stability && !stability.includes('nexowatt-ems-overview-selftest.cjs')) {
111
+ packageJson.scripts['check:eos-stability'] = `${stability} && node tools/nexowatt-ems-overview-selftest.cjs`;
112
+ }
113
+ write(packagePath, `${JSON.stringify(packageJson, null, 2)}\n`);
114
+
115
+ console.log('[NexoWatt EMS Overview Merge] OK: Intro-Komponente, responsive Live-Diagnose und Selftest eingebunden.');
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const os = require('os');
6
+ const path = require('path');
7
+
8
+ const DEFAULT_ROOT = path.resolve(__dirname, '..');
9
+ const LEGACY_OVERLAYS = [
10
+ 'adminWww/js/eos-branding.js',
11
+ 'adminWww/js/eos-security-ui.js',
12
+ 'adminWww/js/eos-console-quiet.js',
13
+ 'adminWww/js/eos-objects-state-tools.js',
14
+ 'adminWww/js/eos-performance-guard.js',
15
+ 'adminWww/js/eos-runtime-fixes.js',
16
+ 'adminWww/js/eos-hard-logout.js',
17
+ 'adminWww/css/eos-branding.css',
18
+ 'src-admin/public/js/eos-branding.js',
19
+ 'src-admin/public/js/eos-security-ui.js',
20
+ 'src-admin/public/js/eos-console-quiet.js',
21
+ 'src-admin/public/js/eos-objects-state-tools.js',
22
+ 'src-admin/public/js/eos-performance-guard.js',
23
+ 'src-admin/public/js/eos-runtime-fixes.js',
24
+ 'src-admin/public/js/eos-hard-logout.js',
25
+ 'src-admin/public/css/eos-branding.css',
26
+ ];
27
+
28
+ const readJson = file => JSON.parse(fs.readFileSync(file, 'utf8'));
29
+
30
+ function runtimeNumber(root) {
31
+ const info = readJson(path.join(root, 'NEXOWATT_EOS_BUILD_INFO.json'));
32
+ const value = Number(String(info.runtimeEntry || '').replace(/^v/, ''));
33
+ if (!Number.isFinite(value)) {
34
+ throw new Error(`Invalid runtimeEntry in NEXOWATT_EOS_BUILD_INFO.json: ${info.runtimeEntry}`);
35
+ }
36
+ return value;
37
+ }
38
+
39
+ function walkFiles(dir) {
40
+ if (!fs.existsSync(dir)) return [];
41
+ const result = [];
42
+ const stack = [dir];
43
+ while (stack.length) {
44
+ const current = stack.pop();
45
+ for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
46
+ const absolute = path.join(current, entry.name);
47
+ if (entry.isDirectory()) stack.push(absolute);
48
+ else if (entry.isFile()) result.push(absolute);
49
+ }
50
+ }
51
+ return result;
52
+ }
53
+
54
+ function isLegacyVersionedRuntime(file, activeRuntime) {
55
+ const name = path.basename(file);
56
+ // Stable product overlays use the release number in their filename, not the
57
+ // compiled React runtime number. They are release assets and must not be
58
+ // deleted merely because runtimeEntry remains on the validated v84 bundle.
59
+ if (/^nexowatt-stable-v\d+\.js$/i.test(name)) return false;
60
+ const versionMatch = name.match(/-v(\d+)\.(?:js|js\.map|css|css\.map)$/i);
61
+ if (versionMatch) return Number(versionMatch[1]) !== activeRuntime;
62
+ const remoteMatch = name.match(/^remoteEntry-v(\d+)\.(?:js|js\.map)$/i);
63
+ if (remoteMatch) return Number(remoteMatch[1]) !== activeRuntime;
64
+
65
+ // Remove historical emergency bundles which predate the consolidated runtime
66
+ // naming and must never survive in a current EOS Admin source tree.
67
+ return /(?:DPWrite|DPAdapter|DPw\d+|NoLock|Unrestricted|force-version|delete-services-hardfix)/i.test(name) && /\.(?:js|js\.map)$/i.test(name);
68
+ }
69
+
70
+ function collectLegacyRuntimeFiles(root = DEFAULT_ROOT) {
71
+ const activeRuntime = runtimeNumber(root);
72
+ const scanRoots = [
73
+ path.join(root, 'adminWww', 'assets'),
74
+ path.join(root, 'adminWww'),
75
+ path.join(root, 'src-admin', 'build', 'assets'),
76
+ path.join(root, 'src-admin', 'build'),
77
+ ];
78
+ const files = new Set();
79
+ for (const scanRoot of scanRoots) {
80
+ for (const file of walkFiles(scanRoot)) {
81
+ if (isLegacyVersionedRuntime(file, activeRuntime)) files.add(file);
82
+ }
83
+ }
84
+ for (const rel of LEGACY_OVERLAYS) {
85
+ const file = path.join(root, rel);
86
+ if (fs.existsSync(file)) files.add(file);
87
+ }
88
+ return { activeRuntime, files: [...files].sort() };
89
+ }
90
+
91
+ function cleanLegacyRuntime(options = {}) {
92
+ const root = path.resolve(options.root || DEFAULT_ROOT);
93
+ const dryRun = options.dryRun === true;
94
+ const quiet = options.quiet === true;
95
+ const silent = options.silent === true;
96
+ const { activeRuntime, files } = collectLegacyRuntimeFiles(root);
97
+ const removed = [];
98
+ const failed = [];
99
+
100
+ for (const file of files) {
101
+ if (dryRun) {
102
+ removed.push(file);
103
+ continue;
104
+ }
105
+ try {
106
+ try {
107
+ fs.rmSync(file, { force: true });
108
+ } catch (firstError) {
109
+ // ZIP extraction tools on Windows may preserve a read-only bit. Make a
110
+ // single controlled retry before reporting a real cleanup failure.
111
+ try { fs.chmodSync(file, 0o666); } catch (_) { /* best effort */ }
112
+ fs.rmSync(file, { force: true });
113
+ }
114
+ if (!fs.existsSync(file)) removed.push(file);
115
+ else failed.push({ file, error: 'file still exists after removal' });
116
+ } catch (error) {
117
+ failed.push({ file, error: error?.message || String(error) });
118
+ }
119
+ }
120
+
121
+ if (failed.length) {
122
+ const details = failed.map(item => `${path.relative(root, item.file)}: ${item.error}`).join(os.EOL);
123
+ throw new Error(`Could not remove ${failed.length} legacy runtime file(s):${os.EOL}${details}`);
124
+ }
125
+
126
+ if (!silent) {
127
+ if (!quiet) {
128
+ console.log(`[NexoWatt EOS runtime cleanup] OK (runtime v${activeRuntime}, removed ${removed.length} legacy file${removed.length === 1 ? '' : 's'})`);
129
+ } else if (removed.length) {
130
+ console.log(`[NexoWatt EOS runtime cleanup] removed ${removed.length} legacy runtime files before validation`);
131
+ }
132
+ }
133
+
134
+ return { activeRuntime, removed };
135
+ }
136
+
137
+ if (require.main === module) {
138
+ try {
139
+ cleanLegacyRuntime({ dryRun: process.argv.includes('--dry-run'), quiet: process.argv.includes('--quiet') });
140
+ } catch (error) {
141
+ console.error(`[NexoWatt EOS runtime cleanup] ERROR: ${error?.stack || error}`);
142
+ process.exit(1);
143
+ }
144
+ }
145
+
146
+ module.exports = {
147
+ LEGACY_OVERLAYS,
148
+ collectLegacyRuntimeFiles,
149
+ cleanLegacyRuntime,
150
+ isLegacyVersionedRuntime,
151
+ };