neoagent 2.1.7 → 2.1.8
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/README.md +1 -1
- package/docs/configuration.md +1 -0
- package/lib/manager.js +2 -0
- package/package.json +1 -1
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/services/ai/models.js +8 -0
- package/server/services/ai/providers/anthropic.js +4 -2
- package/server/services/ai/settings.js +10 -0
- package/server/services/android/controller.js +30 -5
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
[](LICENSE)
|
|
11
11
|
|
|
12
12
|
A self-hosted, proactive AI agent with a Flutter client for web and Android.
|
|
13
|
-
Connects to OpenAI, xAI, Google, and local Ollama.
|
|
13
|
+
Connects to OpenAI, xAI, Google, MiniMax Code, and local Ollama.
|
|
14
14
|
Runs tasks on a schedule, controls a browser, manages files, and talks to you over Telegram, Discord, or WhatsApp.
|
|
15
15
|
|
|
16
16
|
```bash
|
package/docs/configuration.md
CHANGED
|
@@ -25,6 +25,7 @@ At least one API key is required. The active provider and model are configured i
|
|
|
25
25
|
| `OPENAI_API_KEY` | GPT-4o / Whisper (OpenAI) |
|
|
26
26
|
| `XAI_API_KEY` | Grok (xAI) |
|
|
27
27
|
| `GOOGLE_AI_KEY` | Gemini (Google) |
|
|
28
|
+
| `MINIMAX_API_KEY` | MiniMax Code (Coding Plan / Token Plan for `MiniMax-M2.7`) |
|
|
28
29
|
| `BRAVE_SEARCH_API_KEY` | Brave Search API for the native `web_search` tool |
|
|
29
30
|
| `DEEPGRAM_API_KEY` | Recordings transcription with Deepgram Nova-3 multilingual |
|
|
30
31
|
| `DEEPGRAM_MODEL` | Deepgram speech model override (defaults to `nova-3`) |
|
package/lib/manager.js
CHANGED
|
@@ -268,6 +268,7 @@ async function cmdSetup() {
|
|
|
268
268
|
const openai = await ask('OpenAI API key', current.OPENAI_API_KEY || '');
|
|
269
269
|
const xai = await ask('xAI API key', current.XAI_API_KEY || '');
|
|
270
270
|
const google = await ask('Google API key', current.GOOGLE_AI_KEY || '');
|
|
271
|
+
const minimax = await ask('MiniMax Code key', current.MINIMAX_API_KEY || '');
|
|
271
272
|
const brave = await ask('Brave Search API key', current.BRAVE_SEARCH_API_KEY || '');
|
|
272
273
|
const ollama = await ask('Ollama URL', current.OLLAMA_URL || 'http://localhost:11434');
|
|
273
274
|
const origins = await ask('Allowed CORS origins', current.ALLOWED_ORIGINS || '');
|
|
@@ -280,6 +281,7 @@ async function cmdSetup() {
|
|
|
280
281
|
openai ? `OPENAI_API_KEY=${openai}` : '',
|
|
281
282
|
xai ? `XAI_API_KEY=${xai}` : '',
|
|
282
283
|
google ? `GOOGLE_AI_KEY=${google}` : '',
|
|
284
|
+
minimax ? `MINIMAX_API_KEY=${minimax}` : '',
|
|
283
285
|
brave ? `BRAVE_SEARCH_API_KEY=${brave}` : '',
|
|
284
286
|
ollama ? `OLLAMA_URL=${ollama}` : '',
|
|
285
287
|
origins ? `ALLOWED_ORIGINS=${origins}` : ''
|
package/package.json
CHANGED
|
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"052f31d115eceda8cbff1b3481fcde4330c4ae
|
|
|
37
37
|
|
|
38
38
|
_flutter.loader.load({
|
|
39
39
|
serviceWorkerSettings: {
|
|
40
|
-
serviceWorkerVersion: "
|
|
40
|
+
serviceWorkerVersion: "2922699188" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
|
|
41
41
|
}
|
|
42
42
|
});
|
|
@@ -45,6 +45,12 @@ const STATIC_MODELS = [
|
|
|
45
45
|
provider: 'google',
|
|
46
46
|
purpose: 'general'
|
|
47
47
|
},
|
|
48
|
+
{
|
|
49
|
+
id: 'MiniMax-M2.7',
|
|
50
|
+
label: 'MiniMax M2.7 (Coding Plan)',
|
|
51
|
+
provider: 'minimax',
|
|
52
|
+
purpose: 'coding'
|
|
53
|
+
},
|
|
48
54
|
{
|
|
49
55
|
id: 'qwen3.5:4b',
|
|
50
56
|
label: 'Qwen 3.5 4B (Local / Ollama)',
|
|
@@ -210,6 +216,8 @@ function createProviderInstance(providerStr, userId = null, configOverrides = {}
|
|
|
210
216
|
return new AnthropicProvider({ apiKey: runtime.apiKey, baseUrl: runtime.baseUrl, ...configOverrides });
|
|
211
217
|
} else if (providerStr === 'google') {
|
|
212
218
|
return new GoogleProvider({ apiKey: runtime.apiKey, ...configOverrides });
|
|
219
|
+
} else if (providerStr === 'minimax') {
|
|
220
|
+
return new AnthropicProvider({ apiKey: runtime.apiKey, baseUrl: runtime.baseUrl, ...configOverrides });
|
|
213
221
|
} else if (providerStr === 'ollama') {
|
|
214
222
|
return new OllamaProvider({ baseUrl: runtime.baseUrl, ...configOverrides });
|
|
215
223
|
}
|
|
@@ -9,13 +9,15 @@ class AnthropicProvider extends BaseProvider {
|
|
|
9
9
|
'claude-sonnet-4-20250514',
|
|
10
10
|
'claude-3-5-sonnet-20241022',
|
|
11
11
|
'claude-3-5-haiku-20241022',
|
|
12
|
-
'claude-3-opus-20240229'
|
|
12
|
+
'claude-3-opus-20240229',
|
|
13
|
+
'MiniMax-M2.7'
|
|
13
14
|
];
|
|
14
15
|
this.contextWindows = {
|
|
15
16
|
'claude-sonnet-4-20250514': 200000,
|
|
16
17
|
'claude-3-5-sonnet-20241022': 200000,
|
|
17
18
|
'claude-3-5-haiku-20241022': 200000,
|
|
18
|
-
'claude-3-opus-20240229': 200000
|
|
19
|
+
'claude-3-opus-20240229': 200000,
|
|
20
|
+
'MiniMax-M2.7': 204800
|
|
19
21
|
};
|
|
20
22
|
this.client = new Anthropic({
|
|
21
23
|
apiKey: config.apiKey || process.env.ANTHROPIC_API_KEY,
|
|
@@ -41,6 +41,16 @@ const AI_PROVIDER_DEFINITIONS = Object.freeze({
|
|
|
41
41
|
defaultEnabled: true,
|
|
42
42
|
defaultBaseUrl: 'https://api.x.ai/v1'
|
|
43
43
|
},
|
|
44
|
+
minimax: {
|
|
45
|
+
id: 'minimax',
|
|
46
|
+
label: 'MiniMax Code',
|
|
47
|
+
description: 'MiniMax Coding Plan for MiniMax-M2.7 through the Anthropic-compatible endpoint.',
|
|
48
|
+
envKey: 'MINIMAX_API_KEY',
|
|
49
|
+
supportsApiKey: true,
|
|
50
|
+
supportsBaseUrl: true,
|
|
51
|
+
defaultEnabled: false,
|
|
52
|
+
defaultBaseUrl: 'https://api.minimax.io/anthropic'
|
|
53
|
+
},
|
|
44
54
|
ollama: {
|
|
45
55
|
id: 'ollama',
|
|
46
56
|
label: 'Ollama',
|
|
@@ -456,6 +456,20 @@ function updateIniValue(content, key, value) {
|
|
|
456
456
|
return `${content.replace(/\s*$/, '')}\n${line}\n`;
|
|
457
457
|
}
|
|
458
458
|
|
|
459
|
+
function readIniValue(content, key) {
|
|
460
|
+
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
461
|
+
const match = content.match(new RegExp(`^${escapedKey}=(.*)$`, 'm'));
|
|
462
|
+
return match ? String(match[1] || '').trim() : null;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function systemImagePackageToRelativeDir(packageName) {
|
|
466
|
+
const parts = String(packageName || '').split(';').filter(Boolean);
|
|
467
|
+
if (parts.length !== 4 || parts[0] !== 'system-images') {
|
|
468
|
+
return null;
|
|
469
|
+
}
|
|
470
|
+
return `${parts.join('/')}/`;
|
|
471
|
+
}
|
|
472
|
+
|
|
459
473
|
function sanitizeUiXml(raw) {
|
|
460
474
|
const text = String(raw || '');
|
|
461
475
|
const start = text.indexOf('<?xml');
|
|
@@ -564,10 +578,10 @@ class AndroidController {
|
|
|
564
578
|
const preferredInstalled =
|
|
565
579
|
chooseConfiguredSystemImage(installedImages) ||
|
|
566
580
|
chooseLatestSystemImage(installedImages);
|
|
567
|
-
if (
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
) {
|
|
581
|
+
if (!preferredInstalled) {
|
|
582
|
+
throw new Error(formatSystemImageError(installedImages));
|
|
583
|
+
}
|
|
584
|
+
if (preferredInstalled.packageName !== state.systemImage) {
|
|
571
585
|
appendState({
|
|
572
586
|
bootstrapped: true,
|
|
573
587
|
systemImage: preferredInstalled.packageName,
|
|
@@ -655,7 +669,18 @@ class AndroidController {
|
|
|
655
669
|
const pkg = state.systemImage;
|
|
656
670
|
if (!pkg) throw new Error('Android system image not installed');
|
|
657
671
|
const avdExists = list.includes(`Name: ${this.avdName}`);
|
|
658
|
-
|
|
672
|
+
let avdNeedsRecreate = avdExists && (!state.avdSystemImage || state.avdSystemImage !== pkg);
|
|
673
|
+
const configPath = path.join(AVD_HOME, `${this.avdName}.avd`, 'config.ini');
|
|
674
|
+
if (avdExists && fs.existsSync(configPath)) {
|
|
675
|
+
try {
|
|
676
|
+
const config = fs.readFileSync(configPath, 'utf8');
|
|
677
|
+
const currentImageDir = readIniValue(config, 'image.sysdir.1');
|
|
678
|
+
const expectedImageDir = systemImagePackageToRelativeDir(pkg);
|
|
679
|
+
if (expectedImageDir && currentImageDir && currentImageDir !== expectedImageDir) {
|
|
680
|
+
avdNeedsRecreate = true;
|
|
681
|
+
}
|
|
682
|
+
} catch {}
|
|
683
|
+
}
|
|
659
684
|
|
|
660
685
|
if (avdNeedsRecreate) {
|
|
661
686
|
await this.stopEmulator().catch(() => {});
|