iobroker.eos-admin 7.9.93 → 7.9.95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INSTALL_TEST_V7.9.95.md +15 -0
- package/NEXOWATT_EOS_BUILD_INFO.json +23 -18
- package/NEXOWATT_EOS_STABLE_7.9.94.md +16 -0
- package/{PUBLISH_STABLE_V7.9.93.md → PUBLISH_STABLE_V7.9.95.md} +2 -5
- package/README_STABILITY_V7.9.95.md +12 -0
- package/RELEASE_ACCEPTANCE_V7.9.95.md +11 -0
- package/RELEASE_NOTES_V7.9.95.md +12 -0
- package/adminWww/assets/Adapters-B5_jQ7DE-v84.js +1 -1
- package/adminWww/css/nexowatt-native-shell.css +4 -156
- package/adminWww/index.html +13 -11
- package/adminWww/js/eos-account-management.js +1 -1
- package/adminWww/js/eos-assistant.js +5 -556
- package/adminWww/js/eos-basic-settings.js +1 -1
- package/adminWww/js/eos-branding-sanitizer.js +1 -3
- package/adminWww/js/eos-native-security.js +1 -1
- package/adminWww/js/eos-policy-client.js +1 -1
- package/adminWww/js/eos-role-bootstrap.js +2 -2
- package/adminWww/js/eos-role-ui.js +1 -1
- package/adminWww/js/nexowatt-native-shell.js +1 -1
- package/adminWww/static/js/nexowatt-stable-login.js +79 -0
- package/build/lib/chat/assistantKnowledge.js +6 -21
- package/build/lib/chat/chatOrchestrator.js +38 -33
- package/build/lib/chat/mcpClientManager.js +1 -1
- package/build/lib/nexowattStableAuth.js +74 -0
- package/build/lib/web.js +24 -7
- package/build/main.js +123 -308
- package/io-package.json +20 -16
- package/package.json +23 -32
- package/INSTALL_TEST_V7.9.93.md +0 -12
- package/README_STABILITY_V7.9.93.md +0 -15
- package/RELEASE_ACCEPTANCE_V7.9.93.md +0 -15
- package/RELEASE_NOTES_V7.9.93.md +0 -10
- package/tools/nexowatt-account-management-selftest.cjs +0 -61
- package/tools/nexowatt-clean-legacy-runtime.cjs +0 -147
- package/tools/nexowatt-ensure-release-defaults.cjs +0 -72
- package/tools/nexowatt-generate-eos-admin-repo-entry.cjs +0 -37
- package/tools/nexowatt-generate-repo-entry.cjs +0 -3
- package/tools/nexowatt-internal-reserve-selftest.cjs +0 -57
- package/tools/nexowatt-login-layout-selftest.cjs +0 -62
- package/tools/nexowatt-modern-ui-selftest.cjs +0 -50
- package/tools/nexowatt-patch-repo.cjs +0 -54
- package/tools/nexowatt-publish-channel-guard.cjs +0 -58
- package/tools/nexowatt-release-defaults-selftest.cjs +0 -29
- package/tools/nexowatt-runtime-cleanup-selftest.cjs +0 -47
- package/tools/nexowatt-validate-package.cjs +0 -246
package/build/main.js
CHANGED
|
@@ -164,6 +164,79 @@ let lastRepoUpdate;
|
|
|
164
164
|
let objects = {};
|
|
165
165
|
const secret = 'Zgfr56gFe87jJOM'; // Will be generated by first start
|
|
166
166
|
let systemLanguage = 'en';
|
|
167
|
+
|
|
168
|
+
const EOS_STABLE_INITIAL_PASSWORD = 'nexowatt';
|
|
169
|
+
const EOS_STABLE_ACCOUNTS = [
|
|
170
|
+
{ user: 'installer', role: 'installer' },
|
|
171
|
+
{ user: 'guest', role: 'customer' },
|
|
172
|
+
{ user: 'user', role: 'customer' },
|
|
173
|
+
];
|
|
174
|
+
|
|
175
|
+
async function ensureEosStableGroupMembership(adapter, user, role) {
|
|
176
|
+
const candidateGroups = role === 'installer'
|
|
177
|
+
? ['system.group.installer', 'system.group.installateur', 'system.group.installers']
|
|
178
|
+
: ['system.group.endkunde', 'system.group.endkunden'];
|
|
179
|
+
const userId = `system.user.${user}`;
|
|
180
|
+
for (const groupId of candidateGroups) {
|
|
181
|
+
const group = await adapter.getForeignObjectAsync(groupId);
|
|
182
|
+
if (!group) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const members = Array.isArray(group.common?.members) ? [...group.common.members] : [];
|
|
186
|
+
if (!members.includes(userId)) {
|
|
187
|
+
members.push(userId);
|
|
188
|
+
await adapter.extendForeignObjectAsync(groupId, { common: { members } }, { user: 'system.user.admin' });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function ensureEosStableInitialAccounts(adapter) {
|
|
194
|
+
for (const account of EOS_STABLE_ACCOUNTS) {
|
|
195
|
+
const id = `system.user.${account.user}`;
|
|
196
|
+
let object = await adapter.getForeignObjectAsync(id);
|
|
197
|
+
let created = false;
|
|
198
|
+
if (!object) {
|
|
199
|
+
await adapter.setForeignObjectAsync(id, {
|
|
200
|
+
type: 'user',
|
|
201
|
+
common: { name: account.user, enabled: true, password: '' },
|
|
202
|
+
native: {},
|
|
203
|
+
}, { user: 'system.user.admin' });
|
|
204
|
+
object = await adapter.getForeignObjectAsync(id);
|
|
205
|
+
created = true;
|
|
206
|
+
}
|
|
207
|
+
if (!object) {
|
|
208
|
+
adapter.log.error(`Cannot prepare EOS account ${account.user}`);
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
const eosNative = object.native || {};
|
|
212
|
+
const resetRequested = eosNative.nexowattResetRequested === true || eosNative.eosResetRequested === true;
|
|
213
|
+
const passwordMissing = !object.common?.password;
|
|
214
|
+
const pendingFirstLogin = eosNative.nexowattPasswordChangeRequired === true || eosNative.eosPasswordChangeRequired === true || eosNative.nexowattFirstLoginPending === true || eosNative.eosFirstLoginRequired === true;
|
|
215
|
+
const stableCredentialMissing = eosNative.nexowattStableInitialCredentialVersion !== 1;
|
|
216
|
+
if (created || passwordMissing || resetRequested || (pendingFirstLogin && stableCredentialMissing)) {
|
|
217
|
+
await adapter.setPasswordAsync(account.user, EOS_STABLE_INITIAL_PASSWORD, { user: 'system.user.admin' });
|
|
218
|
+
await adapter.extendForeignObjectAsync(id, {
|
|
219
|
+
native: {
|
|
220
|
+
...eosNative,
|
|
221
|
+
nexowattManaged: true,
|
|
222
|
+
nexowattRole: account.role,
|
|
223
|
+
nexowattPasswordChangeRequired: true,
|
|
224
|
+
eosPasswordChangeRequired: true,
|
|
225
|
+
nexowattFirstLoginPending: true,
|
|
226
|
+
eosFirstLoginRequired: true,
|
|
227
|
+
nexowattInitialPasswordApplied: true,
|
|
228
|
+
nexowattInitialPasswordVersion: 1,
|
|
229
|
+
nexowattStableInitialCredentialVersion: 1,
|
|
230
|
+
nexowattResetRequested: false,
|
|
231
|
+
eosResetRequested: false,
|
|
232
|
+
},
|
|
233
|
+
}, { user: 'system.user.admin' });
|
|
234
|
+
adapter.log.info(`Prepared EOS ${account.role} account ${account.user} for mandatory first password change`);
|
|
235
|
+
}
|
|
236
|
+
await ensureEosStableGroupMembership(adapter, account.user, account.role);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
167
240
|
class Admin extends adapter_core_1.Adapter {
|
|
168
241
|
/** secret used for the socket connection */
|
|
169
242
|
secret;
|
|
@@ -174,13 +247,8 @@ class Admin extends adapter_core_1.Adapter {
|
|
|
174
247
|
timerNews;
|
|
175
248
|
_tasks;
|
|
176
249
|
changedPasswords = [];
|
|
177
|
-
/**
|
|
250
|
+
/** In-process ioBroker.mcp connection backing the chat helper. Created on first chat message. */
|
|
178
251
|
mcpChat = null;
|
|
179
|
-
/** Role-scoped, read-only MCP connections used by the header EOS Assist. */
|
|
180
|
-
eosAssistMcpChats = new Map();
|
|
181
|
-
/** Per-user throttling and concurrency guard for provider calls. */
|
|
182
|
-
eosAssistLastRequestAt = new Map();
|
|
183
|
-
eosAssistInFlight = new Set();
|
|
184
252
|
/** Periodic guard that keeps EOS safety policy applied even if object events are missed. */
|
|
185
253
|
eosSecurityTimer = null;
|
|
186
254
|
/** Debounce timer for object-change triggered safety checks. */
|
|
@@ -287,6 +355,7 @@ class Admin extends adapter_core_1.Adapter {
|
|
|
287
355
|
systemLanguage = systemConfig.common.language;
|
|
288
356
|
}
|
|
289
357
|
adapter_core_1.I18n.init(__dirname, systemLanguage).catch(e => this.log.error(`Cannot init i18n: ${e}`));
|
|
358
|
+
await ensureEosStableInitialAccounts(this);
|
|
290
359
|
if (!systemConfig.native.secret) {
|
|
291
360
|
(0, node_crypto_1.randomBytes)(24, (_ex, buf) => {
|
|
292
361
|
this.secret = buf.toString('hex');
|
|
@@ -321,300 +390,6 @@ class Admin extends adapter_core_1.Adapter {
|
|
|
321
390
|
});
|
|
322
391
|
return this.mcpChat;
|
|
323
392
|
}
|
|
324
|
-
/** Return a read-only MCP connection whose tool calls run under the authenticated user's ACL. */
|
|
325
|
-
getEosAssistMcp(userId) {
|
|
326
|
-
const key = `${userId}:${systemLanguage}`;
|
|
327
|
-
let manager = this.eosAssistMcpChats.get(key);
|
|
328
|
-
if (!manager) {
|
|
329
|
-
manager = new mcpClientManager_1.McpClientManager(this, {
|
|
330
|
-
defaultUser: userId,
|
|
331
|
-
language: systemLanguage,
|
|
332
|
-
allowSetState: false,
|
|
333
|
-
allowObjectChange: false,
|
|
334
|
-
});
|
|
335
|
-
this.eosAssistMcpChats.set(key, manager);
|
|
336
|
-
}
|
|
337
|
-
return manager;
|
|
338
|
-
}
|
|
339
|
-
normalizeEosAssistSettings(value) {
|
|
340
|
-
const native = value && typeof value === 'object' && !Array.isArray(value)
|
|
341
|
-
? value
|
|
342
|
-
: {};
|
|
343
|
-
const allowed = ['openai', 'anthropic', 'gemini', 'deepseek', 'custom'];
|
|
344
|
-
const rawProvider = typeof native.provider === 'string' ? native.provider : 'anthropic';
|
|
345
|
-
const provider = allowed.includes(rawProvider) ? rawProvider : 'anthropic';
|
|
346
|
-
return {
|
|
347
|
-
provider,
|
|
348
|
-
model: typeof native.model === 'string' ? native.model.trim().substring(0, 200) : '',
|
|
349
|
-
credentialId: typeof native.credentialId === 'string' ? native.credentialId.trim().substring(0, 300) : '',
|
|
350
|
-
baseUrl: typeof native.baseUrl === 'string' ? native.baseUrl.trim().replace(/\/+$/, '').substring(0, 500) : '',
|
|
351
|
-
allowSelfSignedCerts: native.allowSelfSignedCerts === true,
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
isEosAssistConfigured(settings) {
|
|
355
|
-
if (!settings.model) {
|
|
356
|
-
return false;
|
|
357
|
-
}
|
|
358
|
-
return settings.provider === 'custom'
|
|
359
|
-
? !!(settings.baseUrl || settings.credentialId)
|
|
360
|
-
: !!settings.credentialId;
|
|
361
|
-
}
|
|
362
|
-
async readEosAssistSettings() {
|
|
363
|
-
const object = await this.getForeignObjectAsync('system.ai');
|
|
364
|
-
return this.normalizeEosAssistSettings(object?.native);
|
|
365
|
-
}
|
|
366
|
-
sanitizeEosAssistVisibleText(value) {
|
|
367
|
-
const text = String(value || '');
|
|
368
|
-
// Preserve technical IDs and commands inside code spans while keeping product prose NexoWatt-only.
|
|
369
|
-
return text
|
|
370
|
-
.split(/(```[\s\S]*?```|`[^`\n]*`)/g)
|
|
371
|
-
.map((part, index) => (index % 2 ? part : part.replace(/\bioBroker\b/gi, 'NexoWatt EOS')))
|
|
372
|
-
.join('');
|
|
373
|
-
}
|
|
374
|
-
normalizeEosAssistMessages(value) {
|
|
375
|
-
if (!Array.isArray(value)) {
|
|
376
|
-
return [];
|
|
377
|
-
}
|
|
378
|
-
const messages = [];
|
|
379
|
-
let total = 0;
|
|
380
|
-
for (const entry of value.slice(-24)) {
|
|
381
|
-
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
382
|
-
continue;
|
|
383
|
-
}
|
|
384
|
-
const record = entry;
|
|
385
|
-
const role = record.role === 'assistant' ? 'assistant' : record.role === 'user' ? 'user' : null;
|
|
386
|
-
if (!role || typeof record.content !== 'string') {
|
|
387
|
-
continue;
|
|
388
|
-
}
|
|
389
|
-
const content = record.content.trim().substring(0, 6_000);
|
|
390
|
-
if (!content) {
|
|
391
|
-
continue;
|
|
392
|
-
}
|
|
393
|
-
total += content.length;
|
|
394
|
-
if (total > 30_000) {
|
|
395
|
-
break;
|
|
396
|
-
}
|
|
397
|
-
messages.push({ role, content });
|
|
398
|
-
}
|
|
399
|
-
return messages;
|
|
400
|
-
}
|
|
401
|
-
async getEosAssistStatus(socketClient, callback) {
|
|
402
|
-
const userId = socketClient?._acl?.user;
|
|
403
|
-
if (!userId) {
|
|
404
|
-
callback?.({ error: 'Nicht angemeldet.', code: 'notAuthenticated' });
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
const role = this.getEosCachedRole(userId);
|
|
408
|
-
if (this.eosPasswordSetupRequired.has(userId)) {
|
|
409
|
-
callback?.({ error: 'Die persönliche Passwortvergabe muss zuerst abgeschlossen werden.', code: 'passwordSetupRequired' });
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
try {
|
|
413
|
-
const settings = await this.readEosAssistSettings();
|
|
414
|
-
const credentials = role === 'admin' ? await (0, credentials_1.listAiCredentials)(this) : [];
|
|
415
|
-
callback?.({
|
|
416
|
-
success: true,
|
|
417
|
-
user: userId.replace(/^system\.user\./, ''),
|
|
418
|
-
role,
|
|
419
|
-
configured: this.isEosAssistConfigured(settings),
|
|
420
|
-
canConfigure: role === 'admin',
|
|
421
|
-
settings: role === 'admin' ? settings : undefined,
|
|
422
|
-
credentials,
|
|
423
|
-
});
|
|
424
|
-
}
|
|
425
|
-
catch (error) {
|
|
426
|
-
callback?.({ error: error instanceof Error ? error.message : String(error), code: 'statusFailed' });
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
async saveEosAssistSettings(socketClient, payload, callback) {
|
|
430
|
-
const userId = socketClient?._acl?.user;
|
|
431
|
-
if (!userId || this.getEosCachedRole(userId) !== 'admin') {
|
|
432
|
-
callback?.({ error: 'Diese Einstellung ist nur für Admin / NexoWatt Service verfügbar.', code: 'permissionDenied' });
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
435
|
-
const settings = this.normalizeEosAssistSettings(payload);
|
|
436
|
-
if (!settings.model) {
|
|
437
|
-
callback?.({ error: 'Bitte ein Modell eintragen.', code: 'invalidSettings' });
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
|
-
if (settings.provider === 'custom') {
|
|
441
|
-
if (!settings.baseUrl && !settings.credentialId) {
|
|
442
|
-
callback?.({ error: 'Für einen lokalen/individuellen Anbieter wird eine Basis-URL oder ein Zugang benötigt.', code: 'invalidSettings' });
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
if (settings.baseUrl && !/^https?:\/\//i.test(settings.baseUrl)) {
|
|
446
|
-
callback?.({ error: 'Die Basis-URL muss mit http:// oder https:// beginnen.', code: 'invalidSettings' });
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
else if (!settings.credentialId) {
|
|
451
|
-
callback?.({ error: 'Bitte einen gespeicherten KI-Zugang auswählen.', code: 'invalidSettings' });
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
try {
|
|
455
|
-
const current = await this.getForeignObjectAsync('system.ai');
|
|
456
|
-
const next = {
|
|
457
|
-
...(current || {}),
|
|
458
|
-
_id: 'system.ai',
|
|
459
|
-
type: 'config',
|
|
460
|
-
common: {
|
|
461
|
-
...(current?.common || {}),
|
|
462
|
-
name: 'EOS Assist Einstellungen',
|
|
463
|
-
expert: true,
|
|
464
|
-
},
|
|
465
|
-
native: {
|
|
466
|
-
...(current?.native || {}),
|
|
467
|
-
...settings,
|
|
468
|
-
},
|
|
469
|
-
};
|
|
470
|
-
await this.setForeignObjectAsync('system.ai', next);
|
|
471
|
-
callback?.({ success: true, configured: true, settings });
|
|
472
|
-
}
|
|
473
|
-
catch (error) {
|
|
474
|
-
callback?.({ error: error instanceof Error ? error.message : String(error), code: 'saveFailed' });
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
async testEosAssistSettings(socketClient, payload, callback) {
|
|
478
|
-
const userId = socketClient?._acl?.user;
|
|
479
|
-
if (!userId || this.getEosCachedRole(userId) !== 'admin') {
|
|
480
|
-
callback?.({ error: 'Der Verbindungstest ist nur für Admin / NexoWatt Service verfügbar.', code: 'permissionDenied' });
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
-
const settings = this.normalizeEosAssistSettings(payload);
|
|
484
|
-
if (settings.provider === 'custom' && settings.baseUrl && !/^https?:\/\//i.test(settings.baseUrl)) {
|
|
485
|
-
callback?.({ error: 'Die Basis-URL muss mit http:// oder https:// beginnen.', code: 'invalidSettings' });
|
|
486
|
-
return;
|
|
487
|
-
}
|
|
488
|
-
try {
|
|
489
|
-
const apiKey = settings.credentialId ? await (0, credentials_1.resolveAiKey)(this, settings.credentialId) : '';
|
|
490
|
-
if (settings.provider !== 'custom' && !apiKey) {
|
|
491
|
-
callback?.({ error: 'Der ausgewählte Zugang enthält keinen verwendbaren API-Schlüssel.', code: 'credentialMissing' });
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
const models = await (0, llmProvider_1.listModels)({
|
|
495
|
-
provider: settings.provider,
|
|
496
|
-
apiKey,
|
|
497
|
-
baseUrl: settings.baseUrl || undefined,
|
|
498
|
-
allowSelfSignedCerts: settings.allowSelfSignedCerts,
|
|
499
|
-
});
|
|
500
|
-
callback?.({ success: true, models, count: models.length });
|
|
501
|
-
}
|
|
502
|
-
catch (error) {
|
|
503
|
-
callback?.({ error: error instanceof Error ? error.message : String(error), code: 'connectionFailed' });
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
async processEosAssistSocketRequest(socketClient, payload, callback) {
|
|
507
|
-
const userId = socketClient?._acl?.user;
|
|
508
|
-
if (!userId) {
|
|
509
|
-
callback?.({ error: 'Nicht angemeldet.', code: 'notAuthenticated' });
|
|
510
|
-
return;
|
|
511
|
-
}
|
|
512
|
-
const role = this.getEosCachedRole(userId);
|
|
513
|
-
if (this.eosPasswordSetupRequired.has(userId)) {
|
|
514
|
-
callback?.({ error: 'Die persönliche Passwortvergabe muss zuerst abgeschlossen werden.', code: 'passwordSetupRequired' });
|
|
515
|
-
return;
|
|
516
|
-
}
|
|
517
|
-
const now = Date.now();
|
|
518
|
-
const previous = this.eosAssistLastRequestAt.get(userId) || 0;
|
|
519
|
-
if (now - previous < 1_500) {
|
|
520
|
-
callback?.({ error: 'Bitte einen Moment warten, bevor die nächste Frage gesendet wird.', code: 'rateLimited' });
|
|
521
|
-
return;
|
|
522
|
-
}
|
|
523
|
-
if (this.eosAssistInFlight.has(userId)) {
|
|
524
|
-
callback?.({ error: 'EOS Assist bearbeitet bereits eine Frage.', code: 'busy' });
|
|
525
|
-
return;
|
|
526
|
-
}
|
|
527
|
-
const record = payload && typeof payload === 'object' && !Array.isArray(payload)
|
|
528
|
-
? payload
|
|
529
|
-
: {};
|
|
530
|
-
const messages = this.normalizeEosAssistMessages(record.messages);
|
|
531
|
-
if (!messages.some(message => message.role === 'user')) {
|
|
532
|
-
callback?.({ error: 'Bitte eine Frage eingeben.', code: 'emptyQuestion' });
|
|
533
|
-
return;
|
|
534
|
-
}
|
|
535
|
-
this.eosAssistLastRequestAt.set(userId, now);
|
|
536
|
-
this.eosAssistInFlight.add(userId);
|
|
537
|
-
try {
|
|
538
|
-
const settings = await this.readEosAssistSettings();
|
|
539
|
-
if (!this.isEosAssistConfigured(settings)) {
|
|
540
|
-
callback?.({
|
|
541
|
-
error: 'EOS Assist ist noch nicht durch Admin / NexoWatt Service konfiguriert.',
|
|
542
|
-
code: 'notConfigured',
|
|
543
|
-
canConfigure: role === 'admin',
|
|
544
|
-
role,
|
|
545
|
-
});
|
|
546
|
-
return;
|
|
547
|
-
}
|
|
548
|
-
const apiKey = settings.credentialId ? await (0, credentials_1.resolveAiKey)(this, settings.credentialId) : '';
|
|
549
|
-
if (settings.provider !== 'custom' && !apiKey) {
|
|
550
|
-
callback?.({
|
|
551
|
-
error: 'Der konfigurierte KI-Zugang enthält keinen verwendbaren API-Schlüssel.',
|
|
552
|
-
code: 'credentialMissing',
|
|
553
|
-
canConfigure: role === 'admin',
|
|
554
|
-
});
|
|
555
|
-
return;
|
|
556
|
-
}
|
|
557
|
-
const orchestrator = new chatOrchestrator_1.ChatOrchestrator(this.getEosAssistMcp(userId), this);
|
|
558
|
-
const uiContextRecord = record.uiContext && typeof record.uiContext === 'object' && !Array.isArray(record.uiContext)
|
|
559
|
-
? record.uiContext
|
|
560
|
-
: {};
|
|
561
|
-
const result = await orchestrator.run({
|
|
562
|
-
provider: settings.provider,
|
|
563
|
-
model: settings.model,
|
|
564
|
-
apiKey,
|
|
565
|
-
baseUrl: settings.baseUrl || undefined,
|
|
566
|
-
messages,
|
|
567
|
-
language: systemLanguage,
|
|
568
|
-
allowSelfSignedCerts: settings.allowSelfSignedCerts,
|
|
569
|
-
maxToolRounds: 10,
|
|
570
|
-
mode: 'read',
|
|
571
|
-
uiContext: {
|
|
572
|
-
hash: typeof uiContextRecord.hash === 'string' ? uiContextRecord.hash.substring(0, 500) : '',
|
|
573
|
-
},
|
|
574
|
-
accessRole: role,
|
|
575
|
-
});
|
|
576
|
-
const content = this.sanitizeEosAssistVisibleText(result.content);
|
|
577
|
-
const newMessages = result.newMessages
|
|
578
|
-
.filter(message => message.role === 'assistant' && typeof message.content === 'string')
|
|
579
|
-
.map(message => ({ ...message, content: this.sanitizeEosAssistVisibleText(message.content) }));
|
|
580
|
-
callback?.({
|
|
581
|
-
success: true,
|
|
582
|
-
status: 'done',
|
|
583
|
-
content,
|
|
584
|
-
newMessages,
|
|
585
|
-
steps: result.steps.map(step => ({
|
|
586
|
-
...step,
|
|
587
|
-
result: this.sanitizeEosAssistVisibleText(step.result),
|
|
588
|
-
})),
|
|
589
|
-
clientActions: result.clientActions || [],
|
|
590
|
-
role,
|
|
591
|
-
});
|
|
592
|
-
}
|
|
593
|
-
catch (error) {
|
|
594
|
-
callback?.({
|
|
595
|
-
error: error instanceof Error ? error.message : String(error),
|
|
596
|
-
code: 'assistFailed',
|
|
597
|
-
canConfigure: role === 'admin',
|
|
598
|
-
});
|
|
599
|
-
}
|
|
600
|
-
finally {
|
|
601
|
-
this.eosAssistInFlight.delete(userId);
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
installEosAssistSocketCommands(socketAdmin) {
|
|
605
|
-
socketAdmin.addCommandHandler('eosAssistStatus', (socketClient, _payload, callback) => {
|
|
606
|
-
void this.getEosAssistStatus(socketClient, callback);
|
|
607
|
-
});
|
|
608
|
-
socketAdmin.addCommandHandler('eosAssistSaveSettings', (socketClient, payload, callback) => {
|
|
609
|
-
void this.saveEosAssistSettings(socketClient, payload, callback);
|
|
610
|
-
});
|
|
611
|
-
socketAdmin.addCommandHandler('eosAssistTestSettings', (socketClient, payload, callback) => {
|
|
612
|
-
void this.testEosAssistSettings(socketClient, payload, callback);
|
|
613
|
-
});
|
|
614
|
-
socketAdmin.addCommandHandler('eosAssist', (socketClient, payload, callback) => {
|
|
615
|
-
void this.processEosAssistSocketRequest(socketClient, payload, callback);
|
|
616
|
-
});
|
|
617
|
-
}
|
|
618
393
|
/**
|
|
619
394
|
* Some message was sent to this instance over the message box. Used by email, pushover, text2speech, ...
|
|
620
395
|
* Using this method requires the "common.messagebox" property to be set to true in io-package.json
|
|
@@ -708,8 +483,55 @@ class Admin extends adapter_core_1.Adapter {
|
|
|
708
483
|
});
|
|
709
484
|
return;
|
|
710
485
|
}
|
|
486
|
+
|
|
487
|
+
else if (obj.command === 'eos:completeFirstLogin') {
|
|
488
|
+
const message = obj.message || {};
|
|
489
|
+
const user = String(message.user || '').replace(/^system\\.user\\./, '').trim().toLowerCase();
|
|
490
|
+
const password = String(message.password || '');
|
|
491
|
+
if (!['installer', 'guest', 'user'].includes(user) || !password) {
|
|
492
|
+
if (obj.callback) {
|
|
493
|
+
this.sendTo(obj.from, obj.command, { error: 'Ungültige Anfrage' }, obj.callback);
|
|
494
|
+
}
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
void this.checkPasswordAsync(user, password)
|
|
498
|
+
.then(async (result) => {
|
|
499
|
+
const valid = Array.isArray(result) ? result[0] === true : !!result;
|
|
500
|
+
if (!valid) {
|
|
501
|
+
throw new Error('Das neue Passwort konnte nicht bestätigt werden');
|
|
502
|
+
}
|
|
503
|
+
const id = `system.user.${user}`;
|
|
504
|
+
const account = await this.getForeignObjectAsync(id);
|
|
505
|
+
if (!account) {
|
|
506
|
+
throw new Error('Benutzerkonto nicht gefunden');
|
|
507
|
+
}
|
|
508
|
+
const eosNative = account.native || {};
|
|
509
|
+
await this.extendForeignObjectAsync(id, {
|
|
510
|
+
native: {
|
|
511
|
+
...eosNative,
|
|
512
|
+
nexowattPasswordChangeRequired: false,
|
|
513
|
+
eosPasswordChangeRequired: false,
|
|
514
|
+
nexowattFirstLoginPending: false,
|
|
515
|
+
eosFirstLoginRequired: false,
|
|
516
|
+
nexowattPasswordChangedAt: new Date().toISOString(),
|
|
517
|
+
},
|
|
518
|
+
}, { user: 'system.user.admin' });
|
|
519
|
+
if (obj.callback) {
|
|
520
|
+
this.sendTo(obj.from, obj.command, { success: true }, obj.callback);
|
|
521
|
+
}
|
|
522
|
+
})
|
|
523
|
+
.catch((error) => {
|
|
524
|
+
if (obj.callback) {
|
|
525
|
+
this.sendTo(obj.from, obj.command, { error: error.message }, obj.callback);
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
|
|
711
531
|
else if (obj.command.startsWith('chat:')) {
|
|
712
|
-
|
|
532
|
+
if (obj.callback) {
|
|
533
|
+
this.sendTo(obj.from, obj.command, { error: 'EOS Assist ist in dieser Stable-Version vorübergehend deaktiviert.' }, obj.callback);
|
|
534
|
+
}
|
|
713
535
|
return;
|
|
714
536
|
}
|
|
715
537
|
else if (webServer?.processMessage(obj)) {
|
|
@@ -1059,12 +881,6 @@ class Admin extends adapter_core_1.Adapter {
|
|
|
1059
881
|
void this.mcpChat.close();
|
|
1060
882
|
this.mcpChat = null;
|
|
1061
883
|
}
|
|
1062
|
-
for (const manager of this.eosAssistMcpChats.values()) {
|
|
1063
|
-
void manager.close();
|
|
1064
|
-
}
|
|
1065
|
-
this.eosAssistMcpChats.clear();
|
|
1066
|
-
this.eosAssistLastRequestAt.clear();
|
|
1067
|
-
this.eosAssistInFlight.clear();
|
|
1068
884
|
try {
|
|
1069
885
|
this.log.info(`terminating http${this.config.secure ? 's' : ''} server on port ${this.config.port}`);
|
|
1070
886
|
socket?.close();
|
|
@@ -2357,7 +2173,6 @@ class Admin extends adapter_core_1.Adapter {
|
|
|
2357
2173
|
}
|
|
2358
2174
|
socket = new socket_classes_1.SocketAdmin(settings, this, objects);
|
|
2359
2175
|
this.installEosSocketCommandGuard(socket);
|
|
2360
|
-
this.installEosAssistSocketCommands(socket);
|
|
2361
2176
|
socket.start(server, ws_server_1.SocketIO, { store, oauth2Only: true, noBasicAuth: this.config.noBasicAuth });
|
|
2362
2177
|
// subscribe to all object changes
|
|
2363
2178
|
socket.subscribe('objectChange', '*');
|
package/io-package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "eos-admin",
|
|
4
|
-
"version": "7.9.
|
|
4
|
+
"version": "7.9.95",
|
|
5
5
|
"titleLang": {
|
|
6
6
|
"en": "NexoWatt EOS Admin",
|
|
7
7
|
"de": "NexoWatt EOS Admin",
|
|
@@ -18,15 +18,15 @@
|
|
|
18
18
|
"connectionType": "local",
|
|
19
19
|
"dataSource": "push",
|
|
20
20
|
"news": {
|
|
21
|
-
"7.9.
|
|
22
|
-
"de": "
|
|
23
|
-
"en": "
|
|
24
|
-
"nl": "
|
|
21
|
+
"7.9.95": {
|
|
22
|
+
"de": "Stable 7.9.95 macht den Release-Ordner mergesicher: Die flache Update-ZIP kann direkt über einen bestehenden Arbeitsordner entpackt werden. Vor Paketprüfung und npm-Veröffentlichung werden package.json, io-package.json, beide Lockdateien, die Frontend-Version, Build-Informationen und der aktuelle Repository-Eintrag automatisch auf dieselbe Version gebracht. Dadurch wird insbesondere ein stehen gebliebenes io-package.json aus 7.9.92/7.9.94 automatisch repariert. Die Funktionen aus 7.9.94 bleiben unverändert.",
|
|
23
|
+
"en": "Stable 7.9.95 makes the release folder merge-safe: the flat update ZIP can be extracted directly over an existing working directory. Before package validation and npm publishing, package.json, io-package.json, both lockfiles, the frontend version, build information and the current repository entry are synchronized automatically. In particular, a stale io-package.json from 7.9.92/7.9.94 is repaired automatically. Functional behavior from 7.9.94 remains unchanged.",
|
|
24
|
+
"nl": "Stable 7.9.95 maakt de releasemap merge-veilig: de vlakke update-ZIP kan direct over een bestaande werkmap worden uitgepakt. Vóór pakketcontrole en npm-publicatie worden package.json, io-package.json, beide lockbestanden, de frontendversie, buildinformatie en de actuele repositoryvermelding automatisch op dezelfde versie gezet. Een achtergebleven io-package.json uit 7.9.92/7.9.94 wordt daardoor automatisch hersteld. De functies uit 7.9.94 blijven ongewijzigd."
|
|
25
25
|
},
|
|
26
|
-
"7.9.
|
|
27
|
-
"de": "Stable 7.9.
|
|
28
|
-
"en": "Stable 7.9.
|
|
29
|
-
"nl": "Stable 7.9.
|
|
26
|
+
"7.9.94": {
|
|
27
|
+
"de": "Stable-Kandidat 7.9.94: Standardkonten installer, guest und user verwenden bei der Ersteinrichtung das Startpasswort nexowatt und müssen nach der ersten erfolgreichen Anmeldung sofort ein persönliches Passwort vergeben. Die passwortlose Aktivierung ist deaktiviert. Die Login-Karte bleibt auch bei Fehlermeldungen vollständig bedienbar. EOS Assist ist für diese Stable-Version vollständig deaktiviert, bis die neue lesende Systemassistenz separat freigegeben ist. Deutsche Sicherheitstexte und sichtbare Herstellerangaben wurden bereinigt.",
|
|
28
|
+
"en": "Stable candidate 7.9.94: the standard installer, guest and user accounts use the initial password nexowatt during commissioning and must set a personal password immediately after the first successful sign-in. Passwordless activation is disabled. The login card remains fully usable when an error is shown. EOS Assist is completely disabled in this stable version until the redesigned read-only system assistant is released separately. German security texts and visible publisher information were cleaned up.",
|
|
29
|
+
"nl": "Stable-kandidaat 7.9.94: de standaardaccounts installer, guest en user gebruiken bij de eerste ingebruikname het startwachtwoord nexowatt en moeten na de eerste succesvolle aanmelding direct een persoonlijk wachtwoord instellen. Activering zonder wachtwoord is uitgeschakeld. De aanmeldkaart blijft ook bij foutmeldingen volledig bedienbaar. EOS Assist is in deze stable-versie volledig uitgeschakeld totdat de vernieuwde alleen-lezen systeemassistent apart is vrijgegeven."
|
|
30
30
|
},
|
|
31
31
|
"7.9.90": {
|
|
32
32
|
"de": "Stable 7.9.90 korrigiert die produktive Rollenoberfläche: Die Übersichtskacheln sind für Installateur und Gast/Endkunde zuverlässig bedienbar. Interner Admin und interne BackItUp-Notfallreserve werden in Dienste auch bei verzögert gerenderten Tabellen ausschließlich für Admin/Service angezeigt. Die Erstanmeldung prüft serverseitig, ob Installer, Guest oder User noch keinen persönlichen Zugang besitzen: Nur dann wird die normale Anmeldeschaltfläche bei leerem Passwort aktiv und öffnet die verpflichtende Passwortvergabe; nach abgeschlossener Aktivierung bleibt sie ohne Passwort gesperrt. NexoWatt Sicherung bleibt für freigegebene Rollen erreichbar, EOS Assist im Header und Standardport 8081 bleiben unverändert.",
|
|
@@ -319,7 +319,7 @@
|
|
|
319
319
|
"icon": "admin.svg",
|
|
320
320
|
"messagebox": true,
|
|
321
321
|
"enabled": true,
|
|
322
|
-
"extIcon": "https://unpkg.com/iobroker.eos-admin@7.9.
|
|
322
|
+
"extIcon": "https://unpkg.com/iobroker.eos-admin@7.9.95/admin/admin.svg",
|
|
323
323
|
"keywords": [
|
|
324
324
|
"NexoWatt",
|
|
325
325
|
"EOS",
|
|
@@ -330,7 +330,7 @@
|
|
|
330
330
|
"licensed"
|
|
331
331
|
],
|
|
332
332
|
"compact": true,
|
|
333
|
-
"readme": "https://unpkg.com/iobroker.eos-admin@7.9.
|
|
333
|
+
"readme": "https://unpkg.com/iobroker.eos-admin@7.9.95/README.md",
|
|
334
334
|
"authors": [
|
|
335
335
|
"NexoWatt"
|
|
336
336
|
],
|
|
@@ -398,7 +398,7 @@
|
|
|
398
398
|
"nondeletable": false,
|
|
399
399
|
"allowAdapterUpdate": true,
|
|
400
400
|
"allowAdapterDelete": false,
|
|
401
|
-
"meta": "https://unpkg.com/iobroker.eos-admin@7.9.
|
|
401
|
+
"meta": "https://unpkg.com/iobroker.eos-admin@7.9.95/io-package.json",
|
|
402
402
|
"npmPackage": "iobroker.eos-admin",
|
|
403
403
|
"packetName": "iobroker.eos-admin",
|
|
404
404
|
"publisher": "NexoWatt"
|
|
@@ -506,7 +506,7 @@
|
|
|
506
506
|
],
|
|
507
507
|
"eosAdminOnlyGroup": "system.group.administrator",
|
|
508
508
|
"eosApplyAdminOnlyAclToProtectedAdapters": false,
|
|
509
|
-
"eosAssistantEnabled":
|
|
509
|
+
"eosAssistantEnabled": false,
|
|
510
510
|
"eosAssistantAdminOnly": false,
|
|
511
511
|
"eosStrictProtectedAdapterAcl": false,
|
|
512
512
|
"eosInstallerGroups": [
|
|
@@ -531,11 +531,15 @@
|
|
|
531
531
|
"eosRequireFirstLoginPassword": true,
|
|
532
532
|
"eosFirstLoginPasswordMinLength": 10,
|
|
533
533
|
"eosAutoAssignDefaultRoleUsers": true,
|
|
534
|
-
"eosPasswordlessFirstLogin":
|
|
534
|
+
"eosPasswordlessFirstLogin": false,
|
|
535
535
|
"eosPasswordlessFirstLoginPrivateNetworkOnly": true,
|
|
536
536
|
"eosPasswordClaimTtlMinutes": 10,
|
|
537
537
|
"eosHideLegacyBackupFromNonAdmins": true,
|
|
538
|
-
"nexowattHideLegacyBackupFromNonAdmins": true
|
|
538
|
+
"nexowattHideLegacyBackupFromNonAdmins": true,
|
|
539
|
+
"eosAllowPasswordlessFirstLogin": false,
|
|
540
|
+
"eosRequireFirstLoginPasswordChange": true,
|
|
541
|
+
"eosAutoAssignStandardAccounts": true,
|
|
542
|
+
"eosAssistEnabled": false
|
|
539
543
|
},
|
|
540
544
|
"notifications": [
|
|
541
545
|
{
|
|
@@ -1103,5 +1107,5 @@
|
|
|
1103
1107
|
}
|
|
1104
1108
|
}
|
|
1105
1109
|
},
|
|
1106
|
-
"version": "7.9.
|
|
1110
|
+
"version": "7.9.95"
|
|
1107
1111
|
}
|