neoagent 2.3.1-beta.44 → 2.3.1-beta.46
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/flutter_app/lib/main_chat.dart +7 -44
- package/flutter_app/lib/main_models.dart +0 -7
- package/package.json +1 -1
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +28437 -28534
- package/server/routes/settings.js +24 -15
- package/server/services/ai/engine.js +1 -0
- package/server/services/messaging/meshtastic.js +9 -0
- package/server/services/messaging/meshtastic_env.js +0 -60
- package/server/services/messaging/meshtastic_protocol.js +41 -10
|
@@ -23,8 +23,6 @@ const {
|
|
|
23
23
|
} = require('../services/ai/settings');
|
|
24
24
|
const {
|
|
25
25
|
readMeshtasticEnabled,
|
|
26
|
-
setMeshtasticEnabled,
|
|
27
|
-
resetMeshtasticEnabled,
|
|
28
26
|
} = require('../services/messaging/meshtastic_env');
|
|
29
27
|
const {
|
|
30
28
|
ensureDefaultRuntimeSettings,
|
|
@@ -84,6 +82,10 @@ const ENV_BACKED_SETTING_KEYS = new Set([
|
|
|
84
82
|
'meshtastic_enabled',
|
|
85
83
|
]);
|
|
86
84
|
|
|
85
|
+
const READ_ONLY_ENV_SETTING_KEYS = new Set([
|
|
86
|
+
'meshtastic_enabled',
|
|
87
|
+
]);
|
|
88
|
+
|
|
87
89
|
function toOptionalTrimmedString(value) {
|
|
88
90
|
if (typeof value !== 'string') return undefined;
|
|
89
91
|
const trimmed = value.trim();
|
|
@@ -175,14 +177,8 @@ function readEnvBackedSettingValue(key) {
|
|
|
175
177
|
|
|
176
178
|
async function writeEnvBackedSettingValue(req, key, value) {
|
|
177
179
|
switch (key) {
|
|
178
|
-
case 'meshtastic_enabled':
|
|
179
|
-
|
|
180
|
-
const manager = req.app?.locals?.messagingManager;
|
|
181
|
-
if (manager && typeof manager.updateMeshtasticEnabled === 'function') {
|
|
182
|
-
await manager.updateMeshtasticEnabled(enabled);
|
|
183
|
-
}
|
|
184
|
-
return enabled;
|
|
185
|
-
}
|
|
180
|
+
case 'meshtastic_enabled':
|
|
181
|
+
return readMeshtasticEnabled();
|
|
186
182
|
default:
|
|
187
183
|
return value;
|
|
188
184
|
}
|
|
@@ -191,11 +187,6 @@ async function writeEnvBackedSettingValue(req, key, value) {
|
|
|
191
187
|
async function resetEnvBackedSettingValue(req, key) {
|
|
192
188
|
switch (key) {
|
|
193
189
|
case 'meshtastic_enabled': {
|
|
194
|
-
resetMeshtasticEnabled();
|
|
195
|
-
const manager = req.app?.locals?.messagingManager;
|
|
196
|
-
if (manager && typeof manager.updateMeshtasticEnabled === 'function') {
|
|
197
|
-
await manager.updateMeshtasticEnabled(readMeshtasticEnabled());
|
|
198
|
-
}
|
|
199
190
|
return;
|
|
200
191
|
}
|
|
201
192
|
default:
|
|
@@ -292,6 +283,10 @@ router.put('/', async (req, res) => {
|
|
|
292
283
|
}
|
|
293
284
|
|
|
294
285
|
for (const key of Object.keys(normalizedBody)) {
|
|
286
|
+
if (READ_ONLY_ENV_SETTING_KEYS.has(key)) {
|
|
287
|
+
delete normalizedBody[key];
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
295
290
|
if (isEnvBackedSettingKey(key)) {
|
|
296
291
|
normalizedBody[key] = await writeEnvBackedSettingValue(req, key, normalizedBody[key]);
|
|
297
292
|
}
|
|
@@ -470,6 +465,13 @@ router.put('/:key', async (req, res) => {
|
|
|
470
465
|
const agentId = resolveAgentId(userId, getAgentIdFromRequest(req));
|
|
471
466
|
ensureDefaultRuntimeSettings(userId);
|
|
472
467
|
let value = req.body.value;
|
|
468
|
+
if (READ_ONLY_ENV_SETTING_KEYS.has(req.params.key)) {
|
|
469
|
+
return res.status(403).json({
|
|
470
|
+
success: false,
|
|
471
|
+
error: `${req.params.key} is managed via environment only`,
|
|
472
|
+
value: readEnvBackedSettingValue(req.params.key),
|
|
473
|
+
});
|
|
474
|
+
}
|
|
473
475
|
if (isEnvBackedSettingKey(req.params.key)) {
|
|
474
476
|
const saved = await writeEnvBackedSettingValue(req, req.params.key, value);
|
|
475
477
|
return res.json({ success: true, value: saved });
|
|
@@ -535,6 +537,13 @@ router.put('/:key', async (req, res) => {
|
|
|
535
537
|
|
|
536
538
|
// Delete setting
|
|
537
539
|
router.delete('/:key', (req, res) => {
|
|
540
|
+
if (READ_ONLY_ENV_SETTING_KEYS.has(req.params.key)) {
|
|
541
|
+
return res.status(403).json({
|
|
542
|
+
success: false,
|
|
543
|
+
error: `${req.params.key} is managed via environment only`,
|
|
544
|
+
value: readEnvBackedSettingValue(req.params.key),
|
|
545
|
+
});
|
|
546
|
+
}
|
|
538
547
|
if (isEnvBackedSettingKey(req.params.key)) {
|
|
539
548
|
return resetEnvBackedSettingValue(req, req.params.key)
|
|
540
549
|
.then(() => res.json({ success: true }))
|
|
@@ -1551,6 +1551,7 @@ class AgentEngine {
|
|
|
1551
1551
|
agentId,
|
|
1552
1552
|
triggerType,
|
|
1553
1553
|
triggerSource,
|
|
1554
|
+
widgetId: options.widgetId || null,
|
|
1554
1555
|
});
|
|
1555
1556
|
const mcpManager = app?.locals?.mcpManager || app?.locals?.mcpClient || this.mcpManager;
|
|
1556
1557
|
const integrationManager = app?.locals?.integrationManager || null;
|
|
@@ -103,6 +103,12 @@ class MeshtasticPlatform extends BasePlatform {
|
|
|
103
103
|
this._disconnecting = false;
|
|
104
104
|
this.status = 'connecting';
|
|
105
105
|
|
|
106
|
+
const staleTransport = this._transport;
|
|
107
|
+
this._transport = null;
|
|
108
|
+
if (staleTransport) {
|
|
109
|
+
await staleTransport.disconnect().catch(() => {});
|
|
110
|
+
}
|
|
111
|
+
|
|
106
112
|
const transport = await MeshtasticTcpTransport.create(this.host, this.port, 60000);
|
|
107
113
|
this._transport = transport;
|
|
108
114
|
|
|
@@ -174,6 +180,9 @@ class MeshtasticPlatform extends BasePlatform {
|
|
|
174
180
|
|
|
175
181
|
conn.on('disconnected', (info) => {
|
|
176
182
|
if (this._disconnecting) return;
|
|
183
|
+
if (this._transport === transport) {
|
|
184
|
+
this._transport = null;
|
|
185
|
+
}
|
|
177
186
|
this.status = 'disconnected';
|
|
178
187
|
this.emit('disconnected', { reason: info?.reason || 'device_disconnected' });
|
|
179
188
|
this._scheduleReconnect();
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
4
|
const { ENV_FILE } = require('../../../runtime/paths');
|
|
6
5
|
const { parseEnv } = require('../../../runtime/env');
|
|
7
6
|
|
|
@@ -20,41 +19,6 @@ function readEnvFileRaw(envFile = ENV_FILE) {
|
|
|
20
19
|
return fs.readFileSync(envFile, 'utf8');
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
function writeEnvFileValue(key, value, envFile = ENV_FILE) {
|
|
24
|
-
const raw = readEnvFileRaw(envFile);
|
|
25
|
-
const lines = raw ? raw.split('\n') : [];
|
|
26
|
-
let replaced = false;
|
|
27
|
-
|
|
28
|
-
for (let i = 0; i < lines.length; i += 1) {
|
|
29
|
-
if (lines[i].startsWith(`${key}=`)) {
|
|
30
|
-
lines[i] = `${key}=${value}`;
|
|
31
|
-
replaced = true;
|
|
32
|
-
break;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (!replaced) {
|
|
37
|
-
lines.push(`${key}=${value}`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const output = `${lines
|
|
41
|
-
.filter((_, idx, items) => idx !== items.length - 1 || items[idx] !== '')
|
|
42
|
-
.join('\n')}\n`;
|
|
43
|
-
fs.mkdirSync(path.dirname(envFile), { recursive: true });
|
|
44
|
-
fs.writeFileSync(envFile, output, { mode: 0o600 });
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function removeEnvFileValue(key, envFile = ENV_FILE) {
|
|
48
|
-
const raw = readEnvFileRaw(envFile);
|
|
49
|
-
if (!raw) return false;
|
|
50
|
-
const lines = raw.split('\n').filter((line) => !line.startsWith(`${key}=`));
|
|
51
|
-
const output = `${lines
|
|
52
|
-
.filter((_, idx, items) => idx !== items.length - 1 || items[idx] !== '')
|
|
53
|
-
.join('\n')}\n`;
|
|
54
|
-
fs.mkdirSync(path.dirname(envFile), { recursive: true });
|
|
55
|
-
fs.writeFileSync(envFile, output, { mode: 0o600 });
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
22
|
|
|
59
23
|
function readMeshtasticEnabled({
|
|
60
24
|
env = process.env,
|
|
@@ -69,32 +33,8 @@ function readMeshtasticEnabled({
|
|
|
69
33
|
return normalizeMeshtasticEnabled(parsed.get(MESHTASTIC_ENABLED_ENV_KEY), fallback);
|
|
70
34
|
}
|
|
71
35
|
|
|
72
|
-
function setMeshtasticEnabled(enabled, {
|
|
73
|
-
env = process.env,
|
|
74
|
-
envFile = ENV_FILE,
|
|
75
|
-
} = {}) {
|
|
76
|
-
const normalized = normalizeMeshtasticEnabled(enabled, true);
|
|
77
|
-
writeEnvFileValue(MESHTASTIC_ENABLED_ENV_KEY, normalized ? 'true' : 'false', envFile);
|
|
78
|
-
if (env && typeof env === 'object') {
|
|
79
|
-
env[MESHTASTIC_ENABLED_ENV_KEY] = normalized ? 'true' : 'false';
|
|
80
|
-
}
|
|
81
|
-
return normalized;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function resetMeshtasticEnabled({
|
|
85
|
-
env = process.env,
|
|
86
|
-
envFile = ENV_FILE,
|
|
87
|
-
} = {}) {
|
|
88
|
-
removeEnvFileValue(MESHTASTIC_ENABLED_ENV_KEY, envFile);
|
|
89
|
-
if (env && typeof env === 'object') {
|
|
90
|
-
delete env[MESHTASTIC_ENABLED_ENV_KEY];
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
36
|
module.exports = {
|
|
95
37
|
MESHTASTIC_ENABLED_ENV_KEY,
|
|
96
38
|
normalizeMeshtasticEnabled,
|
|
97
39
|
readMeshtasticEnabled,
|
|
98
|
-
setMeshtasticEnabled,
|
|
99
|
-
resetMeshtasticEnabled,
|
|
100
40
|
};
|
|
@@ -314,36 +314,63 @@ class MeshtasticConnection extends EventEmitter {
|
|
|
314
314
|
return new Promise((resolve, reject) => {
|
|
315
315
|
const socket = new Socket();
|
|
316
316
|
let settled = false;
|
|
317
|
+
let timer = null;
|
|
318
|
+
|
|
319
|
+
const cleanupHandshake = () => {
|
|
320
|
+
if (timer) {
|
|
321
|
+
clearTimeout(timer);
|
|
322
|
+
timer = null;
|
|
323
|
+
}
|
|
324
|
+
socket.removeListener('error', fail);
|
|
325
|
+
socket.removeListener('close', onPreReadyClose);
|
|
326
|
+
this.removeListener('configured', onConfigured);
|
|
327
|
+
this.removeListener('disconnected', onDisconnected);
|
|
328
|
+
};
|
|
317
329
|
|
|
318
330
|
const fail = (err) => {
|
|
319
331
|
if (settled) return;
|
|
320
332
|
settled = true;
|
|
333
|
+
cleanupHandshake();
|
|
334
|
+
if (this._socket === socket) {
|
|
335
|
+
this._socket = null;
|
|
336
|
+
}
|
|
321
337
|
socket.destroy();
|
|
322
338
|
reject(err);
|
|
323
339
|
};
|
|
324
340
|
|
|
325
|
-
const
|
|
341
|
+
const onConfigured = () => {
|
|
342
|
+
if (settled) return;
|
|
343
|
+
settled = true;
|
|
344
|
+
cleanupHandshake();
|
|
345
|
+
resolve();
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
const onDisconnected = (info) => {
|
|
349
|
+
fail(new Error(`Disconnected before configuration: ${info?.reason || 'unknown'}`));
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
const onPreReadyClose = () => {
|
|
353
|
+
fail(new Error('Disconnected before configuration: socket-closed'));
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
timer = setTimeout(() => fail(new Error('Connection timeout')), timeout);
|
|
326
357
|
|
|
327
358
|
socket.once('error', fail);
|
|
359
|
+
socket.once('close', onPreReadyClose);
|
|
328
360
|
socket.once('ready', () => {
|
|
329
361
|
socket.removeListener('error', fail);
|
|
362
|
+
socket.removeListener('close', onPreReadyClose);
|
|
330
363
|
this._socket = socket;
|
|
331
364
|
this._wireSocket(socket);
|
|
332
365
|
this.emit('status', 'connected');
|
|
333
366
|
|
|
334
|
-
const onConfigured = () => {
|
|
335
|
-
if (settled) return;
|
|
336
|
-
settled = true;
|
|
337
|
-
clearTimeout(timer);
|
|
338
|
-
resolve();
|
|
339
|
-
};
|
|
340
367
|
this.once('configured', onConfigured);
|
|
368
|
+
this.once('disconnected', onDisconnected);
|
|
341
369
|
|
|
342
370
|
const toRadio = encodeToRadioWantConfig(this._configId);
|
|
343
371
|
socket.write(framePacket(toRadio));
|
|
344
372
|
});
|
|
345
373
|
|
|
346
|
-
socket.setTimeout(timeout);
|
|
347
374
|
socket.connect(port, host);
|
|
348
375
|
});
|
|
349
376
|
}
|
|
@@ -361,18 +388,22 @@ class MeshtasticConnection extends EventEmitter {
|
|
|
361
388
|
});
|
|
362
389
|
|
|
363
390
|
socket.on('data', parser);
|
|
364
|
-
socket.on('error', (err) => this._onDisconnected(`socket-error: ${err.message}`))
|
|
391
|
+
socket.on('error', (err) => this._onDisconnected(`socket-error: ${err.message}`));
|
|
365
392
|
socket.on('end', () => this._onDisconnected('socket-end'));
|
|
366
393
|
socket.on('close', () => this._onDisconnected('socket-closed'));
|
|
367
394
|
socket.on('timeout', () => {
|
|
368
395
|
this._onDisconnected('socket-timeout');
|
|
369
|
-
socket.destroy();
|
|
370
396
|
});
|
|
371
397
|
}
|
|
372
398
|
|
|
373
399
|
_onDisconnected(reason) {
|
|
374
400
|
if (this._closing) return;
|
|
401
|
+
const socket = this._socket;
|
|
402
|
+
if (!socket) return;
|
|
403
|
+
this._socket = null;
|
|
375
404
|
this._configured = false;
|
|
405
|
+
socket.removeAllListeners();
|
|
406
|
+
socket.destroy();
|
|
376
407
|
this.emit('status', 'disconnected');
|
|
377
408
|
this.emit('disconnected', { reason });
|
|
378
409
|
}
|