mindcraft 0.1.4-0 → 0.1.4-2
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 +36 -111
- package/bin/mindcraft.js +15 -10
- package/package.json +3 -3
- package/settings.js +10 -0
- package/src/agent/agent.js +8 -2
- package/src/agent/commands/actions.js +1 -1
- package/src/agent/mindserver_proxy.js +6 -0
- package/src/agent/vision/browser_viewer.js +29 -6
- package/src/mindcraft/mcserver.js +2 -2
- package/src/mindcraft/mindserver.js +46 -6
- package/src/mindcraft/providers.js +38 -0
- package/src/mindcraft/public/index.html +461 -324
- package/src/mindcraft/userconfig.js +19 -1
package/README.md
CHANGED
|
@@ -1,60 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
<h1 align="center">
|
|
3
|
-
<a href="https://trendshift.io/repositories/9163" target="_blank"><img src="https://trendshift.io/api/badge/repositories/9163" alt="kolbytn%2Fmindcraft | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
|
4
|
-
</h1>
|
|
5
|
-
|
|
6
|
-
<p align="center">Crafting minds for Minecraft with LLMs and <a href="https://prismarinejs.github.io/mineflayer/#/">Mineflayer!</a></p>
|
|
7
|
-
|
|
8
|
-
<p align="center">
|
|
9
|
-
<a href="https://github.com/mindcraft-bots/mindcraft/blob/main/FAQ.md">FAQ</a> |
|
|
10
|
-
<a href="https://discord.gg/mp73p35dzC">Discord Support</a> |
|
|
11
|
-
<a href="https://www.youtube.com/watch?v=gRotoL8P8D8">Video Tutorial</a> |
|
|
12
|
-
<a href="https://kolbynottingham.com/mindcraft/">Blog Post</a> |
|
|
13
|
-
<a href="https://mindcraft-minecollab.github.io/index.html">Paper Website</a> |
|
|
14
|
-
<a href="https://github.com/mindcraft-bots/mindcraft/blob/main/minecollab.md">MineCollab</a>
|
|
15
|
-
</p>
|
|
1
|
+
# mindcraft
|
|
16
2
|
|
|
17
|
-
|
|
18
|
-
Do not connect this bot to public servers with coding enabled. This project allows an LLM to write/execute code on your computer. The code is sandboxed, but still vulnerable to injection attacks. Code writing is disabled by default, you can enable it by setting `allow_insecure_coding` to `true` in `settings.js`. Ye be warned.
|
|
3
|
+
LLM agents that play Minecraft, built on [mineflayer](https://prismarinejs.github.io/mineflayer/#/).
|
|
19
4
|
|
|
20
|
-
|
|
21
|
-
|
|
5
|
+
> [!Note]
|
|
6
|
+
> This is an **unofficial fork** of [mindcraft-bots/mindcraft](https://github.com/mindcraft-bots/mindcraft) that packages it as an npm CLI with a browser-based setup. See [upstream issue #758](https://github.com/mindcraft-bots/mindcraft/issues/758) for context. All credit for the agent itself goes to the upstream authors.
|
|
22
7
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
- At least one API key from a supported API provider. See [supported APIs](#model-customization). OpenAI is the default.
|
|
8
|
+
> [!Caution]
|
|
9
|
+
> Don't connect to public servers with `allow_insecure_coding` enabled — it lets the LLM write and run code on your machine. It's off by default.
|
|
26
10
|
|
|
27
|
-
|
|
28
|
-
> If installing node on windows, ensure you check `Automatically install the necessary tools`
|
|
29
|
-
>
|
|
30
|
-
> If you encounter `npm install` errors on macOS, see the [FAQ](FAQ.md#common-issues) for troubleshooting native module build issues
|
|
11
|
+
## Quick start
|
|
31
12
|
|
|
32
|
-
|
|
13
|
+
```bash
|
|
14
|
+
npx mindcraft ui
|
|
15
|
+
```
|
|
33
16
|
|
|
34
|
-
|
|
17
|
+
This opens a web UI where you set your Minecraft server address, add an API key, and create an agent. Settings are saved to `~/.config/mindcraft/` so subsequent runs pick up where you left off.
|
|
35
18
|
|
|
36
|
-
|
|
19
|
+
You'll need:
|
|
20
|
+
- [Minecraft Java Edition](https://www.minecraft.net/en-us/store/minecraft-java-bedrock-edition-pc) (up to v1.21.11; v1.21.6 recommended), with a world open to LAN
|
|
21
|
+
- [Node.js](https://nodejs.org/) 18 or 20 (v24+ may have native-dep issues)
|
|
22
|
+
- An API key for one of the [supported providers](#supported-providers) (or a local Ollama model)
|
|
37
23
|
|
|
38
|
-
|
|
24
|
+
For programmatic use:
|
|
39
25
|
|
|
40
|
-
|
|
26
|
+
```js
|
|
27
|
+
import { init, createAgent } from 'mindcraft';
|
|
28
|
+
await init();
|
|
29
|
+
await createAgent({ host: 'localhost', port: 25565, profile: { name: 'andy', model: 'gpt-4o-mini' } });
|
|
30
|
+
```
|
|
41
31
|
|
|
42
|
-
|
|
32
|
+
## Running from a checkout
|
|
43
33
|
|
|
44
|
-
|
|
34
|
+
If you want to hack on it, clone and `npm install`, start a Minecraft world open to LAN, then `node main.js`. Put API keys in `keys.json` (copy `keys.example.json`) and pick a profile in `settings.js`. The [FAQ](FAQ.md) covers common install errors.
|
|
45
35
|
|
|
46
|
-
|
|
36
|
+
To run benchmark tasks: `node main.js --task_path tasks/basic/single_agent.json --task_id gather_oak_logs` — see [MineCollab](minecollab.md).
|
|
47
37
|
|
|
38
|
+
## Configuration
|
|
48
39
|
|
|
49
|
-
|
|
50
|
-
## Model Customization
|
|
40
|
+
The `npx mindcraft` UI writes config to `~/.config/mindcraft/config.json` and `keys.json`. Don't edit `settings.js` — it's defaults only. To override from the shell, use `SETTINGS_JSON='{"host":"..."}' node main.js` or `--profiles ./foo.json`.
|
|
51
41
|
|
|
52
|
-
|
|
42
|
+
Agent name, model and prompts live in a profile JSON (e.g. `andy.json`). The model can be a string like `"gpt-4o-mini"` or `"anthropic/claude-sonnet-4-6"`, or an object — see [Model Specifications](#model-specifications).
|
|
53
43
|
|
|
54
|
-
|
|
44
|
+
## Supported providers
|
|
55
45
|
|
|
56
46
|
<details>
|
|
57
|
-
<summary
|
|
47
|
+
<summary>All supported APIs</summary>
|
|
58
48
|
|
|
59
49
|
| API Name | Config Variable| Docs |
|
|
60
50
|
|------|------|------|
|
|
@@ -87,59 +77,11 @@ To install our models, install ollama and run the following terminal command:
|
|
|
87
77
|
ollama pull sweaterdog/andy-4:micro-q8_0 && ollama pull embeddinggemma
|
|
88
78
|
```
|
|
89
79
|
|
|
90
|
-
## Online
|
|
91
|
-
To connect to online servers your bot will need an official Microsoft/Minecraft account. You can use your own personal one, but will need another account if you want to connect too and play with it. To connect, change these lines in `settings.js`:
|
|
92
|
-
```javascript
|
|
93
|
-
"host": "111.222.333.444",
|
|
94
|
-
"port": 55920,
|
|
95
|
-
"auth": "microsoft",
|
|
96
|
-
|
|
97
|
-
// rest is same...
|
|
98
|
-
```
|
|
99
|
-
> [!Important]
|
|
100
|
-
> The bot's name in the profile.json must exactly match the Minecraft profile name! Otherwise the bot will spam talk to itself.
|
|
80
|
+
## Online servers
|
|
101
81
|
|
|
102
|
-
To
|
|
82
|
+
To connect to an online server the agent needs a real Microsoft/Minecraft account. Set `auth: "microsoft"` in the Server panel (or your config). The agent's profile name must exactly match the Minecraft account's username, or it will talk to itself. Mindcraft uses whichever account is active in the Minecraft launcher; switch accounts there, start the agent, then switch back.
|
|
103
83
|
|
|
104
|
-
##
|
|
105
|
-
|
|
106
|
-
Tasks automatically start the bot with a prompt and a goal item to acquire or blueprint to construct. To run a simple task that involves collecting 4 oak_logs run
|
|
107
|
-
|
|
108
|
-
`node main.js --task_path tasks/basic/single_agent.json --task_id gather_oak_logs`
|
|
109
|
-
|
|
110
|
-
Here is an example task json format:
|
|
111
|
-
|
|
112
|
-
```
|
|
113
|
-
{
|
|
114
|
-
"gather_oak_logs": {
|
|
115
|
-
"goal": "Collect at least four logs",
|
|
116
|
-
"initial_inventory": {
|
|
117
|
-
"0": {
|
|
118
|
-
"wooden_axe": 1
|
|
119
|
-
}
|
|
120
|
-
},
|
|
121
|
-
"agent_count": 1,
|
|
122
|
-
"target": "oak_log",
|
|
123
|
-
"number_of_target": 4,
|
|
124
|
-
"type": "techtree",
|
|
125
|
-
"max_depth": 1,
|
|
126
|
-
"depth": 0,
|
|
127
|
-
"timeout": 300,
|
|
128
|
-
"blocked_actions": {
|
|
129
|
-
"0": [],
|
|
130
|
-
"1": []
|
|
131
|
-
},
|
|
132
|
-
"missing_items": [],
|
|
133
|
-
"requires_ctable": false
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
The `initial_inventory` is what the bot will have at the start of the episode, `target` refers to the target item and `number_of_target` refers to the number of target items the agent needs to collect to successfully complete the task.
|
|
139
|
-
|
|
140
|
-
If you want more optimization and automatic launching of the minecraft world, you will need to follow the instructions in [Minecollab Instructions](minecollab.md#installation)
|
|
141
|
-
|
|
142
|
-
## Docker Container
|
|
84
|
+
## Docker
|
|
143
85
|
|
|
144
86
|
If you intend to `allow_insecure_coding`, it is a good idea to run the app in a docker container to reduce risks of running unknown code. This is strongly recommended before connecting to remote servers, although still does not guarantee complete safety.
|
|
145
87
|
|
|
@@ -151,21 +93,11 @@ or simply
|
|
|
151
93
|
docker-compose up --build
|
|
152
94
|
```
|
|
153
95
|
|
|
154
|
-
When running in docker,
|
|
155
|
-
|
|
156
|
-
```javascript
|
|
157
|
-
"host": "host.docker.internal", // instead of "localhost", to join your local minecraft from inside the docker container
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
To connect to an unsupported minecraft version, you can try to use [viaproxy](services/viaproxy/README.md)
|
|
161
|
-
|
|
162
|
-
# Bot Profiles
|
|
96
|
+
When running in docker, use `host.docker.internal` instead of `localhost` to reach a Minecraft server on your host machine. For unsupported Minecraft versions, try [viaproxy](services/viaproxy/README.md).
|
|
163
97
|
|
|
164
|
-
|
|
98
|
+
# Profiles
|
|
165
99
|
|
|
166
|
-
|
|
167
|
-
2. Prompts used to influence the bot's behavior.
|
|
168
|
-
3. Examples help the bot perform tasks.
|
|
100
|
+
A profile JSON (e.g. `andy.json`) defines the agent's name, which LLMs it uses for chat/coding/embedding, its prompts, and example conversations.
|
|
169
101
|
|
|
170
102
|
## Model Specifications
|
|
171
103
|
|
|
@@ -217,16 +149,9 @@ If you try to use an unsupported model, then it will default to a simple word-ov
|
|
|
217
149
|
|
|
218
150
|
Voice synthesis models are used to narrate bot responses and specified with `speak_model`. This field is parsed differently than other models and only supports strings formatted as `"{api}/{model}/{voice}"`, like `"openai/tts-1/echo"`. We only support `openai` and `google` for voice synthesis.
|
|
219
151
|
|
|
220
|
-
## Specifying Profiles via Command Line
|
|
221
|
-
|
|
222
|
-
By default, the program will use the profiles specified in `settings.js`. You can specify one or more agent profiles using the `--profiles` argument: `node main.js --profiles ./profiles/andy.json ./profiles/jill.json`
|
|
223
|
-
|
|
224
|
-
|
|
225
152
|
# Contributing
|
|
226
153
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
While AI generated code is allowed, please vet it carefully. Submitting tons of sloppy code and documentation actively harms development.
|
|
154
|
+
Please contribute upstream at [mindcraft-bots/mindcraft](https://github.com/mindcraft-bots/mindcraft) — this fork tracks it. Join the upstream [discord](https://discord.gg/mp73p35dzC) for support.
|
|
230
155
|
|
|
231
156
|
## Patches
|
|
232
157
|
|
package/bin/mindcraft.js
CHANGED
|
@@ -46,34 +46,39 @@ async function main() {
|
|
|
46
46
|
if (opts.cmd !== 'ui') return;
|
|
47
47
|
|
|
48
48
|
// Load persisted config from ~/.config/mindcraft so a restart picks up where the
|
|
49
|
-
//
|
|
50
|
-
// saved
|
|
49
|
+
// setup panels left off: keys → process.env, server + settings → spec defaults,
|
|
50
|
+
// saved agents → recreated. Merge order is settings_spec.json defaults
|
|
51
|
+
// ← config.settings ← config.server ← per-agent overrides.
|
|
51
52
|
userconfig.loadKeysIntoEnv();
|
|
52
53
|
const config = userconfig.getConfig();
|
|
53
54
|
overrideSpecDefaults({
|
|
54
55
|
data_dir: opts.dataDir,
|
|
56
|
+
...(config?.settings || {}),
|
|
55
57
|
...(config?.server || {}),
|
|
56
58
|
});
|
|
57
59
|
|
|
58
60
|
await init(false, opts.port, opts.open);
|
|
59
61
|
console.log(`\nMindcraft UI: http://localhost:${opts.port}`);
|
|
60
|
-
console.log(`
|
|
62
|
+
console.log(`Agent data dir: ${opts.dataDir}`);
|
|
61
63
|
|
|
62
|
-
|
|
64
|
+
const agents = config?.agents || config?.bots; // .bots = legacy key
|
|
65
|
+
if (agents?.length) {
|
|
63
66
|
const profiles = Object.fromEntries(userconfig.listProfiles().map(p => [p.name, p]));
|
|
64
|
-
for (const
|
|
65
|
-
const profile = profiles[
|
|
66
|
-
if (!profile) { console.warn(`Skipping
|
|
67
|
-
console.log(`Restoring
|
|
67
|
+
for (const a of agents) {
|
|
68
|
+
const profile = profiles[a.profile];
|
|
69
|
+
if (!profile) { console.warn(`Skipping agent "${a.profile}": profile not found`); continue; }
|
|
70
|
+
console.log(`Restoring agent: ${a.profile}`);
|
|
68
71
|
await createAgent({
|
|
72
|
+
...(config.settings || {}),
|
|
69
73
|
...(config.server || {}),
|
|
74
|
+
...(a.settings || {}),
|
|
70
75
|
data_dir: opts.dataDir,
|
|
71
|
-
base_profile:
|
|
76
|
+
base_profile: a.base_profile || 'assistant',
|
|
72
77
|
profile,
|
|
73
78
|
});
|
|
74
79
|
}
|
|
75
80
|
} else {
|
|
76
|
-
console.log(`No saved
|
|
81
|
+
console.log(`No saved agents — open the UI to run setup.\n`);
|
|
77
82
|
}
|
|
78
83
|
}
|
|
79
84
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mindcraft",
|
|
3
|
-
"version": "0.1.4-
|
|
3
|
+
"version": "0.1.4-2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"tag": "latest"
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
"node-canvas-webgl": "^0.3.0"
|
|
66
66
|
},
|
|
67
67
|
"overrides": {
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
"canvas": "^3.1.0",
|
|
69
|
+
"gl": "^8.1.6"
|
|
70
70
|
},
|
|
71
71
|
"scripts": {
|
|
72
72
|
"postinstall": "patch-package",
|
package/settings.js
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
// Default settings for the `node main.js` entrypoint.
|
|
2
|
+
//
|
|
3
|
+
// DON'T EDIT THIS FILE for local config — that just dirties your git checkout.
|
|
4
|
+
// Instead, override via:
|
|
5
|
+
// - `npx mindcraft ui` (writes ~/.config/mindcraft/config.json), or
|
|
6
|
+
// - the SETTINGS_JSON env var: SETTINGS_JSON='{"host":"..."}' node main.js, or
|
|
7
|
+
// - CLI flags: node main.js --profiles ./foo.json
|
|
8
|
+
//
|
|
9
|
+
// The web UI / `npx mindcraft` flow does not read this file at all; it uses
|
|
10
|
+
// settings_spec.json defaults overlaid with ~/.config/mindcraft/config.json.
|
|
1
11
|
const settings = {
|
|
2
12
|
"minecraft_version": "auto", // or specific version like "1.21.6"
|
|
3
13
|
"host": "127.0.0.1", // or "localhost", "your.ip.address.here"
|
package/src/agent/agent.js
CHANGED
|
@@ -11,7 +11,7 @@ import { SelfPrompter } from './self_prompter.js';
|
|
|
11
11
|
import convoManager from './conversation.js';
|
|
12
12
|
import { handleTranslation, handleEnglishTranslation } from '../utils/translator.js';
|
|
13
13
|
import { addBrowserViewer } from './vision/browser_viewer.js';
|
|
14
|
-
import { serverProxy, sendOutputToServer } from './mindserver_proxy.js';
|
|
14
|
+
import { serverProxy, sendOutputToServer, sendLogToServer } from './mindserver_proxy.js';
|
|
15
15
|
import settings from './settings.js';
|
|
16
16
|
import { Task } from './tasks/tasks.js';
|
|
17
17
|
import { speak } from './speak.js';
|
|
@@ -108,7 +108,11 @@ export class Agent {
|
|
|
108
108
|
this.bot.once('spawn', async () => {
|
|
109
109
|
try {
|
|
110
110
|
clearTimeout(spawnTimeout);
|
|
111
|
-
addBrowserViewer(this.bot, count_id);
|
|
111
|
+
const viewer = await addBrowserViewer(this.bot, count_id);
|
|
112
|
+
if (viewer.error) {
|
|
113
|
+
serverProxy.getSocket()?.emit('viewer-status', this.name, { error: viewer.error });
|
|
114
|
+
sendOutputToServer(this.name, `Viewer unavailable: ${viewer.error}`);
|
|
115
|
+
}
|
|
112
116
|
console.log('Initializing vision intepreter...');
|
|
113
117
|
// VisionInterpreter -> camera.js -> node-canvas-webgl, whose native
|
|
114
118
|
// deps (gl, canvas) are optionalDependencies and may not have built.
|
|
@@ -303,6 +307,7 @@ export class Agent {
|
|
|
303
307
|
// Now translate the message
|
|
304
308
|
message = await handleEnglishTranslation(message);
|
|
305
309
|
console.log('received message from', source, ':', message);
|
|
310
|
+
sendLogToServer(this.name, self_prompt ? 'thinking' : 'user', `${source}: ${message}`);
|
|
306
311
|
|
|
307
312
|
const checkInterrupt = () => this.self_prompter.shouldInterrupt(self_prompt) || this.shut_up || convoManager.responseScheduledFor(source);
|
|
308
313
|
|
|
@@ -328,6 +333,7 @@ export class Agent {
|
|
|
328
333
|
let res = await this.prompter.promptConvo(history);
|
|
329
334
|
|
|
330
335
|
console.log(`${this.name} full response to ${source}: ""${res}""`);
|
|
336
|
+
sendLogToServer(this.name, 'thinking', res);
|
|
331
337
|
|
|
332
338
|
if (res.trim().length === 0) {
|
|
333
339
|
console.warn('no response')
|
|
@@ -35,7 +35,7 @@ export const actionsList = [
|
|
|
35
35
|
perform: async function(agent, prompt) {
|
|
36
36
|
// just ignore prompt - it is now in context in chat history
|
|
37
37
|
if (!settings.allow_insecure_coding) {
|
|
38
|
-
agent.openChat('newAction is disabled. Enable with allow_insecure_coding=true in settings.
|
|
38
|
+
agent.openChat('newAction is disabled. Enable with allow_insecure_coding=true in agent settings.');
|
|
39
39
|
return "newAction not allowed! Code writing is disabled in settings. Notify the user.";
|
|
40
40
|
}
|
|
41
41
|
let result = "";
|
|
@@ -134,3 +134,9 @@ export function sendBotChatToServer(agentName, json) {
|
|
|
134
134
|
export function sendOutputToServer(agentName, message) {
|
|
135
135
|
serverProxy.getSocket().emit('bot-output', agentName, message);
|
|
136
136
|
}
|
|
137
|
+
|
|
138
|
+
// structured conversation log for the UI's per-agent log panel
|
|
139
|
+
// role: 'user' | 'bot' | 'thinking' | 'error'
|
|
140
|
+
export function sendLogToServer(agentName, role, text) {
|
|
141
|
+
serverProxy.getSocket()?.emit('agent-log', agentName, { role, text });
|
|
142
|
+
}
|
|
@@ -1,8 +1,31 @@
|
|
|
1
1
|
import settings from '../settings.js';
|
|
2
|
-
import
|
|
3
|
-
const mineflayerViewer = prismarineViewer.mineflayer;
|
|
2
|
+
import net from 'net';
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
// prismarine-viewer starts an express server on a fixed port and doesn't expose
|
|
5
|
+
// the http.Server, so an EADDRINUSE becomes an uncaught exception that kills
|
|
6
|
+
// the agent process. Probe the port first, and lazy-import so missing optional
|
|
7
|
+
// native deps also degrade to "no viewer" instead of crashing.
|
|
8
|
+
function portFree(port) {
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
const s = net.createServer()
|
|
11
|
+
.once('error', () => resolve(false))
|
|
12
|
+
.once('listening', () => s.close(() => resolve(true)))
|
|
13
|
+
.listen(port, '127.0.0.1');
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function addBrowserViewer(bot, count_id) {
|
|
18
|
+
if (!settings.render_bot_view) return { ok: false, disabled: true };
|
|
19
|
+
const port = 3000 + count_id;
|
|
20
|
+
try {
|
|
21
|
+
if (!(await portFree(port))) {
|
|
22
|
+
throw new Error(`port ${port} is already in use`);
|
|
23
|
+
}
|
|
24
|
+
const { default: prismarineViewer } = await import('prismarine-viewer');
|
|
25
|
+
prismarineViewer.mineflayer(bot, { port, firstPerson: true });
|
|
26
|
+
return { ok: true, port };
|
|
27
|
+
} catch (err) {
|
|
28
|
+
console.warn(`prismarine-viewer unavailable (port ${port}):`, err.message);
|
|
29
|
+
return { ok: false, port, error: err.message };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -131,7 +131,7 @@ export async function getServer(host, port, version) {
|
|
|
131
131
|
|
|
132
132
|
// Server not found
|
|
133
133
|
if (server == null)
|
|
134
|
-
throw new Error(`MC server not found. (Host: ${host}, Port: ${port}) Check the host and port in
|
|
134
|
+
throw new Error(`MC server not found. (Host: ${host}, Port: ${port}) Check the host and port in your server config, and ensure the server is running and open to public or LAN.`);
|
|
135
135
|
|
|
136
136
|
serverString = `(Host: ${server.host}, Port: ${server.port}, Version: ${server.version})`;
|
|
137
137
|
|
|
@@ -146,7 +146,7 @@ export async function getServer(host, port, version) {
|
|
|
146
146
|
if (!isSupported)
|
|
147
147
|
throw new Error(`MC server was found ${serverString}, but version is unsupported. Supported versions are: ${mc.supportedVersions.join(", ")}.`);
|
|
148
148
|
else if (version !== "auto" && server.version !== version)
|
|
149
|
-
throw new Error(`MC server was found ${serverString}, but version is incorrect. Expected ${version}, but found ${server.version}. Check the server version in
|
|
149
|
+
throw new Error(`MC server was found ${serverString}, but version is incorrect. Expected ${version}, but found ${server.version}. Check the server version in your config.`);
|
|
150
150
|
else
|
|
151
151
|
console.log(`MC server found. ${serverString}`);
|
|
152
152
|
|
|
@@ -5,6 +5,7 @@ import path from 'path';
|
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import * as mindcraft from './mindcraft.js';
|
|
7
7
|
import * as userconfig from './userconfig.js';
|
|
8
|
+
import { configuredProviders } from './providers.js';
|
|
8
9
|
import { readFileSync } from 'fs';
|
|
9
10
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
11
|
|
|
@@ -35,10 +36,16 @@ class AgentConnection {
|
|
|
35
36
|
this.in_game = false;
|
|
36
37
|
this.full_state = null;
|
|
37
38
|
this.viewer_port = viewer_port;
|
|
39
|
+
this.viewer_error = null;
|
|
40
|
+
this.log = [];
|
|
38
41
|
}
|
|
39
42
|
setSettings(settings) {
|
|
40
43
|
this.settings = settings;
|
|
41
44
|
}
|
|
45
|
+
appendLog(entry) {
|
|
46
|
+
this.log.push({ ...entry, ts: Date.now() });
|
|
47
|
+
if (this.log.length > 500) this.log.shift();
|
|
48
|
+
}
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
export function registerAgent(settings, viewer_port) {
|
|
@@ -218,21 +225,40 @@ export function createMindServer(host_public = false, port = 8080) {
|
|
|
218
225
|
});
|
|
219
226
|
|
|
220
227
|
socket.on('bot-output', (agentName, message) => {
|
|
228
|
+
agent_connections[agentName]?.appendLog({ role: 'bot', text: message });
|
|
221
229
|
io.emit('bot-output', agentName, message);
|
|
222
230
|
});
|
|
223
231
|
|
|
232
|
+
socket.on('agent-log', (agentName, entry) => {
|
|
233
|
+
agent_connections[agentName]?.appendLog(entry);
|
|
234
|
+
io.emit('agent-log', agentName, entry);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
socket.on('get-agent-log', (agentName, callback) => {
|
|
238
|
+
callback({ log: agent_connections[agentName]?.log || [] });
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
socket.on('viewer-status', (agentName, status) => {
|
|
242
|
+
if (agent_connections[agentName]) {
|
|
243
|
+
agent_connections[agentName].viewer_error = status.error || null;
|
|
244
|
+
agentsStatusUpdate();
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
224
248
|
socket.on('listen-to-agents', () => {
|
|
225
249
|
addListener(socket);
|
|
226
250
|
});
|
|
227
251
|
|
|
228
|
-
// ---- setup
|
|
252
|
+
// ---- setup panels / persistence ----
|
|
229
253
|
|
|
230
254
|
socket.on('get-config', (callback) => {
|
|
231
255
|
callback({
|
|
232
256
|
config: userconfig.getConfig(),
|
|
233
257
|
hasKeys: userconfig.hasKeys(),
|
|
258
|
+
providers: configuredProviders(userconfig.getKeys()),
|
|
234
259
|
profiles: userconfig.listProfiles(),
|
|
235
260
|
settingsSpec: settings_spec,
|
|
261
|
+
paths: { root: userconfig.paths.ROOT },
|
|
236
262
|
});
|
|
237
263
|
});
|
|
238
264
|
|
|
@@ -241,11 +267,12 @@ export function createMindServer(host_public = false, port = 8080) {
|
|
|
241
267
|
catch (e) { callback({ success: false, error: e.message }); }
|
|
242
268
|
});
|
|
243
269
|
|
|
244
|
-
socket.on('save-config', (
|
|
270
|
+
socket.on('save-config', (partial, callback) => {
|
|
245
271
|
try {
|
|
246
|
-
userconfig.
|
|
247
|
-
if (
|
|
248
|
-
|
|
272
|
+
const merged = userconfig.mergeConfig(partial);
|
|
273
|
+
if (partial.server) overrideSpecDefaults(partial.server);
|
|
274
|
+
if (partial.settings) overrideSpecDefaults(partial.settings);
|
|
275
|
+
callback({ success: true, config: merged });
|
|
249
276
|
} catch (e) { callback({ success: false, error: e.message }); }
|
|
250
277
|
});
|
|
251
278
|
|
|
@@ -254,6 +281,18 @@ export function createMindServer(host_public = false, port = 8080) {
|
|
|
254
281
|
catch (e) { callback({ success: false, error: e.message }); }
|
|
255
282
|
});
|
|
256
283
|
|
|
284
|
+
socket.on('delete-profile', (name, callback) => {
|
|
285
|
+
try {
|
|
286
|
+
userconfig.deleteProfile(name);
|
|
287
|
+
const cfg = userconfig.getConfig() || {};
|
|
288
|
+
if (cfg.agents) {
|
|
289
|
+
cfg.agents = cfg.agents.filter(a => a.profile !== name);
|
|
290
|
+
userconfig.saveConfig(cfg);
|
|
291
|
+
}
|
|
292
|
+
callback({ success: true });
|
|
293
|
+
} catch (e) { callback({ success: false, error: e.message }); }
|
|
294
|
+
});
|
|
295
|
+
|
|
257
296
|
socket.on('list-profiles', (callback) => {
|
|
258
297
|
callback({ profiles: userconfig.listProfiles() });
|
|
259
298
|
});
|
|
@@ -278,9 +317,10 @@ function agentsStatusUpdate(socket) {
|
|
|
278
317
|
for (let agentName in agent_connections) {
|
|
279
318
|
const conn = agent_connections[agentName];
|
|
280
319
|
agents.push({
|
|
281
|
-
name: agentName,
|
|
320
|
+
name: agentName,
|
|
282
321
|
in_game: conn.in_game,
|
|
283
322
|
viewerPort: conn.viewer_port,
|
|
323
|
+
viewerError: conn.viewer_error,
|
|
284
324
|
socket_connected: !!conn.socket
|
|
285
325
|
});
|
|
286
326
|
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Metadata for the API-providers setup panel: maps each provider (the `api`
|
|
2
|
+
// prefix used in profiles, see src/models/*.js) to its env-var key name and a
|
|
3
|
+
// short list of suggested models. Suggested models are just defaults for the
|
|
4
|
+
// dropdown — any model string the provider accepts will work.
|
|
5
|
+
//
|
|
6
|
+
// Order matters: the first entry is the wizard's default selection, matching
|
|
7
|
+
// upstream's default (OpenAI / andy.json uses gpt-4o-mini).
|
|
8
|
+
|
|
9
|
+
export const PROVIDERS = [
|
|
10
|
+
{ api: 'openai', label: 'OpenAI', key: 'OPENAI_API_KEY', models: ['gpt-5.4-mini', 'gpt-5.4', 'gpt-5.4-nano', 'o3'] },
|
|
11
|
+
{ api: 'anthropic', label: 'Anthropic', key: 'ANTHROPIC_API_KEY', models: ['claude-sonnet-4-6', 'claude-opus-4-6', 'claude-haiku-4-5'] },
|
|
12
|
+
{ api: 'google', label: 'Google Gemini', key: 'GEMINI_API_KEY', models: ['gemini-3.1-pro', 'gemini-2.5-pro', 'gemini-2.5-flash'] },
|
|
13
|
+
{ api: 'xai', label: 'xAI (Grok)', key: 'XAI_API_KEY', models: ['grok-4', 'grok-code-fast-1'] },
|
|
14
|
+
{ api: 'deepseek', label: 'DeepSeek', key: 'DEEPSEEK_API_KEY', models: ['deepseek-chat', 'deepseek-reasoner'] },
|
|
15
|
+
{ api: 'mistral', label: 'Mistral', key: 'MISTRAL_API_KEY', models: ['mistral-large-latest', 'mistral-small-latest'] },
|
|
16
|
+
{ api: 'groq', label: 'Groq', key: 'GROQCLOUD_API_KEY', models: ['groq/llama-3.3-70b-versatile', 'groq/openai/gpt-oss-120b'] },
|
|
17
|
+
{ api: 'qwen', label: 'Qwen', key: 'QWEN_API_KEY', models: ['qwen-max', 'qwen-plus'] },
|
|
18
|
+
{ api: 'replicate', label: 'Replicate', key: 'REPLICATE_API_KEY', models: ['replicate/meta/meta-llama-3-70b-instruct'] },
|
|
19
|
+
{ api: 'openrouter', label: 'OpenRouter', key: 'OPENROUTER_API_KEY', models: ['openrouter/anthropic/claude-sonnet-4-6', 'openrouter/openai/gpt-5.4'] },
|
|
20
|
+
{ api: 'huggingface', label: 'Hugging Face', key: 'HUGGINGFACE_API_KEY',models: ['huggingface/meta-llama/Llama-3.3-70B-Instruct'] },
|
|
21
|
+
{ api: 'novita', label: 'Novita', key: 'NOVITA_API_KEY', models: ['novita/meta-llama/llama-3.3-70b-instruct'] },
|
|
22
|
+
{ api: 'hyperbolic', label: 'Hyperbolic', key: 'HYPERBOLIC_API_KEY', models: ['hyperbolic/meta-llama/Llama-3.3-70B-Instruct'] },
|
|
23
|
+
{ api: 'cerebras', label: 'Cerebras', key: 'CEREBRAS_API_KEY', models: ['cerebras/llama-3.3-70b'] },
|
|
24
|
+
{ api: 'mercury', label: 'Mercury', key: 'MERCURY_API_KEY', models: ['mercury/mercury-coder-small'] },
|
|
25
|
+
{ api: 'glhf', label: 'glhf.chat', key: 'GHLF_API_KEY', models: ['glhf/hf:meta-llama/Llama-3.3-70B-Instruct'] },
|
|
26
|
+
{ api: 'azure', label: 'Azure OpenAI', key: 'OPENAI_API_KEY', models: ['azure/gpt-5.4'] },
|
|
27
|
+
{ api: 'ollama', label: 'Ollama (local)', key: null, models: ['ollama/sweaterdog/andy-4', 'ollama/llama3.3'] },
|
|
28
|
+
{ api: 'lmstudio', label: 'LM Studio (local)',key: null, models: ['lmstudio/default'] },
|
|
29
|
+
{ api: 'vllm', label: 'vLLM (local)', key: null, models: ['vllm/default'] },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
export function configuredProviders(keys) {
|
|
33
|
+
const have = new Set(Object.entries(keys).filter(([, v]) => v).map(([k]) => k));
|
|
34
|
+
return PROVIDERS.map(p => ({
|
|
35
|
+
...p,
|
|
36
|
+
configured: p.key === null || have.has(p.key) || !!process.env[p.key],
|
|
37
|
+
}));
|
|
38
|
+
}
|