neoagent 2.3.1-beta.25 → 2.3.1-beta.27

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neoagent",
3
- "version": "2.3.1-beta.25",
3
+ "version": "2.3.1-beta.27",
4
4
  "description": "Proactive personal AI agent with no limits",
5
5
  "license": "MIT",
6
6
  "main": "server/index.js",
@@ -69,9 +69,11 @@ ensureSessionStoreSchema(sessionsDb);
69
69
  function buildHelmetOptions({ secureCookies }) {
70
70
  const wsConnectSrc = secureCookies ? ['wss:'] : ['ws:', 'wss:'];
71
71
  const isDevelopment = String(process.env.NODE_ENV || '').trim() === 'development';
72
- const allowUnsafeEval = boolEnv('NEOAGENT_CSP_ALLOW_UNSAFE_EVAL', isDevelopment);
73
- const allowExternalScriptCdn = boolEnv('NEOAGENT_CSP_ALLOW_EXTERNAL_SCRIPT_CDN', isDevelopment);
74
- const allowExternalConnect = boolEnv('NEOAGENT_CSP_ALLOW_EXTERNAL_CONNECT', false);
72
+ // Flutter web (CanvasKit) requires external script/connect sources and wasm eval.
73
+ // Keep strict overrides available via env for hardened deployments.
74
+ const allowUnsafeEval = boolEnv('NEOAGENT_CSP_ALLOW_UNSAFE_EVAL', true);
75
+ const allowExternalScriptCdn = boolEnv('NEOAGENT_CSP_ALLOW_EXTERNAL_SCRIPT_CDN', true);
76
+ const allowExternalConnect = boolEnv('NEOAGENT_CSP_ALLOW_EXTERNAL_CONNECT', true);
75
77
 
76
78
  const scriptSrc = ["'self'", "'unsafe-inline'", 'blob:'];
77
79
  if (allowUnsafeEval) {
@@ -83,7 +85,7 @@ function buildHelmetOptions({ secureCookies }) {
83
85
 
84
86
  const connectSrc = ["'self'", ...wsConnectSrc];
85
87
  if (allowExternalConnect) {
86
- connectSrc.push('https://fonts.googleapis.com', 'https://fonts.gstatic.com', 'https://www.gstatic.com');
88
+ connectSrc.push('https://fonts.googleapis.com', 'https://fonts.gstatic.com', 'https://www.gstatic.com', 'https://cdn.jsdelivr.net');
87
89
  }
88
90
 
89
91
  return {
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"59aa584fdf100e6c78c785d8a5b565d1de4b48
37
37
 
38
38
  _flutter.loader.load({
39
39
  serviceWorkerSettings: {
40
- serviceWorkerVersion: "1747451041" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
40
+ serviceWorkerVersion: "450979186" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
41
41
  }
42
42
  });
@@ -12,7 +12,7 @@ const { getErrorMessage } = require('../bootstrap_helpers');
12
12
  const execAsync = promisify(exec);
13
13
 
14
14
  class ScreenRecorder {
15
- constructor() {
15
+ constructor(options = {}) {
16
16
  this.intervalMs = 10000; // 10 seconds
17
17
  this.intervalId = null;
18
18
  this.cleanupIntervalId = null;
@@ -20,6 +20,9 @@ class ScreenRecorder {
20
20
  this.isProcessing = false;
21
21
  this.tempFilePath = path.join(os.tmpdir(), `neoagent-screen-${Date.now()}.png`);
22
22
  this.lastBenignSkipAt = 0;
23
+ this.hasActiveRemoteCaptureSession = typeof options.hasActiveRemoteCaptureSession === 'function'
24
+ ? options.hasActiveRemoteCaptureSession
25
+ : () => false;
23
26
  }
24
27
 
25
28
  _isCaptureInactiveApp(appName) {
@@ -94,6 +97,13 @@ class ScreenRecorder {
94
97
  this.isProcessing = true;
95
98
 
96
99
  try {
100
+ // Only capture while at least one external device/session is actively connected.
101
+ // This prevents host-level screenshots when no user-side capture source is live.
102
+ if (!this.hasActiveRemoteCaptureSession()) {
103
+ this._logBenignSkip('no active external capture session');
104
+ return;
105
+ }
106
+
97
107
  // Skip capture when the desktop session is inactive (e.g. locked screen).
98
108
  let frontmostApp = '';
99
109
  try {
@@ -337,15 +337,15 @@ function createGithubProvider() {
337
337
  method: 'POST',
338
338
  headers: {
339
339
  'Accept': 'application/json',
340
- 'Content-Type': 'application/json',
340
+ 'Content-Type': 'application/x-www-form-urlencoded',
341
341
  },
342
- body: JSON.stringify({
342
+ body: new URLSearchParams({
343
343
  client_id: config.clientId,
344
344
  client_secret: config.clientSecret,
345
345
  code,
346
346
  code_verifier: codeVerifier,
347
347
  redirect_uri: config.redirectUri,
348
- }),
348
+ }).toString(),
349
349
  });
350
350
 
351
351
  const tokenBody = await tokenResponse.text();
@@ -430,10 +430,35 @@ function createWidgetService(app) {
430
430
  }
431
431
 
432
432
  function createScreenRecorder(app) {
433
+ const hasActiveRemoteCaptureSession = () => {
434
+ const desktopRegistry = app.locals.desktopCompanionRegistry;
435
+ if (desktopRegistry?.connectionsByUser instanceof Map) {
436
+ for (const userMap of desktopRegistry.connectionsByUser.values()) {
437
+ if (!(userMap instanceof Map)) continue;
438
+ for (const connection of userMap.values()) {
439
+ if (typeof connection?.isOpen === 'function' && connection.isOpen()) {
440
+ return true;
441
+ }
442
+ }
443
+ }
444
+ }
445
+
446
+ const extensionRegistry = app.locals.browserExtensionRegistry;
447
+ if (extensionRegistry?.connectionsByUser instanceof Map) {
448
+ for (const connection of extensionRegistry.connectionsByUser.values()) {
449
+ if (typeof connection?.isOpen === 'function' && connection.isOpen()) {
450
+ return true;
451
+ }
452
+ }
453
+ }
454
+
455
+ return false;
456
+ };
457
+
433
458
  const screenRecorder = registerLocal(
434
459
  app,
435
460
  'screenRecorder',
436
- new ScreenRecorder(),
461
+ new ScreenRecorder({ hasActiveRemoteCaptureSession }),
437
462
  );
438
463
  screenRecorder.start();
439
464
  logServiceReady('Screen recorder started');