neoagent 2.3.1-beta.52 → 2.3.1-beta.54

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.
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <resources>
3
+ <!-- Hosts allowed to connect to the Android Auto app. Update as needed. -->
4
+ <string-array name="hosts_allowlist">
5
+ <item>androidx.car.app</item>
6
+ <item>com.google.android.projection.gearhead</item>
7
+ </string-array>
8
+ </resources>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neoagent",
3
- "version": "2.3.1-beta.52",
3
+ "version": "2.3.1-beta.54",
4
4
  "description": "Proactive personal AI agent with no limits",
5
5
  "license": "MIT",
6
6
  "main": "server/index.js",
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"42d3d75a56efe1a2e9902f52dc8006099c45d9
37
37
 
38
38
  _flutter.loader.load({
39
39
  serviceWorkerSettings: {
40
- serviceWorkerVersion: "2273256777" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
40
+ serviceWorkerVersion: "3319853538" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
41
41
  }
42
42
  });
@@ -330,7 +330,12 @@ router.get('/api/auth/providers/complete', async (req, res) => {
330
330
  if (!user) {
331
331
  return res.status(404).json({ error: 'User not found' });
332
332
  }
333
- if (getTwoFactorStatus(user.id).enabled) {
333
+ // Skip secondary two-factor when signing in via certain social providers.
334
+ // Currently skip for Google (`provider === 'google`).
335
+ const providerKey = String(completion.provider || '').trim();
336
+ const twoFactor = getTwoFactorStatus(user.id);
337
+ const skipTwoFactorForProviders = new Set(['google']);
338
+ if (twoFactor.enabled && !skipTwoFactorForProviders.has(providerKey)) {
334
339
  return establishPendingTwoFactorSession(req, res, user);
335
340
  }
336
341
  updateLastLogin(user.id);
@@ -76,6 +76,7 @@ class MeshtasticPlatform extends BasePlatform {
76
76
  this._connectPromise = null;
77
77
  this._disconnecting = false;
78
78
  this._reconnectTimer = null;
79
+ this._reconnectDelayMs = 10000;
79
80
  }
80
81
 
81
82
  async connect() {
@@ -109,7 +110,11 @@ class MeshtasticPlatform extends BasePlatform {
109
110
  await staleTransport.disconnect().catch(() => {});
110
111
  }
111
112
 
112
- const transport = await MeshtasticTcpTransport.create(this.host, this.port, 60000);
113
+ const transport = await MeshtasticTcpTransport.create(this.host, this.port, 60000, {
114
+ // This service only needs the local node info plus live packets. Pulling
115
+ // the full node DB on each reconnect adds avoidable load to Wi-Fi radios.
116
+ noNodes: true,
117
+ });
113
118
  this._transport = transport;
114
119
 
115
120
  const conn = transport.connection;
@@ -189,6 +194,7 @@ class MeshtasticPlatform extends BasePlatform {
189
194
  });
190
195
 
191
196
  this.status = 'connected';
197
+ this._reconnectDelayMs = 10000;
192
198
  this.authInfo = this._buildAuthInfo(conn);
193
199
  this.emit('connected');
194
200
  return { status: this.status };
@@ -196,7 +202,8 @@ class MeshtasticPlatform extends BasePlatform {
196
202
 
197
203
  _scheduleReconnect() {
198
204
  if (this._disconnecting || this._reconnectTimer) return;
199
- console.log(`[Meshtastic] Connection lost. Reconnecting in 10 seconds...`);
205
+ const delayMs = this._reconnectDelayMs;
206
+ console.log(`[Meshtastic] Connection lost. Reconnecting in ${Math.round(delayMs / 1000)} seconds...`);
200
207
  this._reconnectTimer = setTimeout(() => {
201
208
  this._reconnectTimer = null;
202
209
  if (this._disconnecting) return;
@@ -204,7 +211,8 @@ class MeshtasticPlatform extends BasePlatform {
204
211
  console.error(`[Meshtastic] Auto-reconnect failed:`, err.message);
205
212
  this._scheduleReconnect();
206
213
  });
207
- }, 10000);
214
+ }, delayMs);
215
+ this._reconnectDelayMs = Math.min(delayMs * 2, 60000);
208
216
  }
209
217
 
210
218
  _buildAuthInfo(conn) {
@@ -8,6 +8,7 @@ const FRAME_START_1 = 0x94;
8
8
  const FRAME_START_2 = 0xC3;
9
9
 
10
10
  const BROADCAST_NUM = 0xFFFFFFFF;
11
+ const NODELESS_WANT_CONFIG_ID = 69420;
11
12
 
12
13
  // PortNum values from public protocol specification
13
14
  const PortNum = Object.freeze({
@@ -174,6 +175,21 @@ function encodeToRadioWantConfig(configId) {
174
175
  return new Uint8Array(encodeVarintField(3, configId));
175
176
  }
176
177
 
178
+ function resolveWantConfigId(options = {}) {
179
+ if (Number.isInteger(options.wantConfigId) && options.wantConfigId >= 0) {
180
+ return options.wantConfigId >>> 0;
181
+ }
182
+ if (options.noNodes) {
183
+ return NODELESS_WANT_CONFIG_ID;
184
+ }
185
+
186
+ let configId = (Math.random() * 0xFFFFFFFF) >>> 0;
187
+ if (configId === NODELESS_WANT_CONFIG_ID) {
188
+ configId = (configId + 1) >>> 0;
189
+ }
190
+ return configId;
191
+ }
192
+
177
193
  function decodeUser(buf) {
178
194
  const fields = decodeFields(buf);
179
195
  return {
@@ -288,10 +304,10 @@ function createFrameParser(onPacket) {
288
304
  // --------------------------------------------------------------------------
289
305
 
290
306
  class MeshtasticConnection extends EventEmitter {
291
- constructor() {
307
+ constructor(options = {}) {
292
308
  super();
293
309
  this._socket = null;
294
- this._configId = (Math.random() * 0x7FFFFFFF) >>> 0;
310
+ this._configId = resolveWantConfigId(options);
295
311
  this._myNodeNum = 0;
296
312
  this._configured = false;
297
313
  this._closing = false;
@@ -316,6 +332,9 @@ class MeshtasticConnection extends EventEmitter {
316
332
  let settled = false;
317
333
  let timer = null;
318
334
 
335
+ socket.setKeepAlive(true, 15000);
336
+ socket.setNoDelay(true);
337
+
319
338
  const cleanupHandshake = () => {
320
339
  if (timer) {
321
340
  clearTimeout(timer);
@@ -487,6 +506,8 @@ class MeshtasticConnection extends EventEmitter {
487
506
  this._socket = null;
488
507
  if (socket) {
489
508
  socket.removeAllListeners();
509
+ socket.end();
510
+ socket.destroySoon?.();
490
511
  socket.destroy();
491
512
  }
492
513
  }
@@ -496,12 +517,14 @@ module.exports = {
496
517
  MeshtasticConnection,
497
518
  PortNum,
498
519
  BROADCAST_NUM,
520
+ NODELESS_WANT_CONFIG_ID,
499
521
  encodeVarint,
500
522
  decodeVarint,
501
523
  decodeFields,
502
524
  decodeFromRadio,
503
525
  decodeMeshPacket,
504
526
  decodeUser,
527
+ encodeToRadioWantConfig,
505
528
  framePacket,
506
529
  createFrameParser,
507
530
  };
@@ -0,0 +1,33 @@
1
+ const test = require('node:test');
2
+ const assert = require('node:assert/strict');
3
+
4
+ const {
5
+ MeshtasticConnection,
6
+ NODELESS_WANT_CONFIG_ID,
7
+ decodeFields,
8
+ encodeToRadioWantConfig,
9
+ } = require('./meshtastic_protocol');
10
+
11
+ test('nodeless startup uses Meshtastic special want_config_id', () => {
12
+ const connection = new MeshtasticConnection({ noNodes: true });
13
+ assert.equal(connection._configId, NODELESS_WANT_CONFIG_ID);
14
+ });
15
+
16
+ test('default startup never collides with nodeless want_config_id', () => {
17
+ const originalRandom = Math.random;
18
+ Math.random = () => NODELESS_WANT_CONFIG_ID / 0xFFFFFFFF;
19
+ try {
20
+ const connection = new MeshtasticConnection();
21
+ assert.notEqual(connection._configId, NODELESS_WANT_CONFIG_ID);
22
+ } finally {
23
+ Math.random = originalRandom;
24
+ }
25
+ });
26
+
27
+ test('want_config packets encode the requested config id in field 3', () => {
28
+ const packet = Buffer.from(encodeToRadioWantConfig(NODELESS_WANT_CONFIG_ID));
29
+ const fields = decodeFields(packet);
30
+ assert.deepEqual(fields, [
31
+ { field: 3, wire: 0, value: NODELESS_WANT_CONFIG_ID },
32
+ ]);
33
+ });
@@ -7,8 +7,8 @@ class MeshtasticTcpTransport {
7
7
  this._connection = connection;
8
8
  }
9
9
 
10
- static async create(hostname, port = 4403, timeout = 60000) {
11
- const connection = new MeshtasticConnection();
10
+ static async create(hostname, port = 4403, timeout = 60000, options = {}) {
11
+ const connection = new MeshtasticConnection(options);
12
12
  await connection.connect(hostname, port, timeout);
13
13
  return new MeshtasticTcpTransport(connection);
14
14
  }