iobroker.eos-admin 7.10.0 → 7.10.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.
Files changed (53) hide show
  1. package/INSTALL_TEST_V7.10.1.md +10 -0
  2. package/MERGE_UPDATE_README_V7.10.1.md +10 -0
  3. package/NEXOWATT_EOS_BUILD_INFO.json +20 -21
  4. package/PUBLISH_STABLE_V7.10.1.md +10 -0
  5. package/README.md +16 -6
  6. package/README_STABILITY_V7.10.1.md +10 -0
  7. package/RELEASE_ACCEPTANCE_V7.10.1.md +10 -0
  8. package/RELEASE_NOTES_V7.10.1.md +10 -0
  9. package/adminWww/assets/Intro-DkwRiz1n-v84.js +1 -1
  10. package/adminWww/assets/bootstrap-COulQZax-v84.js +2 -2
  11. package/adminWww/css/eos-ems-overview.css +5 -3
  12. package/adminWww/css/nexowatt-native-shell.css +85 -413
  13. package/adminWww/index.html +20 -21
  14. package/adminWww/js/eos-account-management.js +28 -19
  15. package/adminWww/js/eos-assistant.js +1 -1
  16. package/adminWww/js/eos-ems-overview.js +1 -1
  17. package/adminWww/js/eos-role-bootstrap.js +17 -80
  18. package/adminWww/js/eos-role-ui.js +28 -4
  19. package/adminWww/js/nexowatt-native-shell.js +1 -1
  20. package/adminWww/static/js/nexowatt-stable-login.js +43 -70
  21. package/adminWww/static/js/nexowatt-stable-v7101.js +27 -0
  22. package/build/lib/web.js +159 -283
  23. package/build/main.js +39 -67
  24. package/io-package.json +17 -7
  25. package/package.json +15 -22
  26. package/tools/nexowatt-account-management-selftest.cjs +15 -19
  27. package/tools/nexowatt-account-password-flow-selftest.cjs +15 -0
  28. package/tools/nexowatt-apply-ems-overview.cjs +4 -2
  29. package/tools/nexowatt-clean-legacy-runtime.cjs +1 -1
  30. package/tools/nexowatt-ems-overview-selftest.cjs +1 -1
  31. package/tools/nexowatt-ensure-release-defaults.cjs +1 -1
  32. package/tools/nexowatt-login-layout-selftest.cjs +9 -13
  33. package/tools/nexowatt-merge-update-selftest.cjs +1 -1
  34. package/tools/nexowatt-modern-ui-selftest.cjs +10 -46
  35. package/tools/nexowatt-password-write-selftest.cjs +46 -16
  36. package/tools/nexowatt-release-defaults-selftest.cjs +1 -1
  37. package/tools/nexowatt-runtime-cleanup-selftest.cjs +6 -8
  38. package/tools/nexowatt-stable-v7101-selftest.cjs +15 -0
  39. package/tools/nexowatt-sync-release-version.cjs +43 -5
  40. package/tools/nexowatt-ui-overview-runtime-selftest.cjs +2 -3
  41. package/tools/nexowatt-validate-package.cjs +27 -26
  42. package/tools/nexowatt-version-sync-selftest.cjs +18 -1
  43. package/INSTALL_TEST_V7.10.0.md +0 -11
  44. package/MERGE_UPDATE_README_V7.10.0.md +0 -5
  45. package/PUBLISH_STABLE_V7.10.0.md +0 -15
  46. package/README_STABILITY_V7.10.0.md +0 -3
  47. package/RELEASE_ACCEPTANCE_V7.10.0.md +0 -11
  48. package/RELEASE_NOTES_V7.10.0.md +0 -17
  49. package/adminWww/static/js/nexowatt-stable-v7100.js +0 -111
  50. package/src/lib/eosAutoUpdate.ts +0 -320
  51. package/tools/nexowatt-cache-update-selftest.cjs +0 -12
  52. package/tools/nexowatt-request-security-selftest.cjs +0 -11
  53. package/tools/nexowatt-stable-v7100-selftest.cjs +0 -17
@@ -1,18 +1,48 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
- const assert=require('node:assert/strict');
4
- const {normalizeEosPasswordTarget,setEosUserPasswordWithVerification,verifyEosUserPasswordCredential}=require('../build/lib/eosPassword.js');
5
- function adapter({canonicalFails=false, optionsFail=false, verify=true}={}){
6
- const user={_id:'system.user.user',common:{password:'$old'},native:{}};let accepted='old';const calls=[];
7
- const set=async(target,password,options)=>{calls.push({target,password,options});if(canonicalFails&&target==='system.user.user')throw new Error('canonicalRejected');if(optionsFail&&options)throw new Error('optionsRejected');user.common.password=`$${Date.now()}:${password}`;accepted=password};
8
- return {calls,getForeignObjectAsync:async id=>id==='system.user.user'?user:null,setPasswordAsync:set,checkPasswordAsync:async(target,password)=>[verify&&['system.user.user','user'].includes(target)&&password===accepted,'system.user.user']};
9
- }
10
- (async()=>{
11
- assert.deepEqual(normalizeEosPasswordTarget('user'),{userId:'system.user.user',userName:'user'});
12
- const normal=adapter();const a=await setEosUserPasswordWithVerification(normal,'system.user.user','Strong!2026');assert.equal(a.apiTarget,'system.user.user');
13
- await verifyEosUserPasswordCredential(normal,'system.user.user','Strong!2026');
14
- const legacy=adapter({canonicalFails:true});const b=await setEosUserPasswordWithVerification(legacy,'system.user.user','Legacy!2026');assert.equal(b.apiTarget,'user');
15
- const noOptions=adapter({optionsFail:true});const c=await setEosUserPasswordWithVerification(noOptions,'system.user.user','NoOptions!2026');assert.ok(['system.user.user','user'].includes(c.apiTarget));assert.ok(noOptions.calls.some(call=>call.options===null));
16
- await assert.rejects(()=>setEosUserPasswordWithVerification(adapter({verify:false}),'user','Fail!2026'),/passwordVerificationFailed/);
17
- console.log('[NexoWatt EOS password write v7100] OK');
18
- })().catch(error=>{console.error(error.stack||error);process.exit(1)});
3
+ const assert = require('node:assert/strict');
4
+ const fs = require('node:fs');
5
+ const path = require('node:path');
6
+ const vm = require('node:vm');
7
+ const root = path.resolve(__dirname, '..');
8
+ const built = fs.readFileSync(path.join(root, 'build/lib/web.js'), 'utf8');
9
+ const source = fs.readFileSync(path.join(root, 'src/lib/web.ts'), 'utf8');
10
+ const begin = built.indexOf(' getEosPasswordUserName(userId) {');
11
+ const end = built.indexOf(' async destroyEosRequestSessions', begin);
12
+ assert(begin >= 0 && end > begin, 'password methods not found in built backend');
13
+ const methods = built.slice(begin, end);
14
+ const sandbox = { module: { exports: null }, EOS_PASSWORD_SERVICE_USER: 'system.user.admin' };
15
+ vm.runInNewContext(`module.exports = class Harness {\nconstructor(adapter){this.adapter=adapter;}\n${methods}\n}`, sandbox);
16
+ const Harness = sandbox.module.exports;
17
+ (async () => {
18
+ const calls = [];
19
+ const userObject = { _id: 'system.user.user', common: { password: '$2b$hash' }, native: { keep: true } };
20
+ const adapter = {
21
+ setPasswordAsync: async (user, password, options) => calls.push(['set', user, password, options]),
22
+ checkPasswordAsync: async (user, password, options) => { calls.push(['check', user, password, options]); return [true, `system.user.${user}`]; },
23
+ getForeignObjectAsync: async id => { calls.push(['get', id]); return JSON.parse(JSON.stringify(userObject)); },
24
+ extendForeignObjectAsync: async (id, patch, options) => calls.push(['extend', id, patch, options]),
25
+ setForeignObjectAsync: async () => { throw new Error('full user object replacement must not be used'); },
26
+ };
27
+ const harness = new Harness(adapter);
28
+ await harness.setEosUserPassword('system.user.user', 'NexoWatt2025!');
29
+ assert.deepEqual(calls[0].slice(0, 3), ['set', 'user', 'NexoWatt2025!']);
30
+ assert.deepEqual(calls[1].slice(0, 3), ['check', 'user', 'NexoWatt2025!']);
31
+ await harness.updateEosAccountMetadata('system.user.user', (native, account) => {
32
+ account.passwordInitialized = true;
33
+ native.nexowattPasswordChangeRequired = false;
34
+ });
35
+ const extend = calls.find(row => row[0] === 'extend');
36
+ assert(extend, 'native metadata was not extended');
37
+ assert.equal(extend[1], 'system.user.user');
38
+ assert.equal(extend[2].native.keep, true);
39
+ assert.equal(extend[2].native.nexowattEosAccount.passwordInitialized, true);
40
+ assert(!Object.prototype.hasOwnProperty.call(extend[2], 'common'), 'password/common must not be overwritten by metadata update');
41
+ for (const code of [source, built]) {
42
+ assert.match(code, /setPasswordAsync\(userName, password/);
43
+ assert.match(code, /checkPasswordAsync\(userName, password/);
44
+ assert.match(code, /extendForeignObjectAsync/);
45
+ assert.match(code, /passwordVerificationFailed/);
46
+ }
47
+ console.log('[NexoWatt EOS password write] OK: real account name, verified password API and metadata-only update');
48
+ })().catch(error => { console.error(error.stack || error); process.exit(1); });
@@ -13,7 +13,7 @@ const unsafe = {
13
13
  eosHideLegacyAdminFromNonAdmins: false,
14
14
  eosHideLegacyBackupFromNonAdmins: false,
15
15
  nexowattHideLegacyBackupFromNonAdmins: false,
16
- eosRequireFirstLoginPassword: false,
16
+ eosRequireFirstLoginPassword: true,
17
17
  },
18
18
  };
19
19
  const repaired = ensureReleaseDefaults(unsafe);
@@ -15,15 +15,15 @@ try {
15
15
  fs.mkdirSync(path.join(temp, 'src-admin', 'public', 'js'), { recursive: true });
16
16
  fs.mkdirSync(path.join(temp, 'src-admin', 'public', 'static', 'js'), { recursive: true });
17
17
  fs.mkdirSync(path.join(temp, 'adminWww', 'static', 'js'), { recursive: true });
18
- fs.writeFileSync(path.join(temp, 'NEXOWATT_EOS_BUILD_INFO.json'), JSON.stringify({ runtimeEntry: 'v82', shellCacheVersion: 7100 }));
18
+ fs.writeFileSync(path.join(temp, 'NEXOWATT_EOS_BUILD_INFO.json'), JSON.stringify({ runtimeEntry: 'v82', shellCacheVersion: 98 }));
19
19
 
20
20
  const keep = [
21
21
  'adminWww/assets/bootstrap-current-v82.js',
22
22
  'adminWww/assets/Objects-current-v82.js',
23
23
  'adminWww/remoteEntry-v82.js',
24
24
  'adminWww/remoteEntry.js',
25
- 'adminWww/static/js/nexowatt-stable-v7100.js',
26
- 'src-admin/public/static/js/nexowatt-stable-v7100.js',
25
+ 'adminWww/static/js/nexowatt-stable-v98.js',
26
+ 'src-admin/public/static/js/nexowatt-stable-v98.js',
27
27
  'adminWww/static/js/nexowatt-stable-login.js',
28
28
  ];
29
29
  const remove = [
@@ -34,10 +34,8 @@ try {
34
34
  'adminWww/js/eos-branding.js',
35
35
  'adminWww/css/eos-branding.css',
36
36
  'src-admin/public/js/eos-security-ui.js',
37
- 'adminWww/static/js/nexowatt-stable-v100.js',
38
- 'src-admin/public/static/js/nexowatt-stable-v99.js',
39
- 'adminWww/static/js/nexowatt-stable-v98.js',
40
- 'src-admin/public/static/js/nexowatt-stable-v97.js',
37
+ 'adminWww/static/js/nexowatt-stable-v97.js',
38
+ 'src-admin/public/static/js/nexowatt-stable-v95.js',
41
39
  ];
42
40
  for (const rel of [...keep, ...remove]) {
43
41
  const file = path.join(temp, rel);
@@ -47,7 +45,7 @@ try {
47
45
 
48
46
  const result = cleanLegacyRuntime({ root: temp, quiet: true, silent: true });
49
47
  assert.strictEqual(result.activeRuntime, 82);
50
- assert.strictEqual(result.activeShell, 7100);
48
+ assert.strictEqual(result.activeShell, 98);
51
49
  assert.strictEqual(result.removed.length, remove.length);
52
50
  for (const rel of keep) assert.ok(fs.existsSync(path.join(temp, rel)), `current runtime removed: ${rel}`);
53
51
  for (const rel of remove) assert.ok(!fs.existsSync(path.join(temp, rel)), `legacy file remained: ${rel}`);
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ const fs=require('node:fs'),path=require('node:path');const root=path.resolve(__dirname,'..');const read=r=>fs.readFileSync(path.join(root,r),'utf8');const j=r=>JSON.parse(read(r));const must=(v,m)=>{if(!v){console.error('[NexoWatt EOS stable v7101] '+m);process.exit(1)}};
4
+ const p=j('package.json'),io=j('io-package.json'),info=j('NEXOWATT_EOS_BUILD_INFO.json'),main=read('src/main.ts'),boot=read('adminWww/js/eos-role-bootstrap.js'),role=read('adminWww/js/eos-role-ui.js'),css=read('adminWww/css/nexowatt-native-shell.css'),index=read('adminWww/index.html'),ems=read('adminWww/js/eos-ems-overview.js'),intro=read('src-admin/src/tabs/Intro.tsx'),bundle=read('adminWww/assets/Intro-DkwRiz1n-v84.js'),stable=read('adminWww/static/js/nexowatt-stable-v7101.js'),auto=read('src/lib/eosAutoUpdate.ts');
5
+ must(p.version==='7.10.1'&&io.version==='7.10.1'&&io.common.version==='7.10.1'&&info.version==='7.10.1','version drift');
6
+ must(io.native.eosRequireFirstLoginPassword===false&&io.native.eosRequireFirstLoginPasswordChange===false,'forced first password still enabled');
7
+ must(!index.includes('eos-account-management.js'),'custom reset UI still loaded');
8
+ must(!boot.includes('installIntegratedFirstLogin(base);')&&!boot.includes('showFirstLoginPassword(resolved, base);'),'forced first-login UI still active');
9
+ must(main.includes('standardPasswordMode: true')&&main.includes('nexowattPasswordChangeRequired: false'),'account migration missing');
10
+ must(role.includes("route === 'tab-users'")&&main.includes("command === 'changePassword'"),'role-aware native password editing missing');
11
+ must(css.includes('persistent native overview scrolling')&&stable.includes("overflow-y','scroll"),'persistent scroll missing');
12
+ must(intro.includes('eos-overview-edit-controls')&&bundle.includes('eos-overview-edit-controls'),'edit control not in bottom grid');
13
+ must(!ems.includes('Vollständige EMS-Diagnose öffnen'),'unsupported EMS link remains');
14
+ must(index.includes('eos-auto-update.js?v=7101')&&auto.includes("const POLICY: NexoWattAutoUpdatePolicy = 'major'"),'auto update missing');
15
+ console.log('[NexoWatt EOS stable v7101] OK');
@@ -19,6 +19,14 @@ function setValue(changes, label, object, key, value) {
19
19
  }
20
20
  }
21
21
 
22
+ function setJsonValue(changes, label, object, key, value) {
23
+ const previous = object[key];
24
+ if (JSON.stringify(previous) !== JSON.stringify(value)) {
25
+ changes.push({ label, previous, value });
26
+ object[key] = value === undefined ? undefined : JSON.parse(JSON.stringify(value));
27
+ }
28
+ }
29
+
22
30
  function syncReleaseVersion(rootDir) {
23
31
  const root = path.resolve(rootDir);
24
32
  const packageFile = path.join(root, 'package.json');
@@ -32,10 +40,7 @@ function syncReleaseVersion(rootDir) {
32
40
  throw new Error(`Invalid package.json version: ${version || '<missing>'}`);
33
41
  }
34
42
 
35
- const parsed = version.match(/^(\d+)\.(\d+)\.(\d+)/);
36
- const releaseNo = parsed
37
- ? (parsed[1] === '7' && parsed[2] === '9' ? parsed[3] : `${parsed[1]}${parsed[2]}${parsed[3]}`)
38
- : version.replace(/[^0-9A-Za-z]+/g, '-');
43
+ const releaseNo = version.match(/^7\.9\.(\d+)/)?.[1] || version.replace(/[^0-9A-Za-z]+/g, '-');
39
44
  const expectedBase = `https://unpkg.com/iobroker.eos-admin@${version}`;
40
45
  const changes = [];
41
46
 
@@ -65,7 +70,40 @@ function syncReleaseVersion(rootDir) {
65
70
  update(json) {
66
71
  setValue(changes, 'package-lock.json version', json, 'version', version);
67
72
  if (json.packages?.['']) {
68
- setValue(changes, 'package-lock.json packages[""] version', json.packages[''], 'version', version);
73
+ const rootPackage = json.packages[''];
74
+ setValue(changes, 'package-lock.json packages[""] version', rootPackage, 'version', version);
75
+ // A merge archive can update package.json while leaving stale release scripts,
76
+ // files and policy metadata in package-lock.json. npm reads the lock root during
77
+ // packaging, so keep every release-critical root field canonical as well.
78
+ for (const key of [
79
+ 'name',
80
+ 'description',
81
+ 'license',
82
+ 'author',
83
+ 'private',
84
+ 'engines',
85
+ 'main',
86
+ 'files',
87
+ 'scripts',
88
+ 'dependencies',
89
+ 'devDependencies',
90
+ 'overrides',
91
+ 'keywords',
92
+ 'publishConfig',
93
+ 'nexowattReleasePolicy',
94
+ 'publisher',
95
+ 'copyright',
96
+ ]) {
97
+ if (Object.prototype.hasOwnProperty.call(pkg, key)) {
98
+ setJsonValue(
99
+ changes,
100
+ `package-lock.json packages[""] ${key}`,
101
+ rootPackage,
102
+ key,
103
+ pkg[key],
104
+ );
105
+ }
106
+ }
69
107
  }
70
108
  },
71
109
  },
@@ -3,11 +3,10 @@
3
3
  const fs=require('node:fs');const path=require('node:path');const root=path.resolve(__dirname,'..');const read=rel=>fs.readFileSync(path.join(root,rel),'utf8');const fail=msg=>{console.error(`[NexoWatt EOS UI overview runtime] ${msg}`);process.exit(1)};
4
4
  const runtime=read('adminWww/js/eos-ems-overview.js');const source=read('src-admin/public/js/eos-ems-overview.js');const css=read('adminWww/css/eos-ems-overview.css');const index=read('adminWww/index.html');const role=read('adminWww/js/eos-role-ui.js');const shell=read('adminWww/js/nexowatt-native-shell.js');const intro=read('src-admin/src/tabs/Intro.tsx');const introBundle=read('adminWww/assets/Intro-DkwRiz1n-v84.js');const bootstrap=read('adminWww/assets/bootstrap-COulQZax-v84.js');const generic=read('adminWww/assets/index-D2ymscJA-v84.js');
5
5
  if(runtime!==source)fail('runtime source/build drift');
6
+ if(runtime.includes('Vollständige EMS-Diagnose öffnen'))fail('unsupported EMS link remains');
6
7
  for(const marker of ['nexowatt-ems-overview-v1','nexowatt-ui.*.info.adminOverview.*','system.adapter.nexowatt-ui.*.alive','POLL_MS = 5000','STALE_MS = 20000',"REQUIRED_UI_VERSION = '0.8.198'",'getForeignStates','Keine Kachel besitzt Schreibhoheit','currentDecisions','§14a-Fallback','nexowatt-eos-admin-socket-ready','readUiAdapterInfo','socketFromReact'])if(!runtime.includes(marker))fail(`runtime marker missing: ${marker}`);
7
8
  for(const forbidden of ['setForeignState','setState(','sendToHost','writeFile','extendObject'])if(runtime.includes(forbidden))fail(`read-only runtime contains ${forbidden}`);
8
- for(const marker of ['eos-ems-overview.css?v=7100','eos-ems-overview.js?v=7100','nexowatt-stable-v7100.js?v=7100','eos-release-watch.js?v=7100'])if(!index.includes(marker))fail(`index marker missing: ${marker}`);
9
- if(runtime.includes('Vollständige EMS-Diagnose öffnen'))fail('unsupported EMS destination remains visible');
10
- if(!intro.includes('eos-overview-edit-controls')||!introBundle.includes('eos-overview-edit-controls'))fail('edit control is not inside bottom card grid');
9
+ for(const marker of ['eos-ems-overview.css?v=7101','eos-ems-overview.js?v=7101','nexowatt-stable-v7101.js?v=7101'])if(!index.includes(marker))fail(`index marker missing: ${marker}`);
11
10
  if(!css.includes('#eos-ems-overview-runtime')||!css.includes('.eos-ems-overview-metrics')||!css.includes('startup and compatibility messages stay compact'))fail('EMS card CSS incomplete');
12
11
  if(role.includes('overview.innerHTML = `')||!role.includes('never cover the real Admin Intro'))fail('obsolete role overview still active');
13
12
  if(!shell.includes("label: 'Installateur'")||!shell.includes("label: 'Endkunde'")||!shell.includes('eosNativeReactOverview'))fail('native overview role bridge incomplete');
@@ -41,20 +41,23 @@ if (pkg.scripts['test:eos-version-sync'] !== 'node tools/nexowatt-version-sync-s
41
41
  if (pkg.scripts['verify:eos-merge'] !== 'node tools/nexowatt-merge-update-selftest.cjs') fail('merge update selftest script is missing or incorrect');
42
42
  if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-version-sync-selftest.cjs')) fail('version sync selftest is not part of check:eos-stability');
43
43
  if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-merge-update-selftest.cjs')) fail('merge update selftest is not part of check:eos-stability');
44
- if (pkg.scripts['check:eos-stable-v7100'] !== 'node tools/nexowatt-stable-v7100-selftest.cjs') fail('stable v7100 selftest script is missing or incorrect');
45
- if (pkg.scripts['test:eos-password-write'] !== 'node tools/nexowatt-password-write-selftest.cjs') fail('password write selftest script is missing');
46
- if (pkg.scripts['test:eos-request-security'] !== 'node tools/nexowatt-request-security-selftest.cjs') fail('request security selftest script is missing');
47
- if (pkg.scripts['test:eos-cache-update'] !== 'node tools/nexowatt-cache-update-selftest.cjs') fail('cache update selftest script is missing');
48
- for (const marker of ['nexowatt-password-write-selftest.cjs','nexowatt-request-security-selftest.cjs','nexowatt-cache-update-selftest.cjs']) if (!pkg.scripts['check:eos-stability']?.includes(marker)) fail(`stability check must execute ${marker}`);
44
+ if (pkg.scripts['check:eos-stable-v7101'] !== 'node tools/nexowatt-stable-v7101-selftest.cjs') fail('stable v7101 selftest script is missing or incorrect');
49
45
  if (!pkg.scripts.prepublishOnly?.startsWith('npm run sync:eos-version && npm run prepare:eos-release-defaults && npm run check:eos-publish-channel')) fail('prepublishOnly must synchronize versions and normalize release defaults before the publish channel guard');
50
46
  if (pkg.scripts['precheck:eos-package'] !== 'npm run sync:eos-version && npm run prepare:eos-release-defaults && npm run clean:eos-runtime') fail('precheck:eos-package must synchronize versions, normalize release defaults and clean stale runtime files');
51
47
  if (pkg.scripts['precheck:eos-stability'] !== 'npm run sync:eos-version && npm run prepare:eos-release-defaults && npm run clean:eos-runtime') fail('precheck:eos-stability must synchronize versions, normalize release defaults and clean stale runtime files');
52
48
  if (pkg.scripts['test:eos-publish-channel'] !== 'node tools/nexowatt-publish-channel-selftest.cjs') fail('publish channel selftest script is missing');
53
49
  if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-publish-channel-selftest.cjs')) fail('stability check must execute the publish channel selftest');
54
50
  if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-release-defaults-selftest.cjs')) fail('stability check must execute the release-default selftest');
51
+ if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-password-write-selftest.cjs')) fail('password-write selftest is not part of check:eos-stability');
52
+ if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-account-password-flow-selftest.cjs')) fail('account-password flow selftest is not part of check:eos-stability');
55
53
  if (io.native?.auth !== true) fail('fresh sales installations must default to authenticated access');
56
54
  if (io.native?.eosHideLegacyBackupFromNonAdmins !== true) fail('internal BackItUp reserve must default to Admin/Service visibility');
57
55
  if (io.native?.nexowattHideLegacyBackupFromNonAdmins !== true) fail('NexoWatt backup visibility compatibility flag must default to true');
56
+ for (const key of ['description', 'files', 'scripts', 'nexowattReleasePolicy']) {
57
+ if (JSON.stringify(pkgLock.packages?.['']?.[key]) !== JSON.stringify(pkg[key])) {
58
+ fail(`package-lock root ${key} must match package.json`);
59
+ }
60
+ }
58
61
  for (const [label, value] of [
59
62
  ['io-package common.version', io.common?.version],
60
63
  ['io-package top-level version', io.version],
@@ -104,7 +107,6 @@ for (const file of [
104
107
  'adminWww/js/eos-basic-settings.js',
105
108
  'adminWww/js/eos-role-ui.js',
106
109
  'adminWww/js/eos-account-management.js',
107
- 'adminWww/js/eos-release-watch.js',
108
110
  'adminWww/js/eos-assistant.js',
109
111
  'adminWww/css/nexowatt-native-shell.css',
110
112
  'adminWww/img/eos/nexowatt-192.png',
@@ -115,14 +117,14 @@ for (const file of [
115
117
  'LICENSE',
116
118
  'NEXOWATT_PROPRIETARY_LICENSE.md',
117
119
  'THIRD_PARTY_NOTICES.md',
118
- 'README_STABILITY_V7.10.0.md',
119
- 'RELEASE_NOTES_V7.10.0.md',
120
- 'PUBLISH_STABLE_V7.10.0.md',
121
- 'INSTALL_TEST_V7.10.0.md',
122
- 'RELEASE_ACCEPTANCE_V7.10.0.md',
120
+ 'README_STABILITY_V7.10.1.md',
121
+ 'RELEASE_NOTES_V7.10.1.md',
122
+ 'PUBLISH_STABLE_V7.10.1.md',
123
+ 'INSTALL_TEST_V7.10.1.md',
124
+ 'RELEASE_ACCEPTANCE_V7.10.1.md',
123
125
  'MERGE_UPDATE.ps1',
124
126
  'MERGE_UPDATE.cmd',
125
- 'MERGE_UPDATE_README_V7.10.0.md',
127
+ 'MERGE_UPDATE_README_V7.10.1.md',
126
128
  'tools/nexowatt-patch-built-frontend.cjs',
127
129
  'tools/nexowatt-native-shell-selftest.cjs',
128
130
  'tools/nexowatt-clean-legacy-runtime.cjs',
@@ -133,9 +135,6 @@ for (const file of [
133
135
  'tools/nexowatt-role-access-selftest.cjs',
134
136
  'tools/nexowatt-first-login-selftest.cjs',
135
137
  'tools/nexowatt-account-management-selftest.cjs',
136
- 'tools/nexowatt-password-write-selftest.cjs',
137
- 'tools/nexowatt-request-security-selftest.cjs',
138
- 'tools/nexowatt-cache-update-selftest.cjs',
139
138
  'tools/nexowatt-modern-ui-selftest.cjs',
140
139
  'tools/nexowatt-login-layout-selftest.cjs',
141
140
  'tools/nexowatt-internal-reserve-selftest.cjs',
@@ -143,12 +142,14 @@ for (const file of [
143
142
  'tools/nexowatt-sync-release-version.cjs',
144
143
  'tools/nexowatt-version-sync-selftest.cjs',
145
144
  'tools/nexowatt-merge-update-selftest.cjs',
146
- 'tools/nexowatt-stable-v7100-selftest.cjs',
145
+ 'tools/nexowatt-stable-v7101-selftest.cjs',
146
+ 'tools/nexowatt-password-write-selftest.cjs',
147
+ 'tools/nexowatt-account-password-flow-selftest.cjs',
147
148
  'tools/nexowatt-ems-overview-selftest.cjs',
148
149
  'tools/nexowatt-ui-overview-runtime-selftest.cjs',
149
150
  'adminWww/js/eos-ems-overview.js',
150
151
  'adminWww/css/eos-ems-overview.css',
151
- 'adminWww/static/js/nexowatt-stable-v7100.js',
152
+ 'adminWww/static/js/nexowatt-stable-v7101.js',
152
153
  'src-admin/src/components/Intro/NexoWattEmsOverview.tsx',
153
154
  'tools/nexowatt-publish-channel-guard.cjs',
154
155
  'tools/nexowatt-publish-channel-selftest.cjs',
@@ -176,8 +177,8 @@ const shellTag = String(buildInfo.shellCacheTag || shellCache);
176
177
  if (!runtime || !Number.isFinite(runtimeNumber)) fail(`invalid runtimeEntry ${runtime}`);
177
178
  if (!Number.isFinite(shellCache)) fail(`invalid shellCacheVersion ${buildInfo.shellCacheVersion}`);
178
179
  for (const marker of [
179
- `hostInit-${runtime}.js?v=${shellTag}`,
180
- `index-CQZugZ1z-${runtime}.js?v=${shellTag}`,
180
+ `hostInit-${runtime}.js?v=${runtimeNumber}`,
181
+ `index-CQZugZ1z-${runtime}.js?v=${runtimeNumber}`,
181
182
  `eos-manual-write-policy.js?v=${shellTag}`,
182
183
  `nexowatt-native-shell.css?v=${shellTag}`,
183
184
  `nexowatt-native-shell.js?v=${shellTag}`,
@@ -187,13 +188,13 @@ for (const marker of [
187
188
  if (!index.includes(`eos-role-bootstrap.js?v=${shellTag}`)) fail('role bootstrap cache key mismatch');
188
189
  if (!index.includes(`eos-branding-sanitizer.js?v=${shellTag}`)) fail('branding sanitizer cache key mismatch');
189
190
  if (!index.includes(`eos-role-ui.js?v=${shellTag}`)) fail('role UI cache key mismatch');
190
- if (!index.includes(`eos-account-management.js?v=${shellTag}`)) fail('account management cache key mismatch');
191
+ if (index.includes('eos-account-management.js')) fail('custom account-management runtime must not be loaded in standard password mode');
192
+ if (!index.includes(`eos-auto-update.js?v=${shellTag}`)) fail('auto-update cache key mismatch');
191
193
  if (!index.includes(`eos-assistant.js?v=${shellTag}`)) fail('EOS Assist cache key mismatch');
192
194
  if (!index.includes(`eos-ems-overview.js?v=${shellTag}`)) fail('EMS overview cache key mismatch');
193
195
  if (!index.includes(`eos-ems-overview.css?v=${shellTag}`)) fail('EMS overview CSS cache key mismatch');
194
- if (!index.includes(`nexowatt-stable-v7100.js?v=${shellTag}`)) fail('stable v7100 cache key mismatch');
195
- if (!index.includes(`eos-release-watch.js?v=${shellTag}`)) fail('release watcher cache key mismatch');
196
- if (index.indexOf(`eos-manual-write-policy.js?v=${shellTag}`) > index.indexOf(`hostInit-${runtime}.js?v=${shellTag}`)) fail('manual-write policy must load before the React runtime');
196
+ if (!index.includes(`nexowatt-stable-v7101.js?v=${shellTag}`)) fail('stable v98 cache key mismatch');
197
+ if (index.indexOf(`eos-manual-write-policy.js?v=${shellTag}`) > index.indexOf(`hostInit-${runtime}.js?v=${runtimeNumber}`)) fail('manual-write policy must load before the React runtime');
197
198
  if (!index.includes('class="eos-native-shell"')) fail('native NexoWatt shell class missing');
198
199
  for (const legacy of ['eos-branding.js', 'eos-security-ui.js', 'eos-console-quiet.js', 'eos-objects-state-tools.js']) {
199
200
  if (index.includes(legacy)) fail(`legacy browser overlay still loaded: ${legacy}`);
@@ -241,7 +242,7 @@ if (accountManagement.includes("launcher.className = 'eos-account-management-lau
241
242
  if (accountManagement.includes('new MutationObserver')) fail('account-management runtime adds a second broad DOM observer');
242
243
  if (read('src-admin/public/js/eos-account-management.js') !== accountManagement) fail('account-management source/build drift');
243
244
  const roleBootstrap = read('adminWww/js/eos-role-bootstrap.js');
244
- if (!roleBootstrap.includes('installIntegratedFirstLogin') || !roleBootstrap.includes('v7100 normal-login first activation')) fail('compact integrated first-login is incomplete');
245
+ if (roleBootstrap.includes('installIntegratedFirstLogin(base);') || roleBootstrap.includes('showFirstLoginPassword(resolved, base);')) fail('forced first-login UI must be disabled');
245
246
  if (/data-eos-account=|selector\.innerHTML/.test(roleBootstrap)) fail('login role buttons must not enlarge the normal login card');
246
247
  if (!roleBootstrap.includes('nexowatt/account/passwordless-status') || !roleBootstrap.includes('eligibility.allowed')) fail('server-checked first-login eligibility is incomplete');
247
248
  if (roleBootstrap.includes('installPasswordlessFirstLoginLauncher')) fail('old first-login launcher remains active');
@@ -268,8 +269,8 @@ if (pkg.scripts['clean:eos-runtime'] !== 'node tools/nexowatt-clean-legacy-runti
268
269
  if (pkg.scripts.prepack !== 'node tools/nexowatt-sync-release-version.cjs --quiet && node tools/nexowatt-clean-legacy-runtime.cjs --quiet') fail('prepack must silently synchronize versions and clean stale runtime files');
269
270
  if (!pkg.scripts.build?.includes('npm run clean:eos-runtime')) fail('build must finish with runtime cleanup');
270
271
  if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-role-access-selftest.cjs')) fail('role access selftest is not part of check:eos-stability');
271
- if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-first-login-selftest.cjs')) fail('first-login selftest is not part of check:eos-stability');
272
- if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-account-management-selftest.cjs')) fail('account-management selftest is not part of check:eos-stability');
272
+ if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-account-password-flow-selftest.cjs')) fail('standard account-password flow selftest is not part of check:eos-stability');
273
+ if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-account-password-flow-selftest.cjs')) fail('standard account-password management selftest is not part of check:eos-stability');
273
274
  if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-modern-ui-selftest.cjs')) fail('modern-UI selftest is not part of check:eos-stability');
274
275
  if (pkg.scripts['test:eos-login-layout'] !== 'node tools/nexowatt-login-layout-selftest.cjs') fail('login-layout selftest script is missing or incorrect');
275
276
  if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-login-layout-selftest.cjs')) fail('login-layout selftest is not part of check:eos-stability');
@@ -24,7 +24,19 @@ try {
24
24
  const currentPkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
25
25
  const version = currentPkg.version;
26
26
  writeJson('package.json', currentPkg);
27
- writeJson('package-lock.json', { name: currentPkg.name, version: '7.9.92', packages: { '': { version: '7.9.92' } } });
27
+ writeJson('package-lock.json', {
28
+ name: currentPkg.name,
29
+ version: '7.9.92',
30
+ packages: {
31
+ '': {
32
+ version: '7.9.92',
33
+ description: 'stale description',
34
+ files: ['README_STABILITY_V7.9.92.md'],
35
+ scripts: { 'check:eos-stable-v92': 'node stale.js' },
36
+ nexowattReleasePolicy: { distTag: 'latest', purpose: 'stale 7.9.92 policy' },
37
+ },
38
+ },
39
+ });
28
40
  writeJson('io-package.json', {
29
41
  version: '7.9.92',
30
42
  common: {
@@ -75,6 +87,11 @@ try {
75
87
  ]) {
76
88
  if (actual !== version) fail(`${label} was not synchronized: ${actual}`);
77
89
  }
90
+ for (const key of ['description', 'files', 'scripts', 'nexowattReleasePolicy']) {
91
+ if (JSON.stringify(lock.packages[''][key]) !== JSON.stringify(currentPkg[key])) {
92
+ fail(`package-lock root ${key} was not synchronized`);
93
+ }
94
+ }
78
95
  if (io.common.meta !== `${expectedBase}/io-package.json`) fail('io-package URL was not synchronized');
79
96
  if (entry.meta !== `${expectedBase}/io-package.json`) fail('repository entry URL was not synchronized');
80
97
  if (!fs.existsSync(path.join(temp, build.repositoryEntry))) fail('current repository entry was not created');
@@ -1,11 +0,0 @@
1
- # Installationstest 7.10.0
2
-
3
- ```bash
4
- cd /opt/iobroker
5
- iobroker stop eos-admin.0
6
- npm install --no-save --omit=dev iobroker.eos-admin@7.10.0
7
- iobroker upload eos-admin
8
- iobroker restart eos-admin.0
9
- ```
10
-
11
- Danach Browser mit `Strg + F5` vollständig neu laden. Für EMS-Livedaten muss `iobroker.nexowatt-ui` mindestens `0.8.198` installiert und gestartet sein.
@@ -1,5 +0,0 @@
1
- # NexoWatt EOS Admin 7.10.0 – Merge-Update
2
-
3
- Die flache Merge-ZIP direkt in den vorhandenen Repository-Ordner entpacken und anschließend `MERGE_UPDATE.cmd` ausführen.
4
-
5
- Enthalten sind alle Änderungen aus 7.9.96 einschließlich nativer Rollenübersicht, persönlicher Erstkennwortvergabe und EMS-Diagnose sowie die Korrekturen für Login-Geometrie und produktive AdminConnection.
@@ -1,15 +0,0 @@
1
- # Stable-Veröffentlichung 7.10.0
2
-
3
- ```powershell
4
- npm login
5
- npm whoami
6
- npm run check:eos-release
7
- npm publish --dry-run --tag latest
8
- npm publish --tag latest
9
- ```
10
-
11
- Das geprüfte Tarball kann alternativ direkt veröffentlicht werden:
12
-
13
- ```powershell
14
- npm publish .\iobroker.eos-admin-7.10.0.tgz --tag latest
15
- ```
@@ -1,3 +0,0 @@
1
- # NexoWatt EOS Admin 7.10.0 – Stabilität
2
-
3
- Die native NexoWatt-Autoaktualisierung ist standardmäßig aktiv und kann im Admin-Cockpit deaktiviert werden. Sie setzt ausschließlich für installierte NexoWatt-Adapter aus dem erkannten Stable-Repository die ioBroker-Richtlinie `major`. Fremdadapter bleiben unverändert. Beim Abschalten werden vorherige Richtlinien wiederhergestellt.
@@ -1,11 +0,0 @@
1
- # Release-Abnahme 7.10.0
2
-
3
- - [ ] Login-Karte ist ordentlich proportioniert und nur moderat größer als vor 7.9.96.
4
- - [ ] Normale Anmeldung, falsches Passwort und persönliche Erstkennwortvergabe erzeugen keine innere Scrollleiste.
5
- - [ ] Erstkennwort `nexowatt` führt zur verpflichtenden persönlichen Kennwortvergabe.
6
- - [ ] Native Adapterkarten sind bei Admin, Installateur und Endkunde sichtbar, soweit die Rolle sie freigibt.
7
- - [ ] NexoWatt-UI-Karte ist in der Übersicht vorhanden.
8
- - [ ] EMS-Kachel zeigt nach Verbindungsaufbau Livewerte aus `nexowatt-ui.*.info.adminOverview.*`.
9
- - [ ] UI-Version unter `0.8.198` wird verständlich gemeldet.
10
- - [ ] Die EMS-Kachel enthält keinen Schreibpfad.
11
- - [ ] `npm run check:eos-release` und `npm publish --dry-run --tag latest` sind vollständig grün.
@@ -1,17 +0,0 @@
1
- # NexoWatt EOS Admin 7.10.0
2
-
3
- ## Korrekturen
4
-
5
- - Dauerhafter vertikaler Scrollbalken in der Cockpit-/Übersichtsfläche.
6
- - Login wieder kompakt und sauber ausgerichtet; keine überlappenden Feldbeschriftungen.
7
- - Erstpasswort und Passwort-Reset verwenden die native ioBroker-Passwortverwaltung mit kanonischer Benutzer-ID und Kompatibilitäts-Fallback.
8
- - Passwort-Metadaten werden erst nach Hash- und Credential-Prüfung freigegeben.
9
- - Präzise Fehlermeldungen für nicht gespeicherte oder nicht verifizierte Passwörter.
10
- - Cache-Tag 7100 und automatische Release-Überwachung gegen gemischte Browserstände.
11
- - Automatische Stable-Updates für installierte NexoWatt-Adapter bleiben standardmäßig aktiv und abschaltbar.
12
-
13
- ## Unverändert
14
-
15
- - EMS-Diagnose bleibt rein lesend.
16
- - Der nicht vorhandene Link zur vollständigen EMS-Diagnose bleibt entfernt.
17
- - Der Bearbeitungsstift bleibt im unteren Kartenbereich.
@@ -1,111 +0,0 @@
1
- (() => {
2
- 'use strict';
3
- const VERSION = 'v7100-scroll-login-auto-update';
4
- const MANAGED_USERS = new Set(['installer', 'guest', 'user']);
5
- const normalizeUser = value => String(value || '').trim().toLowerCase().replace(/^system\.user\./, '');
6
- const INTRO_STYLE_PROPERTIES = [
7
- 'position', 'top', 'right', 'bottom', 'left', 'width', 'height', 'min-height', 'max-height',
8
- 'margin', 'overflow-x', 'overflow-y', 'overscroll-behavior', 'scrollbar-gutter',
9
- ];
10
- const isIntroRoute = () => {
11
- const hash = decodeURIComponent(window.location.hash || '').toLowerCase();
12
- const explicitTab = hash.match(/(?:^|[#/])(tab-[a-z0-9_-]+)(?:[/?&]|$)/)?.[1] || '';
13
- if (explicitTab) return explicitTab === 'tab-intro';
14
- return document.documentElement.classList.contains('eos-route-intro')
15
- || !hash
16
- || hash === '#'
17
- || hash === '#/';
18
- };
19
- const bindScrollInput = paper => {
20
- if (paper.dataset.nexowattScrollBound === 'true') return;
21
- paper.dataset.nexowattScrollBound = 'true';
22
- paper.addEventListener('wheel', event => {
23
- if (!event.deltaY || paper.scrollHeight <= paper.clientHeight + 1) return;
24
- const before = paper.scrollTop;
25
- paper.scrollTop += event.deltaY;
26
- if (paper.scrollTop !== before) event.preventDefault();
27
- }, { passive: false });
28
- paper.addEventListener('keydown', event => {
29
- if (paper.scrollHeight <= paper.clientHeight + 1) return;
30
- const step = Math.max(120, Math.round(paper.clientHeight * .78));
31
- if (event.key === 'PageDown') paper.scrollTop += step;
32
- else if (event.key === 'PageUp') paper.scrollTop -= step;
33
- else if (event.key === 'Home' && event.ctrlKey) paper.scrollTop = 0;
34
- else if (event.key === 'End' && event.ctrlKey) paper.scrollTop = paper.scrollHeight;
35
- else return;
36
- event.preventDefault();
37
- });
38
- };
39
- const disableCockpitScroll = () => {
40
- const root = document.documentElement;
41
- root.classList.remove('eos-intro-scroll-enabled');
42
- const paper = document.getElementById('app-paper');
43
- if (!paper || paper.dataset.nexowattScrollable !== 'true') return;
44
- delete paper.dataset.nexowattScrollable;
45
- for (const property of INTRO_STYLE_PROPERTIES) paper.style.removeProperty(property);
46
- };
47
- const ensureCockpitScroll = () => {
48
- const root = document.documentElement;
49
- const paper = document.getElementById('app-paper');
50
- if (!paper || !root.classList.contains('eos-app')) return;
51
- if (!isIntroRoute()) {
52
- disableCockpitScroll();
53
- return;
54
- }
55
- // eos-route-* is owned by the native shell; only add our dedicated scroll flag.
56
- root.classList.add('eos-intro-scroll-enabled');
57
- paper.dataset.nexowattScrollable = 'true';
58
- if (paper.tabIndex < 0) paper.tabIndex = 0;
59
- const styles = {
60
- position: 'fixed',
61
- top: 'var(--nx-content-top, 154px)',
62
- right: 'var(--nx-page-x, 12px)',
63
- bottom: '8px',
64
- left: 'var(--nx-page-x, 12px)',
65
- width: 'auto',
66
- height: 'auto',
67
- minHeight: '0',
68
- maxHeight: 'none',
69
- margin: '0',
70
- overflowX: 'hidden',
71
- overflowY: 'scroll',
72
- overscrollBehavior: 'contain',
73
- scrollbarGutter: 'stable',
74
- };
75
- for (const [name, value] of Object.entries(styles)) {
76
- paper.style.setProperty(name.replace(/[A-Z]/g, char => `-${char.toLowerCase()}`), value, 'important');
77
- }
78
- bindScrollInput(paper);
79
- };
80
- const apply = () => {
81
- document.documentElement.classList.add('eos-assist-disabled');
82
- document.querySelectorAll('.eos-assist-root,.eos-assist-header-root,[data-eos-assist-root],.eos-passwordless-launcher')
83
- .forEach(element => element.remove());
84
- const username = document.querySelector('input#username,input[name="username"]');
85
- const password = document.querySelector('input#password,input[name="password"]');
86
- const submit = document.querySelector('button[type="submit"],input[type="submit"]');
87
- if (submit && MANAGED_USERS.has(normalizeUser(username?.value)) && !String(password?.value || '')) {
88
- submit.disabled = true;
89
- submit.classList.add('Mui-disabled');
90
- submit.setAttribute('aria-disabled', 'true');
91
- }
92
- ensureCockpitScroll();
93
- };
94
- let scheduled = false;
95
- const schedule = () => {
96
- if (scheduled) return;
97
- scheduled = true;
98
- requestAnimationFrame(() => { scheduled = false; apply(); });
99
- };
100
- const start = () => {
101
- apply();
102
- new MutationObserver(schedule).observe(document.documentElement, { subtree: true, childList: true });
103
- document.addEventListener('input', schedule, true);
104
- document.addEventListener('change', schedule, true);
105
- window.addEventListener('hashchange', schedule);
106
- window.addEventListener('resize', schedule, { passive: true });
107
- window.setInterval(ensureCockpitScroll, 750);
108
- };
109
- document.readyState === 'loading' ? document.addEventListener('DOMContentLoaded', start, { once: true }) : start();
110
- window.NEXOWATT_EOS_STABLE_V7100 = Object.freeze({ version: VERSION, refresh: apply, ensureCockpitScroll, disableCockpitScroll });
111
- })();