neoagent 2.3.0 → 2.3.1-beta.10
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 +13 -0
- package/README.md +3 -1
- package/docs/automation.md +1 -1
- package/docs/capabilities.md +2 -2
- package/docs/configuration.md +14 -1
- package/docs/integrations.md +6 -1
- package/lib/manager.js +127 -1
- package/package.json +2 -1
- package/server/db/database.js +68 -0
- package/server/http/middleware.js +50 -0
- package/server/http/routes.js +3 -1
- package/server/index.js +1 -0
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/NOTICES +61 -0
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +61049 -60444
- package/server/routes/integrations.js +97 -8
- package/server/routes/memory.js +11 -2
- package/server/routes/screenHistory.js +46 -0
- package/server/routes/triggers.js +81 -0
- package/server/services/ai/engine.js +9 -0
- package/server/services/ai/models.js +30 -0
- package/server/services/ai/providers/githubCopilot.js +97 -0
- package/server/services/ai/providers/openaiCodex.js +26 -0
- package/server/services/ai/settings.js +20 -0
- package/server/services/ai/systemPrompt.js +1 -1
- package/server/services/ai/tools.js +150 -11
- package/server/services/browser/controller.js +47 -3
- package/server/services/desktop/screenRecorder.js +126 -0
- package/server/services/integrations/env.js +19 -0
- package/server/services/integrations/github/common.js +106 -0
- package/server/services/integrations/github/provider.js +499 -0
- package/server/services/integrations/github/repos.js +1124 -0
- package/server/services/integrations/home_assistant/provider.js +630 -0
- package/server/services/integrations/manager.js +63 -7
- package/server/services/integrations/oauth_provider.js +13 -6
- package/server/services/integrations/provider_config_store.js +76 -0
- package/server/services/integrations/registry.js +10 -0
- package/server/services/integrations/spotify/provider.js +487 -0
- package/server/services/integrations/weather/provider.js +559 -0
- package/server/services/integrations/whatsapp/provider.js +6 -2
- package/server/services/manager.js +22 -0
- package/server/services/memory/manager.js +39 -2
- package/server/services/messaging/manager.js +29 -7
- package/server/services/skills/base_catalog.js +33 -0
- package/server/services/tasks/adapters/index.js +2 -0
- package/server/services/tasks/adapters/manual.js +12 -0
- package/server/services/tasks/adapters/schedule.js +33 -5
- package/server/services/tasks/adapters/weather_event.js +84 -0
- package/server/services/tasks/integration_runtime.js +85 -0
- package/server/services/tasks/runtime.js +2 -2
- package/server/services/voice/agentBridge.js +20 -4
- package/server/services/voice/message.js +3 -0
- package/server/services/voice/openaiClient.js +4 -1
- package/server/services/voice/providers.js +2 -1
- package/server/services/voice/runtimeManager.js +136 -1
- package/server/services/widgets/service.js +49 -4
- package/server/utils/local_secrets.js +56 -0
- package/server/utils/logger.js +37 -9
|
@@ -13,6 +13,22 @@ const {
|
|
|
13
13
|
|
|
14
14
|
const OAUTH_STATE_PATTERN = /^[a-f0-9]{32,128}$/i;
|
|
15
15
|
|
|
16
|
+
function isLikelyExpiredConnectionError(error) {
|
|
17
|
+
const message = String(error?.message || error || '').toLowerCase();
|
|
18
|
+
if (!message) return false;
|
|
19
|
+
return [
|
|
20
|
+
'invalid_grant',
|
|
21
|
+
'token refresh failed',
|
|
22
|
+
'token expired',
|
|
23
|
+
'access token is missing',
|
|
24
|
+
'refresh token is missing',
|
|
25
|
+
'reconnect this integration account',
|
|
26
|
+
'account is no longer authorized',
|
|
27
|
+
'reauthorize',
|
|
28
|
+
're-authorize',
|
|
29
|
+
].some((hint) => message.includes(hint));
|
|
30
|
+
}
|
|
31
|
+
|
|
16
32
|
class IntegrationManager {
|
|
17
33
|
constructor(options = {}) {
|
|
18
34
|
this.app = options.app || null;
|
|
@@ -147,7 +163,12 @@ class IntegrationManager {
|
|
|
147
163
|
|
|
148
164
|
return this.registry
|
|
149
165
|
.list()
|
|
150
|
-
.map((provider) =>
|
|
166
|
+
.map((provider) =>
|
|
167
|
+
provider.buildSnapshot(rowsByProvider.get(provider.key) || [], {
|
|
168
|
+
userId,
|
|
169
|
+
agentId: scopedAgentId,
|
|
170
|
+
}),
|
|
171
|
+
);
|
|
151
172
|
}
|
|
152
173
|
|
|
153
174
|
async beginOAuth(userId, providerKey, options = {}) {
|
|
@@ -163,7 +184,10 @@ class IntegrationManager {
|
|
|
163
184
|
throw new Error(`Unknown ${provider.label} app: ${appKey || 'missing app key'}`);
|
|
164
185
|
}
|
|
165
186
|
|
|
166
|
-
const env = provider.getEnvStatus(
|
|
187
|
+
const env = provider.getEnvStatus({
|
|
188
|
+
userId,
|
|
189
|
+
agentId,
|
|
190
|
+
});
|
|
167
191
|
if (!env.configured) {
|
|
168
192
|
throw new Error(env.summary);
|
|
169
193
|
}
|
|
@@ -389,7 +413,10 @@ class IntegrationManager {
|
|
|
389
413
|
getToolDefinitions(userId, agentId = null) {
|
|
390
414
|
const definitions = [];
|
|
391
415
|
for (const provider of this.registry.list()) {
|
|
392
|
-
const env = provider.getEnvStatus(
|
|
416
|
+
const env = provider.getEnvStatus({
|
|
417
|
+
userId,
|
|
418
|
+
agentId,
|
|
419
|
+
});
|
|
393
420
|
if (!env.configured) continue;
|
|
394
421
|
const connections = this.listConnections(userId, provider.key, agentId);
|
|
395
422
|
const connectedAppIds = Array.from(
|
|
@@ -411,9 +438,15 @@ class IntegrationManager {
|
|
|
411
438
|
if (!provider) {
|
|
412
439
|
throw new Error(`Unknown integration provider: ${providerKey}`);
|
|
413
440
|
}
|
|
414
|
-
const env = provider.getEnvStatus(
|
|
441
|
+
const env = provider.getEnvStatus({
|
|
442
|
+
userId,
|
|
443
|
+
agentId,
|
|
444
|
+
});
|
|
415
445
|
const connections = this.listConnections(userId, provider.key, agentId);
|
|
416
|
-
const snapshot = provider.buildSnapshot(connections
|
|
446
|
+
const snapshot = provider.buildSnapshot(connections, {
|
|
447
|
+
userId,
|
|
448
|
+
agentId,
|
|
449
|
+
});
|
|
417
450
|
const connectedAppIds = snapshot.apps
|
|
418
451
|
.filter((app) => app.connection.connected)
|
|
419
452
|
.map((app) => app.id);
|
|
@@ -582,7 +615,10 @@ class IntegrationManager {
|
|
|
582
615
|
for (const provider of this.registry.list()) {
|
|
583
616
|
if (!provider.supportsTool(toolName)) continue;
|
|
584
617
|
foundSupportingProvider = true;
|
|
585
|
-
const env = provider.getEnvStatus(
|
|
618
|
+
const env = provider.getEnvStatus({
|
|
619
|
+
userId,
|
|
620
|
+
agentId,
|
|
621
|
+
});
|
|
586
622
|
if (!env.configured) {
|
|
587
623
|
return { error: env.summary };
|
|
588
624
|
}
|
|
@@ -607,6 +643,17 @@ class IntegrationManager {
|
|
|
607
643
|
selection.connection,
|
|
608
644
|
);
|
|
609
645
|
} catch (err) {
|
|
646
|
+
if (isLikelyExpiredConnectionError(err)) {
|
|
647
|
+
db.prepare(
|
|
648
|
+
`UPDATE integration_connections
|
|
649
|
+
SET status = 'expired', updated_at = datetime('now')
|
|
650
|
+
WHERE id = ? AND user_id = ? AND agent_id = ?`,
|
|
651
|
+
).run(
|
|
652
|
+
selection.connection.id,
|
|
653
|
+
userId,
|
|
654
|
+
resolveAgentId(userId, agentId),
|
|
655
|
+
);
|
|
656
|
+
}
|
|
610
657
|
return { error: err?.message || 'execution_error' };
|
|
611
658
|
}
|
|
612
659
|
if (!execution) {
|
|
@@ -641,7 +688,13 @@ class IntegrationManager {
|
|
|
641
688
|
summarizeConnectedProviders(userId, agentId = null) {
|
|
642
689
|
const providers = this.registry.list().map((provider) => ({
|
|
643
690
|
provider,
|
|
644
|
-
snapshot: provider.buildSnapshot(
|
|
691
|
+
snapshot: provider.buildSnapshot(
|
|
692
|
+
this.listConnections(userId, provider.key, agentId),
|
|
693
|
+
{
|
|
694
|
+
userId,
|
|
695
|
+
agentId,
|
|
696
|
+
},
|
|
697
|
+
),
|
|
645
698
|
}));
|
|
646
699
|
|
|
647
700
|
if (providers.length === 0) {
|
|
@@ -655,6 +708,9 @@ class IntegrationManager {
|
|
|
655
708
|
}
|
|
656
709
|
|
|
657
710
|
if (!snapshot?.env?.configured) {
|
|
711
|
+
if (snapshot?.env?.setupMode === 'user') {
|
|
712
|
+
return `${provider.label}: setup is not complete for this user yet. If the user wants to use it, tell them to finish setup in Official Integrations first.`;
|
|
713
|
+
}
|
|
658
714
|
return `${provider.label}: available but not configured on the server yet. If the user wants to use it, tell them to finish setup in Official Integrations first.`;
|
|
659
715
|
}
|
|
660
716
|
|
|
@@ -247,8 +247,8 @@ function createOAuthProvider(options = {}) {
|
|
|
247
247
|
getToolAppId(toolName) {
|
|
248
248
|
return toolAppMap.get(String(toolName || '').trim()) || null;
|
|
249
249
|
},
|
|
250
|
-
getEnvStatus() {
|
|
251
|
-
return options.getEnvStatus();
|
|
250
|
+
getEnvStatus(context = {}) {
|
|
251
|
+
return options.getEnvStatus(context);
|
|
252
252
|
},
|
|
253
253
|
getToolDefinitions(toolOptions = {}) {
|
|
254
254
|
const connectedAppIds = new Set(toolOptions.connectedAppIds || []);
|
|
@@ -257,8 +257,8 @@ function createOAuthProvider(options = {}) {
|
|
|
257
257
|
supportsTool(toolName) {
|
|
258
258
|
return toolAppMap.has(String(toolName || '').trim());
|
|
259
259
|
},
|
|
260
|
-
buildSnapshot(connectionRows) {
|
|
261
|
-
const env = this.getEnvStatus();
|
|
260
|
+
buildSnapshot(connectionRows, context = {}) {
|
|
261
|
+
const env = this.getEnvStatus(context);
|
|
262
262
|
const byApp = new Map();
|
|
263
263
|
for (const row of Array.isArray(connectionRows) ? connectionRows : []) {
|
|
264
264
|
const appId = String(row.app_key || '').trim();
|
|
@@ -319,7 +319,7 @@ function createOAuthProvider(options = {}) {
|
|
|
319
319
|
throw new Error(`Unknown ${this.label} app: ${appKey}`);
|
|
320
320
|
}
|
|
321
321
|
const normalizedState = assertValidOAuthState(state);
|
|
322
|
-
const env = this.getEnvStatus();
|
|
322
|
+
const env = this.getEnvStatus({ userId });
|
|
323
323
|
if (!env.configured) {
|
|
324
324
|
throw new Error(env.summary);
|
|
325
325
|
}
|
|
@@ -343,7 +343,7 @@ function createOAuthProvider(options = {}) {
|
|
|
343
343
|
userId,
|
|
344
344
|
state: normalizedState,
|
|
345
345
|
app,
|
|
346
|
-
env: this.getEnvStatus(),
|
|
346
|
+
env: this.getEnvStatus({ userId }),
|
|
347
347
|
});
|
|
348
348
|
},
|
|
349
349
|
async disconnect(connectionRow) {
|
|
@@ -368,12 +368,16 @@ function createOAuthProvider(options = {}) {
|
|
|
368
368
|
appId: toolAppMap.get(String(toolName || '').trim()) || connectionRow.app_key,
|
|
369
369
|
connection: connectionRow,
|
|
370
370
|
credentials,
|
|
371
|
+
env: this.getEnvStatus({ userId: connectionRow?.user_id }),
|
|
371
372
|
});
|
|
372
373
|
},
|
|
373
374
|
summarizeConnection(connectionRows) {
|
|
374
375
|
const snapshot = this.buildSnapshot(connectionRows);
|
|
375
376
|
if (!snapshot.connection.connected) {
|
|
376
377
|
if (snapshot.connection.status === 'env_not_configured') {
|
|
378
|
+
if (snapshot?.env?.setupMode === 'user') {
|
|
379
|
+
return `${this.label} still needs per-user setup before accounts can connect.`;
|
|
380
|
+
}
|
|
377
381
|
return `${this.label} still needs administrator setup before accounts can connect.`;
|
|
378
382
|
}
|
|
379
383
|
return `${this.label} is not connected.`;
|
|
@@ -392,6 +396,9 @@ function createOAuthProvider(options = {}) {
|
|
|
392
396
|
},
|
|
393
397
|
summarizeForModel(snapshot) {
|
|
394
398
|
if (!snapshot?.env?.configured) {
|
|
399
|
+
if (snapshot?.env?.setupMode === 'user') {
|
|
400
|
+
return `${this.label}: setup is user-managed and is not complete yet. If the user wants to use it, tell them to open Official Integrations and finish setup in their account first.`;
|
|
401
|
+
}
|
|
395
402
|
return `${this.label}: workspace setup is not complete yet. If the user wants to use it, tell them to open Official Integrations and ask an administrator to finish setup first.`;
|
|
396
403
|
}
|
|
397
404
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const db = require('../../db/database');
|
|
4
|
+
const { decryptValue, encryptValue } = require('./secrets');
|
|
5
|
+
|
|
6
|
+
function normalizeProviderKey(providerKey) {
|
|
7
|
+
return String(providerKey || '').trim();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function parseConfig(value) {
|
|
11
|
+
try {
|
|
12
|
+
const parsed = JSON.parse(decryptValue(value || '{}') || '{}');
|
|
13
|
+
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
14
|
+
} catch {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function getProviderConfig(userId, providerKey) {
|
|
20
|
+
const normalizedProviderKey = normalizeProviderKey(providerKey);
|
|
21
|
+
if (!Number.isInteger(Number(userId)) || Number(userId) <= 0 || !normalizedProviderKey) {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const row = db
|
|
26
|
+
.prepare(
|
|
27
|
+
`SELECT config_json
|
|
28
|
+
FROM integration_provider_configs
|
|
29
|
+
WHERE user_id = ? AND provider_key = ?`,
|
|
30
|
+
)
|
|
31
|
+
.get(Number(userId), normalizedProviderKey);
|
|
32
|
+
|
|
33
|
+
return parseConfig(row?.config_json);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function setProviderConfig(userId, providerKey, config) {
|
|
37
|
+
const normalizedProviderKey = normalizeProviderKey(providerKey);
|
|
38
|
+
if (!Number.isInteger(Number(userId)) || Number(userId) <= 0 || !normalizedProviderKey) {
|
|
39
|
+
throw new Error('A valid user and provider are required to save integration config.');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const payload = config && typeof config === 'object' ? config : {};
|
|
43
|
+
db.prepare(
|
|
44
|
+
`INSERT INTO integration_provider_configs (
|
|
45
|
+
user_id,
|
|
46
|
+
provider_key,
|
|
47
|
+
config_json,
|
|
48
|
+
created_at,
|
|
49
|
+
updated_at
|
|
50
|
+
) VALUES (?, ?, ?, datetime('now'), datetime('now'))
|
|
51
|
+
ON CONFLICT(user_id, provider_key) DO UPDATE SET
|
|
52
|
+
config_json = excluded.config_json,
|
|
53
|
+
updated_at = excluded.updated_at`,
|
|
54
|
+
).run(
|
|
55
|
+
Number(userId),
|
|
56
|
+
normalizedProviderKey,
|
|
57
|
+
encryptValue(JSON.stringify(payload)),
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function deleteProviderConfig(userId, providerKey) {
|
|
62
|
+
const normalizedProviderKey = normalizeProviderKey(providerKey);
|
|
63
|
+
if (!Number.isInteger(Number(userId)) || Number(userId) <= 0 || !normalizedProviderKey) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
db.prepare(
|
|
68
|
+
'DELETE FROM integration_provider_configs WHERE user_id = ? AND provider_key = ?',
|
|
69
|
+
).run(Number(userId), normalizedProviderKey);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = {
|
|
73
|
+
deleteProviderConfig,
|
|
74
|
+
getProviderConfig,
|
|
75
|
+
setProviderConfig,
|
|
76
|
+
};
|
|
@@ -2,19 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
const { createFigmaProvider } = require('./figma/provider');
|
|
4
4
|
const { createGoogleWorkspaceProvider } = require('./google/provider');
|
|
5
|
+
const { createGithubProvider } = require('./github/provider');
|
|
6
|
+
const { createHomeAssistantProvider } = require('./home_assistant/provider');
|
|
5
7
|
const { createMicrosoftProvider } = require('./microsoft/provider');
|
|
6
8
|
const { createNotionProvider } = require('./notion/provider');
|
|
9
|
+
const { createSpotifyProvider } = require('./spotify/provider');
|
|
7
10
|
const { createSlackProvider } = require('./slack/provider');
|
|
11
|
+
const { createWeatherProvider } = require('./weather/provider');
|
|
8
12
|
const { createWhatsAppPersonalProvider } = require('./whatsapp');
|
|
13
|
+
const { createCodexProvider } = require('./codex/provider');
|
|
9
14
|
|
|
10
15
|
function createIntegrationRegistry(options = {}) {
|
|
11
16
|
const providers = [
|
|
12
17
|
createGoogleWorkspaceProvider(),
|
|
18
|
+
createGithubProvider(),
|
|
13
19
|
createNotionProvider(),
|
|
14
20
|
createMicrosoftProvider(),
|
|
15
21
|
createSlackProvider(),
|
|
16
22
|
createFigmaProvider(),
|
|
23
|
+
createHomeAssistantProvider(),
|
|
24
|
+
createWeatherProvider(),
|
|
25
|
+
createSpotifyProvider(),
|
|
17
26
|
createWhatsAppPersonalProvider(options),
|
|
27
|
+
createCodexProvider(),
|
|
18
28
|
];
|
|
19
29
|
const byKey = new Map(providers.map((provider) => [provider.key, provider]));
|
|
20
30
|
|