neoagent 2.3.1-beta.16 → 2.3.1-beta.17

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/.env.example CHANGED
@@ -98,6 +98,18 @@ GOOGLE_AI_KEY=your-google-ai-key-here
98
98
  # • MiniMax-M2.7 via the Anthropic-compatible MiniMax endpoint
99
99
  MINIMAX_API_KEY=your-minimax-api-key-here
100
100
 
101
+ # GitHub Copilot OAuth token — used for:
102
+ # • Copilot-backed chat/coding models (gpt-5.3, gpt-4.1)
103
+ # Set via: neoagent login github-copilot
104
+ GITHUB_COPILOT_ACCESS_TOKEN=
105
+
106
+ # OpenAI Codex OAuth token — used for:
107
+ # • Codex-backed chat/coding models (gpt-5.3-codex, gpt-4.1-codex)
108
+ # Set via: neoagent login openai-codex
109
+ OPENAI_CODEX_ACCESS_TOKEN=
110
+ # Optional refresh token if your login flow returns it.
111
+ OPENAI_CODEX_REFRESH_TOKEN=
112
+
101
113
  ########################################
102
114
  # Provider endpoint overrides
103
115
  ########################################
@@ -105,6 +117,10 @@ MINIMAX_API_KEY=your-minimax-api-key-here
105
117
  # OPENAI_BASE_URL=https://your-openai-compatible-endpoint/v1
106
118
  # ANTHROPIC_BASE_URL=https://your-anthropic-compatible-endpoint
107
119
  # XAI_BASE_URL=https://api.x.ai/v1
120
+ # OPENAI_CODEX_BASE_URL=https://chatgpt.com/backend-api/codex
121
+ # OPENAI_CODEX_EDITOR_VERSION=vscode/1.99.0
122
+ # OPENAI_CODEX_EDITOR_PLUGIN_VERSION=neoagent/1.0.0
123
+ # OPENAI_CODEX_USER_AGENT=NeoAgent/1.0.0
108
124
 
109
125
  ########################################
110
126
  # Official integrations
@@ -176,6 +192,10 @@ DEEPGRAM_LANGUAGE=multi
176
192
 
177
193
  OLLAMA_URL=http://localhost:11434
178
194
 
195
+ # Local screen OCR capture (macOS desktop context recorder).
196
+ # Set to false to disable background screen capture/OCR polling.
197
+ NEOAGENT_SCREEN_RECORDER_ENABLED=true
198
+
179
199
  ########################################
180
200
  # Messaging and voice integrations
181
201
  ########################################
package/lib/manager.js CHANGED
@@ -726,6 +726,8 @@ async function cmdLogin(args = []) {
726
726
  if (data.access_token) {
727
727
  upsertEnvValue('GITHUB_COPILOT_ACCESS_TOKEN', data.access_token);
728
728
  logOk('Successfully authenticated and saved GitHub Copilot access token to .env');
729
+ logInfo('Applying updated provider credentials by restarting NeoAgent...');
730
+ cmdRestart();
729
731
  return;
730
732
  } else if (data.error === 'authorization_pending') {
731
733
  // Continue polling
@@ -830,6 +832,8 @@ async function cmdLogin(args = []) {
830
832
  upsertEnvValue('OPENAI_CODEX_REFRESH_TOKEN', exchangeData.refresh_token);
831
833
  }
832
834
  logOk('Successfully authenticated and saved OpenAI Codex tokens to .env');
835
+ logInfo('Applying updated provider credentials by restarting NeoAgent...');
836
+ cmdRestart();
833
837
  } else {
834
838
  throw new Error('OpenAI token exchange succeeded but did not return an access token.');
835
839
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neoagent",
3
- "version": "2.3.1-beta.16",
3
+ "version": "2.3.1-beta.17",
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":"59aa584fdf100e6c78c785d8a5b565d1de4b48
37
37
 
38
38
  _flutter.loader.load({
39
39
  serviceWorkerSettings: {
40
- serviceWorkerVersion: "2591082487" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
40
+ serviceWorkerVersion: "2875227471" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
41
41
  }
42
42
  });
@@ -29,7 +29,8 @@ class OpenAIProvider extends BaseProvider {
29
29
  };
30
30
  this.client = new OpenAI({
31
31
  apiKey: config.apiKey || process.env.OPENAI_API_KEY,
32
- baseURL: config.baseUrl || process.env.OPENAI_BASE_URL || undefined
32
+ baseURL: config.baseUrl || process.env.OPENAI_BASE_URL || undefined,
33
+ defaultHeaders: config.defaultHeaders || undefined
33
34
  });
34
35
  }
35
36
 
@@ -14,7 +14,12 @@ class OpenAICodexProvider extends OpenAIProvider {
14
14
  super({
15
15
  ...config,
16
16
  apiKey: config.apiKey || process.env.OPENAI_CODEX_ACCESS_TOKEN,
17
- baseUrl
17
+ baseUrl,
18
+ defaultHeaders: {
19
+ 'Editor-Version': process.env.OPENAI_CODEX_EDITOR_VERSION || 'vscode/1.99.0',
20
+ 'Editor-Plugin-Version': process.env.OPENAI_CODEX_EDITOR_PLUGIN_VERSION || 'neoagent/1.0.0',
21
+ 'User-Agent': process.env.OPENAI_CODEX_USER_AGENT || 'NeoAgent/1.0.0'
22
+ }
18
23
  });
19
24
  this.name = 'openai-codex';
20
25
  }
@@ -19,9 +19,42 @@ class ScreenRecorder {
19
19
  this.isRecording = false;
20
20
  this.isProcessing = false;
21
21
  this.tempFilePath = path.join(os.tmpdir(), `neoagent-screen-${Date.now()}.png`);
22
+ this.lastBenignSkipAt = 0;
23
+ }
24
+
25
+ _isCaptureInactiveApp(appName) {
26
+ const normalized = String(appName || '').trim().toLowerCase();
27
+ return normalized === '' || normalized === 'loginwindow' || normalized === 'screensaverengine';
28
+ }
29
+
30
+ _isBenignCaptureError(message) {
31
+ const text = String(message || '').toLowerCase();
32
+ return (
33
+ text.includes('operation not permitted') ||
34
+ text.includes('not authorized') ||
35
+ text.includes('user canceled') ||
36
+ text.includes('cgwindowlistcreateimage') ||
37
+ text.includes('screencapture') ||
38
+ text.includes('timed out')
39
+ );
40
+ }
41
+
42
+ _logBenignSkip(reason) {
43
+ const now = Date.now();
44
+ if (now - this.lastBenignSkipAt < 5 * 60 * 1000) {
45
+ return;
46
+ }
47
+ this.lastBenignSkipAt = now;
48
+ console.warn(`[ScreenRecorder] Capture skipped: ${reason}`);
22
49
  }
23
50
 
24
51
  start() {
52
+ const enabledEnv = String(process.env.NEOAGENT_SCREEN_RECORDER_ENABLED || '').trim().toLowerCase();
53
+ if (enabledEnv === '0' || enabledEnv === 'false' || enabledEnv === 'off' || enabledEnv === 'no') {
54
+ console.log('[ScreenRecorder] Not starting: disabled by NEOAGENT_SCREEN_RECORDER_ENABLED.');
55
+ return;
56
+ }
57
+
25
58
  if (process.platform !== 'darwin') {
26
59
  console.log('[ScreenRecorder] Not starting: Screen recording is currently macOS only.');
27
60
  return;
@@ -61,6 +94,20 @@ class ScreenRecorder {
61
94
  this.isProcessing = true;
62
95
 
63
96
  try {
97
+ // Skip capture when the desktop session is inactive (e.g. locked screen).
98
+ let frontmostApp = '';
99
+ try {
100
+ const { stdout } = await execAsync(`osascript -e 'tell application "System Events" to get name of first application process whose frontmost is true'`);
101
+ frontmostApp = (stdout || '').trim();
102
+ } catch {
103
+ frontmostApp = '';
104
+ }
105
+
106
+ if (this._isCaptureInactiveApp(frontmostApp)) {
107
+ this._logBenignSkip('no active frontmost app');
108
+ return;
109
+ }
110
+
64
111
  // Capture screen silently (-x) to file
65
112
  await execAsync(`screencapture -x "${this.tempFilePath}"`);
66
113
 
@@ -80,13 +127,7 @@ class ScreenRecorder {
80
127
  const userRow = db.prepare('SELECT id FROM users ORDER BY id ASC LIMIT 1').get();
81
128
  if (userRow) {
82
129
  // Identify the active foreground app via AppleScript
83
- let appName = 'Unknown';
84
- try {
85
- const { stdout } = await execAsync(`osascript -e 'tell application "System Events" to get name of first application process whose frontmost is true'`);
86
- appName = stdout.trim();
87
- } catch (e) {
88
- // Ignore AppleScript errors
89
- }
130
+ let appName = frontmostApp || 'Unknown';
90
131
 
91
132
  db.prepare(`
92
133
  INSERT INTO screen_history (user_id, app_name, text_content)
@@ -96,7 +137,12 @@ class ScreenRecorder {
96
137
  }
97
138
 
98
139
  } catch (err) {
99
- console.error('[ScreenRecorder] Capture/OCR failed:', getErrorMessage(err));
140
+ const errorMessage = getErrorMessage(err);
141
+ if (this._isBenignCaptureError(errorMessage)) {
142
+ this._logBenignSkip(errorMessage);
143
+ } else {
144
+ console.error('[ScreenRecorder] Capture/OCR failed:', errorMessage);
145
+ }
100
146
  } finally {
101
147
  // Always cleanup the screenshot image immediately
102
148
  try {