neoagent 2.3.1-beta.5 → 2.3.1-beta.7

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.
Files changed (33) hide show
  1. package/.env.example +13 -0
  2. package/docs/configuration.md +8 -4
  3. package/docs/integrations.md +1 -1
  4. package/package.json +3 -1
  5. package/server/db/database.js +11 -0
  6. package/server/http/middleware.js +50 -0
  7. package/server/index.js +1 -0
  8. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  9. package/server/public/flutter_bootstrap.js +1 -1
  10. package/server/public/main.dart.js +50245 -49865
  11. package/server/routes/integrations.js +86 -0
  12. package/server/routes/memory.js +11 -2
  13. package/server/{http/routes → routes}/screenHistory.js +2 -2
  14. package/server/{http/routes → routes}/triggers.js +2 -2
  15. package/server/services/ai/models.js +37 -1
  16. package/server/services/ai/providers/codex.js +244 -0
  17. package/server/services/ai/providers/copilot.js +457 -0
  18. package/server/services/ai/settings.js +20 -0
  19. package/server/services/integrations/codex/provider.js +281 -0
  20. package/server/services/integrations/env.js +5 -0
  21. package/server/services/integrations/github/common.js +106 -0
  22. package/server/services/integrations/github/provider.js +499 -0
  23. package/server/services/integrations/github/repos.js +1124 -0
  24. package/server/services/integrations/home_assistant/provider.js +306 -26
  25. package/server/services/integrations/manager.js +63 -7
  26. package/server/services/integrations/oauth_provider.js +13 -6
  27. package/server/services/integrations/provider_config_store.js +76 -0
  28. package/server/services/integrations/registry.js +4 -0
  29. package/server/services/memory/manager.js +39 -2
  30. package/server/services/voice/openaiClient.js +4 -1
  31. package/server/services/voice/providers.js +2 -1
  32. package/server/utils/local_secrets.js +56 -0
  33. package/server/utils/logger.js +37 -9
package/.env.example CHANGED
@@ -6,6 +6,10 @@ PORT=3333
6
6
  NODE_ENV=production
7
7
  SESSION_SECRET=change-this-to-a-random-secret-in-production
8
8
 
9
+ # Optional: dedicated key for encrypting local at-rest secrets (for example API_KEYS.json).
10
+ # If unset, encryption falls back to SESSION_SECRET.
11
+ # NEOAGENT_DATA_ENCRYPTION_KEY=
12
+
9
13
  # Public base URL used for OAuth callbacks and external links.
10
14
  # Example: https://agent.example.com
11
15
  PUBLIC_URL=
@@ -139,6 +143,15 @@ FIGMA_OAUTH_CLIENT_ID=your-figma-oauth-client-id
139
143
  FIGMA_OAUTH_CLIENT_SECRET=your-figma-oauth-client-secret
140
144
  FIGMA_OAUTH_REDIRECT_URI=
141
145
 
146
+ # GitHub official integration OAuth client.
147
+ # Redirect URI should match <PUBLIC_URL>/api/integrations/oauth/callback.
148
+ # Prefer least-privilege OAuth scopes for your use case.
149
+ # Read-oriented examples: public_repo (public repositories), repo:status (commit status), read:org (org membership).
150
+ # Avoid broad blanket scopes unless explicitly required by a specific write workflow.
151
+ GITHUB_OAUTH_CLIENT_ID=your-github-oauth-client-id
152
+ GITHUB_OAUTH_CLIENT_SECRET=your-github-oauth-client-secret
153
+ GITHUB_OAUTH_REDIRECT_URI=
154
+
142
155
  ########################################
143
156
  # Tools and media services
144
157
  ########################################
@@ -90,14 +90,18 @@ All OAuth callbacks default to `PUBLIC_URL + /api/integrations/oauth/callback` u
90
90
  | `FIGMA_OAUTH_CLIENT_ID` | Figma OAuth client ID |
91
91
  | `FIGMA_OAUTH_CLIENT_SECRET` | Figma OAuth client secret |
92
92
  | `FIGMA_OAUTH_REDIRECT_URI` | Optional Figma OAuth callback URL |
93
- | `HOME_ASSISTANT_BASE_URL` | Home Assistant base URL, for example `https://ha.example.com` |
94
- | `HOME_ASSISTANT_OAUTH_CLIENT_ID` | Home Assistant OAuth client ID |
95
- | `HOME_ASSISTANT_OAUTH_CLIENT_SECRET` | Home Assistant OAuth client secret |
96
- | `HOME_ASSISTANT_OAUTH_REDIRECT_URI` | Optional Home Assistant OAuth callback URL |
93
+ | `HOME_ASSISTANT_BASE_URL` | Optional fallback Home Assistant base URL. Users can configure this per account in Official Integrations. |
94
+ | `HOME_ASSISTANT_OAUTH_CLIENT_ID` | Optional fallback Home Assistant OAuth client ID. |
95
+ | `HOME_ASSISTANT_OAUTH_CLIENT_SECRET` | Optional fallback Home Assistant OAuth client secret. |
96
+ | `HOME_ASSISTANT_OAUTH_REDIRECT_URI` | Optional fallback Home Assistant OAuth callback URL. |
97
+ | `HOME_ASSISTANT_ALLOW_PRIVATE_BASE_URL` | Optional safety override. Set to `1` only if you intentionally allow Home Assistant base URLs on localhost/private networks. |
97
98
  | `SPOTIFY_OAUTH_CLIENT_ID` | Spotify OAuth client ID |
98
99
  | `SPOTIFY_OAUTH_CLIENT_SECRET` | Spotify OAuth client secret |
99
100
  | `SPOTIFY_OAUTH_REDIRECT_URI` | Optional Spotify OAuth callback URL |
100
101
 
102
+ Home Assistant no longer requires server-side setup. Each user can open Official Integrations, select Home Assistant, and enter their own base URL and OAuth app credentials.
103
+ For safety, local/private Home Assistant targets are blocked by default unless `HOME_ASSISTANT_ALLOW_PRIVATE_BASE_URL=1` is set on the server.
104
+
101
105
  Weather integration uses Open-Meteo public endpoints and does not require OAuth environment variables.
102
106
 
103
107
  ## Messaging
@@ -17,7 +17,7 @@ The built-in registry includes:
17
17
  | Weather | Keyless Open-Meteo current weather and forecast tools |
18
18
  | Spotify | Playback state, recently played, search, and playback controls |
19
19
 
20
- OAuth app credentials are configured through server environment variables. Account connections are created in the Flutter UI under **Integrations**. Connected tools are exposed to the agent as structured tools, so prefer them over browser automation when they can do the job.
20
+ OAuth app credentials are configured through server environment variables for most providers. Home Assistant can also be configured per-user in the Flutter **Integrations** UI without any server-side setup. Account connections are created in the Flutter UI under **Integrations**. Connected tools are exposed to the agent as structured tools, so prefer them over browser automation when they can do the job.
21
21
 
22
22
  Weather note: the Weather integration uses Open-Meteo public APIs and does not require OAuth client credentials.
23
23
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neoagent",
3
- "version": "2.3.1-beta.5",
3
+ "version": "2.3.1-beta.7",
4
4
  "description": "Proactive personal AI agent with no limits",
5
5
  "license": "MIT",
6
6
  "main": "server/index.js",
@@ -51,6 +51,7 @@
51
51
  "homepage": "https://github.com/NeoLabs-Systems/NeoAgent#readme",
52
52
  "dependencies": {
53
53
  "@anthropic-ai/sdk": "^0.39.0",
54
+ "@github/copilot-sdk": "^0.3.0",
54
55
  "@google/generative-ai": "^0.24.0",
55
56
  "@modelcontextprotocol/sdk": "^1.12.1",
56
57
  "baileys": "^6.7.21",
@@ -66,6 +67,7 @@
66
67
  "geoip-lite": "^1.4.10",
67
68
  "googleapis": "^150.0.1",
68
69
  "helmet": "^8.0.0",
70
+ "ipaddr.js": "^2.3.0",
69
71
  "multer": "^1.4.5-lts.1",
70
72
  "node-cron": "^3.0.3",
71
73
  "node-pty": "^1.0.0",
@@ -272,6 +272,17 @@ db.exec(`
272
272
  FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE SET NULL
273
273
  );
274
274
 
275
+ CREATE TABLE IF NOT EXISTS integration_provider_configs (
276
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
277
+ user_id INTEGER NOT NULL,
278
+ provider_key TEXT NOT NULL,
279
+ config_json TEXT DEFAULT '{}',
280
+ created_at TEXT DEFAULT (datetime('now')),
281
+ updated_at TEXT DEFAULT (datetime('now')),
282
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
283
+ UNIQUE(user_id, provider_key)
284
+ );
285
+
275
286
  CREATE TABLE IF NOT EXISTS browser_extension_pairing_requests (
276
287
  id TEXT PRIMARY KEY,
277
288
  user_id INTEGER,
@@ -88,7 +88,18 @@ function buildHelmetOptions({ secureCookies }) {
88
88
  return {
89
89
  strictTransportSecurity: false,
90
90
  crossOriginOpenerPolicy: false,
91
+ crossOriginResourcePolicy: { policy: 'same-site' },
91
92
  originAgentCluster: false,
93
+ referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
94
+ permissionsPolicy: {
95
+ features: {
96
+ camera: ['self'],
97
+ geolocation: ['self'],
98
+ microphone: ['self'],
99
+ payment: [],
100
+ usb: [],
101
+ },
102
+ },
92
103
  contentSecurityPolicy: {
93
104
  directives: {
94
105
  defaultSrc: ["'self'"],
@@ -154,6 +165,16 @@ function applyHttpMiddleware(app, { secureCookies, trustProxy, sessionMiddleware
154
165
  const path = `${value}`.split('?')[0];
155
166
  return path === '/socket.io/' || path === '/socket.io' || path.startsWith('/socket.io/');
156
167
  };
168
+ const isStateChangingMethod = (method = '') => ['POST', 'PUT', 'PATCH', 'DELETE'].includes(String(method).toUpperCase());
169
+ const extractOriginFromReferer = (referer = '') => {
170
+ const value = String(referer || '').trim();
171
+ if (!value) return '';
172
+ try {
173
+ return new URL(value).origin;
174
+ } catch {
175
+ return '';
176
+ }
177
+ };
157
178
  const requestPath = (req) => req.originalUrl || req.url || req.path || '';
158
179
  const applyOnlyToRecordingChunk = (handler) => (req, res, next) => (
159
180
  isRecordingChunkPath(requestPath(req)) ? handler(req, res, next) : next()
@@ -187,6 +208,35 @@ function applyHttpMiddleware(app, { secureCookies, trustProxy, sessionMiddleware
187
208
  });
188
209
  })
189
210
  );
211
+ app.use((req, res, next) => {
212
+ const path = `${req.originalUrl || req.url || req.path || ''}`.split('?')[0];
213
+ if (path.startsWith('/api/')) {
214
+ res.setHeader('Cache-Control', 'no-store, max-age=0');
215
+ res.setHeader('Pragma', 'no-cache');
216
+ res.setHeader('Expires', '0');
217
+ }
218
+ next();
219
+ });
220
+ app.use((req, res, next) => {
221
+ const path = `${req.originalUrl || req.url || req.path || ''}`.split('?')[0];
222
+ if (!path.startsWith('/api/')) return next();
223
+ if (!isStateChangingMethod(req.method)) return next();
224
+
225
+ const origin = String(req.get('origin') || '').trim() || extractOriginFromReferer(req.get('referer'));
226
+ if (!origin) return next();
227
+
228
+ const allowBrowserExtensionOrigin = isBrowserExtensionCorsPath(path);
229
+ return validateOrigin(origin, (error) => {
230
+ if (error) {
231
+ logRequestSummary('warn', req, 'blocked state-changing request due to invalid origin', { origin });
232
+ return res.status(403).json({ error: 'Origin not allowed' });
233
+ }
234
+ return next();
235
+ }, {
236
+ allowChromeExtension: allowBrowserExtensionOrigin,
237
+ allowMissingOrigin: false,
238
+ });
239
+ });
190
240
  app.use((req, res, next) => {
191
241
  const startedAt = Date.now();
192
242
 
package/server/index.js CHANGED
@@ -97,6 +97,7 @@ if (!configuredSessionSecret()) {
97
97
  }
98
98
 
99
99
  const app = express();
100
+ app.disable('x-powered-by');
100
101
  const httpServer = createServer(app);
101
102
  const io = createSocketServer(httpServer, { validateOrigin });
102
103
  app.locals.httpRuntimeConfig = {
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"59aa584fdf100e6c78c785d8a5b565d1de4b48
37
37
 
38
38
  _flutter.loader.load({
39
39
  serviceWorkerSettings: {
40
- serviceWorkerVersion: "4123024040" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
40
+ serviceWorkerVersion: "1495797212" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
41
41
  }
42
42
  });