@elizaos/plugin-minecraft 2.0.3-beta.6 → 2.0.3-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.
- package/dist/actions/helpers.d.ts +18 -0
- package/dist/actions/helpers.d.ts.map +1 -0
- package/dist/actions/index.d.ts +10 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/actions/mc.d.ts +25 -0
- package/dist/actions/mc.d.ts.map +1 -0
- package/dist/actions/utils.d.ts +7 -0
- package/dist/actions/utils.d.ts.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1362 -0
- package/dist/index.js.map +21 -0
- package/dist/protocol.d.ts +21 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/providers/index.d.ts +4 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/vision.d.ts +3 -0
- package/dist/providers/vision.d.ts.map +1 -0
- package/dist/providers/waypoints.d.ts +3 -0
- package/dist/providers/waypoints.d.ts.map +1 -0
- package/dist/providers/world-state.d.ts +3 -0
- package/dist/providers/world-state.d.ts.map +1 -0
- package/dist/services/minecraft-service.d.ts +32 -0
- package/dist/services/minecraft-service.d.ts.map +1 -0
- package/dist/services/process-manager.d.ts +13 -0
- package/dist/services/process-manager.d.ts.map +1 -0
- package/dist/services/waypoints-service.d.ts +29 -0
- package/dist/services/waypoints-service.d.ts.map +1 -0
- package/dist/services/websocket-client.d.ts +13 -0
- package/dist/services/websocket-client.d.ts.map +1 -0
- package/dist/types.d.ts +48 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +3 -3
package/dist/index.js
ADDED
|
@@ -0,0 +1,1362 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { logger as logger5 } from "@elizaos/core";
|
|
3
|
+
import { z as z4 } from "zod";
|
|
4
|
+
|
|
5
|
+
// src/services/minecraft-service.ts
|
|
6
|
+
import { logger as logger3, Service } from "@elizaos/core";
|
|
7
|
+
|
|
8
|
+
// src/types.ts
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
var minecraftWorldStateSchema = z.object({
|
|
11
|
+
connected: z.boolean(),
|
|
12
|
+
username: z.string().nullable().optional(),
|
|
13
|
+
version: z.string().nullable().optional(),
|
|
14
|
+
health: z.number().nullable().optional(),
|
|
15
|
+
food: z.number().nullable().optional(),
|
|
16
|
+
experience: z.number().nullable().optional(),
|
|
17
|
+
position: z.object({ x: z.number(), y: z.number(), z: z.number() }).nullable().optional(),
|
|
18
|
+
yaw: z.number().nullable().optional(),
|
|
19
|
+
pitch: z.number().nullable().optional(),
|
|
20
|
+
time: z.number().nullable().optional(),
|
|
21
|
+
isRaining: z.boolean().nullable().optional(),
|
|
22
|
+
inventory: z.array(z.object({
|
|
23
|
+
name: z.string(),
|
|
24
|
+
displayName: z.string(),
|
|
25
|
+
count: z.number(),
|
|
26
|
+
slot: z.number()
|
|
27
|
+
})).optional(),
|
|
28
|
+
nearbyEntities: z.array(z.object({
|
|
29
|
+
id: z.number(),
|
|
30
|
+
type: z.string(),
|
|
31
|
+
name: z.string().nullable(),
|
|
32
|
+
username: z.string().nullable(),
|
|
33
|
+
kind: z.string().nullable(),
|
|
34
|
+
position: z.object({ x: z.number(), y: z.number(), z: z.number() })
|
|
35
|
+
})).optional()
|
|
36
|
+
}).passthrough();
|
|
37
|
+
|
|
38
|
+
// src/services/process-manager.ts
|
|
39
|
+
import { spawn } from "node:child_process";
|
|
40
|
+
import { existsSync } from "node:fs";
|
|
41
|
+
import { createRequire } from "node:module";
|
|
42
|
+
import { dirname, join } from "node:path";
|
|
43
|
+
import { fileURLToPath } from "node:url";
|
|
44
|
+
import { logger } from "@elizaos/core";
|
|
45
|
+
|
|
46
|
+
class MinecraftProcessManager {
|
|
47
|
+
serverPort;
|
|
48
|
+
process = null;
|
|
49
|
+
isRunning = false;
|
|
50
|
+
entryPath = null;
|
|
51
|
+
constructor(serverPort) {
|
|
52
|
+
this.serverPort = serverPort;
|
|
53
|
+
this.entryPath = this.findEntry();
|
|
54
|
+
}
|
|
55
|
+
findEntry() {
|
|
56
|
+
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
57
|
+
const possible = [
|
|
58
|
+
join(moduleDir, "../../../mineflayer-server/dist/index.js"),
|
|
59
|
+
join(moduleDir, "../../../mineflayer-server/src/index.ts"),
|
|
60
|
+
join(moduleDir, "../../../../mineflayer-server/dist/index.js"),
|
|
61
|
+
join(moduleDir, "../../../../mineflayer-server/src/index.ts")
|
|
62
|
+
];
|
|
63
|
+
for (const p of possible) {
|
|
64
|
+
if (existsSync(p)) {
|
|
65
|
+
logger.info(`Found mineflayer-server entry at: ${p}`);
|
|
66
|
+
return p;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
logger.error("Could not find mineflayer-server entry file");
|
|
70
|
+
logger.error(`Searched paths: ${possible.join(", ")}`);
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
async start() {
|
|
74
|
+
if (this.isRunning)
|
|
75
|
+
return;
|
|
76
|
+
if (!this.entryPath) {
|
|
77
|
+
throw new Error("mineflayer-server entry not found (run: bun run build:server)");
|
|
78
|
+
}
|
|
79
|
+
const env = {
|
|
80
|
+
...process.env,
|
|
81
|
+
MC_SERVER_PORT: this.serverPort.toString(),
|
|
82
|
+
NODE_ENV: "development"
|
|
83
|
+
};
|
|
84
|
+
const entry = this.entryPath;
|
|
85
|
+
const isTypeScript = entry.endsWith(".ts");
|
|
86
|
+
return await new Promise((resolve, reject) => {
|
|
87
|
+
if (isTypeScript) {
|
|
88
|
+
const require2 = createRequire(import.meta.url);
|
|
89
|
+
const tsxPath = require2.resolve("tsx/cli", { paths: [process.cwd()] });
|
|
90
|
+
this.process = spawn("node", [tsxPath, entry], { env });
|
|
91
|
+
} else {
|
|
92
|
+
this.process = spawn("node", [entry], { env });
|
|
93
|
+
}
|
|
94
|
+
this.process.stdout?.on("data", (data) => {
|
|
95
|
+
const msg = data.toString().trim();
|
|
96
|
+
logger.debug(`[MinecraftServer] ${msg}`);
|
|
97
|
+
if (msg.includes("listening on port")) {
|
|
98
|
+
this.isRunning = true;
|
|
99
|
+
resolve();
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
this.process.stderr?.on("data", (data) => {
|
|
103
|
+
logger.error(`[MinecraftServer Error] ${data.toString()}`);
|
|
104
|
+
});
|
|
105
|
+
this.process.on("error", (err) => {
|
|
106
|
+
this.isRunning = false;
|
|
107
|
+
reject(err);
|
|
108
|
+
});
|
|
109
|
+
this.process.on("exit", (code) => {
|
|
110
|
+
logger.info(`Minecraft server process exited with code ${code ?? "unknown"}`);
|
|
111
|
+
this.isRunning = false;
|
|
112
|
+
});
|
|
113
|
+
setTimeout(() => {
|
|
114
|
+
if (!this.isRunning) {
|
|
115
|
+
reject(new Error("mineflayer-server failed to start (timeout)"));
|
|
116
|
+
}
|
|
117
|
+
}, 15000);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
async stop() {
|
|
121
|
+
if (!this.process || !this.isRunning)
|
|
122
|
+
return;
|
|
123
|
+
await new Promise((resolve) => {
|
|
124
|
+
this.process?.on("exit", () => resolve());
|
|
125
|
+
this.process?.kill("SIGTERM");
|
|
126
|
+
setTimeout(() => {
|
|
127
|
+
if (this.isRunning && this.process) {
|
|
128
|
+
this.process.kill("SIGKILL");
|
|
129
|
+
}
|
|
130
|
+
}, 5000);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
isServerRunning() {
|
|
134
|
+
return this.isRunning;
|
|
135
|
+
}
|
|
136
|
+
getServerUrl() {
|
|
137
|
+
return `ws://localhost:${this.serverPort}`;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/services/websocket-client.ts
|
|
142
|
+
import { logger as logger2 } from "@elizaos/core";
|
|
143
|
+
import WebSocket from "ws";
|
|
144
|
+
import { z as z2 } from "zod";
|
|
145
|
+
var jsonValueSchema = z2.lazy(() => z2.union([
|
|
146
|
+
z2.null(),
|
|
147
|
+
z2.boolean(),
|
|
148
|
+
z2.number(),
|
|
149
|
+
z2.string(),
|
|
150
|
+
z2.array(jsonValueSchema),
|
|
151
|
+
z2.record(z2.string(), jsonValueSchema)
|
|
152
|
+
]));
|
|
153
|
+
var responseSchema = z2.object({
|
|
154
|
+
type: z2.string(),
|
|
155
|
+
requestId: z2.string(),
|
|
156
|
+
success: z2.boolean(),
|
|
157
|
+
data: z2.record(z2.string(), jsonValueSchema).optional(),
|
|
158
|
+
error: z2.string().optional()
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
class MinecraftWebSocketClient {
|
|
162
|
+
serverUrl;
|
|
163
|
+
ws = null;
|
|
164
|
+
pending = new Map;
|
|
165
|
+
constructor(serverUrl) {
|
|
166
|
+
this.serverUrl = serverUrl;
|
|
167
|
+
}
|
|
168
|
+
async connect() {
|
|
169
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN)
|
|
170
|
+
return;
|
|
171
|
+
await new Promise((resolve, reject) => {
|
|
172
|
+
const ws = new WebSocket(this.serverUrl);
|
|
173
|
+
this.ws = ws;
|
|
174
|
+
ws.on("open", () => resolve());
|
|
175
|
+
ws.on("error", (err) => reject(err));
|
|
176
|
+
ws.on("message", (data) => this.onMessage(data.toString("utf8")));
|
|
177
|
+
ws.on("close", () => {
|
|
178
|
+
for (const [requestId, entry] of this.pending) {
|
|
179
|
+
clearTimeout(entry.timeoutId);
|
|
180
|
+
entry.reject(new Error(`WebSocket closed while waiting for ${requestId}`));
|
|
181
|
+
}
|
|
182
|
+
this.pending.clear();
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
logger2.info(`[Minecraft] Connected to ${this.serverUrl}`);
|
|
186
|
+
}
|
|
187
|
+
disconnect() {
|
|
188
|
+
this.ws?.close();
|
|
189
|
+
this.ws = null;
|
|
190
|
+
}
|
|
191
|
+
async health() {
|
|
192
|
+
const resp = await this.sendMessage("health", undefined, {});
|
|
193
|
+
return resp.success && resp.data?.status === "ok";
|
|
194
|
+
}
|
|
195
|
+
async sendMessage(type, botId, data, timeoutMs = 30000) {
|
|
196
|
+
const ws = this.ws;
|
|
197
|
+
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
198
|
+
throw new Error("Not connected to Mineflayer bridge server");
|
|
199
|
+
}
|
|
200
|
+
const requestId = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
201
|
+
const msg = {
|
|
202
|
+
type,
|
|
203
|
+
requestId,
|
|
204
|
+
...botId ? { botId } : {},
|
|
205
|
+
...Object.keys(data).length > 0 ? { data } : {}
|
|
206
|
+
};
|
|
207
|
+
const payload = JSON.stringify(msg);
|
|
208
|
+
const response = await new Promise((resolve, reject) => {
|
|
209
|
+
const timeoutId = setTimeout(() => {
|
|
210
|
+
this.pending.delete(requestId);
|
|
211
|
+
reject(new Error(`Request timeout: ${type}`));
|
|
212
|
+
}, timeoutMs);
|
|
213
|
+
this.pending.set(requestId, { resolve, reject, timeoutId });
|
|
214
|
+
ws.send(payload, (err) => {
|
|
215
|
+
if (err) {
|
|
216
|
+
clearTimeout(timeoutId);
|
|
217
|
+
this.pending.delete(requestId);
|
|
218
|
+
reject(err);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
if (!response.success) {
|
|
223
|
+
throw new Error(response.error ?? `Request failed: ${type}`);
|
|
224
|
+
}
|
|
225
|
+
return response;
|
|
226
|
+
}
|
|
227
|
+
onMessage(text) {
|
|
228
|
+
let parsed;
|
|
229
|
+
try {
|
|
230
|
+
const json = JSON.parse(text);
|
|
231
|
+
parsed = responseSchema.parse(json);
|
|
232
|
+
} catch (err) {
|
|
233
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
234
|
+
logger2.error(`[Minecraft] Failed to parse server message: ${msg}`);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const pending = this.pending.get(parsed.requestId);
|
|
238
|
+
if (!pending)
|
|
239
|
+
return;
|
|
240
|
+
clearTimeout(pending.timeoutId);
|
|
241
|
+
this.pending.delete(parsed.requestId);
|
|
242
|
+
pending.resolve(parsed);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// src/services/minecraft-service.ts
|
|
247
|
+
var MINECRAFT_SERVICE_TYPE = "minecraft";
|
|
248
|
+
|
|
249
|
+
class Session {
|
|
250
|
+
botId;
|
|
251
|
+
createdAt;
|
|
252
|
+
constructor(botId, createdAt = new Date) {
|
|
253
|
+
this.botId = botId;
|
|
254
|
+
this.createdAt = createdAt;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
class MinecraftService extends Service {
|
|
259
|
+
static serviceType = MINECRAFT_SERVICE_TYPE;
|
|
260
|
+
capabilityDescription = "Minecraft automation service (Mineflayer bridge)";
|
|
261
|
+
session = null;
|
|
262
|
+
processManager;
|
|
263
|
+
client;
|
|
264
|
+
isInitialized = false;
|
|
265
|
+
constructor(runtime) {
|
|
266
|
+
super(runtime);
|
|
267
|
+
if (!runtime)
|
|
268
|
+
throw new Error("MinecraftService requires a runtime");
|
|
269
|
+
this.runtime = runtime;
|
|
270
|
+
const portSetting = runtime.getSetting("MC_SERVER_PORT");
|
|
271
|
+
const port = typeof portSetting === "number" ? portSetting : Number(portSetting ?? 3457);
|
|
272
|
+
const serverPort = Number.isFinite(port) ? port : 3457;
|
|
273
|
+
this.processManager = new MinecraftProcessManager(serverPort);
|
|
274
|
+
this.client = new MinecraftWebSocketClient(this.processManager.getServerUrl());
|
|
275
|
+
}
|
|
276
|
+
static async start(runtime) {
|
|
277
|
+
const service = new MinecraftService(runtime);
|
|
278
|
+
try {
|
|
279
|
+
await service.processManager.start();
|
|
280
|
+
} catch (err) {
|
|
281
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
282
|
+
logger3.warn(`Failed to start Mineflayer server process: ${msg}`);
|
|
283
|
+
}
|
|
284
|
+
await service.initialize();
|
|
285
|
+
return service;
|
|
286
|
+
}
|
|
287
|
+
async initialize() {
|
|
288
|
+
if (this.isInitialized)
|
|
289
|
+
return;
|
|
290
|
+
await this.client.connect();
|
|
291
|
+
await this.waitForReady();
|
|
292
|
+
this.isInitialized = true;
|
|
293
|
+
}
|
|
294
|
+
async stop() {
|
|
295
|
+
if (this.session) {
|
|
296
|
+
try {
|
|
297
|
+
await this.destroyBot(this.session.botId);
|
|
298
|
+
} catch {}
|
|
299
|
+
}
|
|
300
|
+
this.client.disconnect();
|
|
301
|
+
await this.processManager.stop();
|
|
302
|
+
this.isInitialized = false;
|
|
303
|
+
}
|
|
304
|
+
getClient() {
|
|
305
|
+
if (!this.isInitialized) {
|
|
306
|
+
throw new Error("Minecraft service not initialized");
|
|
307
|
+
}
|
|
308
|
+
return this.client;
|
|
309
|
+
}
|
|
310
|
+
getCurrentSession() {
|
|
311
|
+
return this.session;
|
|
312
|
+
}
|
|
313
|
+
async createBot(overrides) {
|
|
314
|
+
if (!this.isInitialized) {
|
|
315
|
+
throw new Error("Minecraft service not initialized");
|
|
316
|
+
}
|
|
317
|
+
const resp = await this.client.sendMessage("createBot", undefined, overrides ?? {});
|
|
318
|
+
const botId = typeof resp.data?.botId === "string" ? resp.data.botId : null;
|
|
319
|
+
if (!botId) {
|
|
320
|
+
throw new Error("Bridge did not return botId");
|
|
321
|
+
}
|
|
322
|
+
this.session = new Session(botId);
|
|
323
|
+
return this.session;
|
|
324
|
+
}
|
|
325
|
+
async destroyBot(botId) {
|
|
326
|
+
if (!this.isInitialized)
|
|
327
|
+
return;
|
|
328
|
+
await this.client.sendMessage("destroyBot", botId, {});
|
|
329
|
+
if (this.session?.botId === botId) {
|
|
330
|
+
this.session = null;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
async ensureBot() {
|
|
334
|
+
if (this.session)
|
|
335
|
+
return this.session;
|
|
336
|
+
return await this.createBot();
|
|
337
|
+
}
|
|
338
|
+
async chat(message) {
|
|
339
|
+
const session = await this.ensureBot();
|
|
340
|
+
await this.client.sendMessage("chat", session.botId, { message });
|
|
341
|
+
}
|
|
342
|
+
async request(type, data) {
|
|
343
|
+
const session = await this.ensureBot();
|
|
344
|
+
const resp = await this.client.sendMessage(type, session.botId, data);
|
|
345
|
+
return resp.data ?? {};
|
|
346
|
+
}
|
|
347
|
+
async getWorldState() {
|
|
348
|
+
if (!this.session) {
|
|
349
|
+
return { connected: false };
|
|
350
|
+
}
|
|
351
|
+
const resp = await this.client.sendMessage("getState", this.session.botId, {});
|
|
352
|
+
return minecraftWorldStateSchema.parse(resp.data ?? { connected: false });
|
|
353
|
+
}
|
|
354
|
+
async waitForReady(maxAttempts = 20, delayMs = 500) {
|
|
355
|
+
for (let i = 0;i < maxAttempts; i++) {
|
|
356
|
+
try {
|
|
357
|
+
if (await this.client.health())
|
|
358
|
+
return;
|
|
359
|
+
} catch {}
|
|
360
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
361
|
+
}
|
|
362
|
+
throw new Error("Mineflayer bridge server did not become ready");
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// src/services/waypoints-service.ts
|
|
367
|
+
import { ChannelType, MemoryType, Service as Service2, stringToUuid } from "@elizaos/core";
|
|
368
|
+
var WAYPOINTS_SERVICE_TYPE = "minecraft_waypoints";
|
|
369
|
+
|
|
370
|
+
class WaypointsService extends Service2 {
|
|
371
|
+
static serviceType = WAYPOINTS_SERVICE_TYPE;
|
|
372
|
+
capabilityDescription = "Minecraft waypoint storage and navigation helpers";
|
|
373
|
+
waypoints = new Map;
|
|
374
|
+
waypointsRoomId;
|
|
375
|
+
waypointsWorldId;
|
|
376
|
+
constructor(runtime) {
|
|
377
|
+
super(runtime);
|
|
378
|
+
if (!runtime) {
|
|
379
|
+
throw new Error("WaypointsService requires a runtime");
|
|
380
|
+
}
|
|
381
|
+
this.runtime = runtime;
|
|
382
|
+
this.waypointsWorldId = stringToUuid("00000000-0000-0000-0000-00000000a001");
|
|
383
|
+
this.waypointsRoomId = stringToUuid(`minecraft-waypoints:${runtime.agentId}`);
|
|
384
|
+
}
|
|
385
|
+
static async start(runtime) {
|
|
386
|
+
const service = new WaypointsService(runtime);
|
|
387
|
+
await service.initialize();
|
|
388
|
+
return service;
|
|
389
|
+
}
|
|
390
|
+
async stop() {}
|
|
391
|
+
async initialize() {
|
|
392
|
+
if (this.runtime.ensureWorldExists) {
|
|
393
|
+
await this.runtime.ensureWorldExists({
|
|
394
|
+
id: this.waypointsWorldId,
|
|
395
|
+
name: "Minecraft Waypoints",
|
|
396
|
+
agentId: this.runtime.agentId,
|
|
397
|
+
messageServerId: stringToUuid("00000000-0000-0000-0000-000000000000"),
|
|
398
|
+
metadata: {
|
|
399
|
+
type: "minecraft",
|
|
400
|
+
description: "Persistent waypoint storage"
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
if (this.runtime.ensureRoomExists) {
|
|
405
|
+
await this.runtime.ensureRoomExists({
|
|
406
|
+
id: this.waypointsRoomId,
|
|
407
|
+
name: "Minecraft Waypoints",
|
|
408
|
+
worldId: this.waypointsWorldId,
|
|
409
|
+
source: "plugin-minecraft",
|
|
410
|
+
type: ChannelType.SELF,
|
|
411
|
+
metadata: {
|
|
412
|
+
type: "minecraft",
|
|
413
|
+
description: "Persistent waypoint storage"
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
if (this.runtime.ensureParticipantInRoom) {
|
|
418
|
+
await this.runtime.ensureParticipantInRoom(this.runtime.agentId, this.waypointsRoomId);
|
|
419
|
+
}
|
|
420
|
+
const memories = await this.runtime.getMemories({
|
|
421
|
+
roomId: this.waypointsRoomId,
|
|
422
|
+
count: 500,
|
|
423
|
+
tableName: "memories"
|
|
424
|
+
});
|
|
425
|
+
for (const m of memories) {
|
|
426
|
+
const md = m.metadata;
|
|
427
|
+
if (!md || md.type !== MemoryType.CUSTOM)
|
|
428
|
+
continue;
|
|
429
|
+
const cmd = md;
|
|
430
|
+
if (cmd.waypointType !== "minecraft_waypoint")
|
|
431
|
+
continue;
|
|
432
|
+
if (typeof cmd.waypointName !== "string")
|
|
433
|
+
continue;
|
|
434
|
+
if (typeof cmd.x !== "number" || typeof cmd.y !== "number" || typeof cmd.z !== "number")
|
|
435
|
+
continue;
|
|
436
|
+
const key = cmd.waypointName.trim().toLowerCase();
|
|
437
|
+
const createdAt = typeof m.createdAt === "number" ? new Date(m.createdAt) : new Date;
|
|
438
|
+
const id = m.id ?? stringToUuid(`mc-waypoint:${this.runtime.agentId}:${key}`);
|
|
439
|
+
this.waypoints.set(key, {
|
|
440
|
+
id,
|
|
441
|
+
name: cmd.waypointName,
|
|
442
|
+
x: cmd.x,
|
|
443
|
+
y: cmd.y,
|
|
444
|
+
z: cmd.z,
|
|
445
|
+
createdAt
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
waypointIdForKey(key) {
|
|
450
|
+
return stringToUuid(`mc-waypoint:${this.runtime.agentId}:${key}`);
|
|
451
|
+
}
|
|
452
|
+
buildWaypointMemory(_key, wp) {
|
|
453
|
+
const createdAt = Date.now();
|
|
454
|
+
const content = {
|
|
455
|
+
text: `Waypoint "${wp.name}" at (${wp.x}, ${wp.y}, ${wp.z})`,
|
|
456
|
+
source: "plugin-minecraft"
|
|
457
|
+
};
|
|
458
|
+
const metadata = {
|
|
459
|
+
type: MemoryType.CUSTOM,
|
|
460
|
+
scope: "private",
|
|
461
|
+
waypointType: "minecraft_waypoint",
|
|
462
|
+
waypointName: wp.name,
|
|
463
|
+
x: wp.x,
|
|
464
|
+
y: wp.y,
|
|
465
|
+
z: wp.z,
|
|
466
|
+
tags: ["minecraft", "waypoint"],
|
|
467
|
+
timestamp: createdAt
|
|
468
|
+
};
|
|
469
|
+
return {
|
|
470
|
+
id: wp.id,
|
|
471
|
+
entityId: this.runtime.agentId,
|
|
472
|
+
agentId: this.runtime.agentId,
|
|
473
|
+
roomId: this.waypointsRoomId,
|
|
474
|
+
worldId: this.waypointsWorldId,
|
|
475
|
+
createdAt,
|
|
476
|
+
content,
|
|
477
|
+
metadata,
|
|
478
|
+
unique: true
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
async setWaypoint(name, x, y, z3) {
|
|
482
|
+
const key = name.trim().toLowerCase();
|
|
483
|
+
const wp = {
|
|
484
|
+
id: this.waypointIdForKey(key),
|
|
485
|
+
name: name.trim(),
|
|
486
|
+
x,
|
|
487
|
+
y,
|
|
488
|
+
z: z3,
|
|
489
|
+
createdAt: new Date
|
|
490
|
+
};
|
|
491
|
+
this.waypoints.set(key, wp);
|
|
492
|
+
const memory = this.buildWaypointMemory(key, wp);
|
|
493
|
+
const existing = await this.runtime.getMemories({
|
|
494
|
+
roomId: this.waypointsRoomId,
|
|
495
|
+
count: 1,
|
|
496
|
+
tableName: "memories"
|
|
497
|
+
});
|
|
498
|
+
if (existing.some((m) => m.id === wp.id)) {
|
|
499
|
+
await this.runtime.updateMemory({
|
|
500
|
+
id: wp.id,
|
|
501
|
+
content: memory.content,
|
|
502
|
+
metadata: memory.metadata
|
|
503
|
+
});
|
|
504
|
+
} else {
|
|
505
|
+
await this.runtime.createMemory(memory, "memories", true);
|
|
506
|
+
}
|
|
507
|
+
return wp;
|
|
508
|
+
}
|
|
509
|
+
async deleteWaypoint(name) {
|
|
510
|
+
const key = name.trim().toLowerCase();
|
|
511
|
+
const wp = this.waypoints.get(key);
|
|
512
|
+
const deleted = this.waypoints.delete(key);
|
|
513
|
+
if (wp) {
|
|
514
|
+
await this.runtime.deleteMemory(wp.id);
|
|
515
|
+
}
|
|
516
|
+
return deleted;
|
|
517
|
+
}
|
|
518
|
+
getWaypoint(name) {
|
|
519
|
+
const key = name.trim().toLowerCase();
|
|
520
|
+
return this.waypoints.get(key) ?? null;
|
|
521
|
+
}
|
|
522
|
+
listWaypoints() {
|
|
523
|
+
return Array.from(this.waypoints.values()).sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// src/actions/utils.ts
|
|
528
|
+
import { z as z3 } from "zod";
|
|
529
|
+
var vec3Schema = z3.object({ x: z3.number(), y: z3.number(), z: z3.number() });
|
|
530
|
+
function extractVec3(text) {
|
|
531
|
+
const trimmed = text.trim();
|
|
532
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
|
533
|
+
try {
|
|
534
|
+
const parsed = JSON.parse(trimmed);
|
|
535
|
+
const v = vec3Schema.parse(parsed);
|
|
536
|
+
return v;
|
|
537
|
+
} catch {}
|
|
538
|
+
}
|
|
539
|
+
const m = trimmed.match(/(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)/);
|
|
540
|
+
if (!m)
|
|
541
|
+
return null;
|
|
542
|
+
const x = Number(m[1]);
|
|
543
|
+
const y = Number(m[2]);
|
|
544
|
+
const z4 = Number(m[3]);
|
|
545
|
+
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z4))
|
|
546
|
+
return null;
|
|
547
|
+
return { x, y, z: z4 };
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// src/actions/helpers.ts
|
|
551
|
+
var MINECRAFT_ACTION_TIMEOUT_MS = 15000;
|
|
552
|
+
var MAX_MINECRAFT_TEXT_LENGTH = 2000;
|
|
553
|
+
function isRecord(value) {
|
|
554
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
555
|
+
}
|
|
556
|
+
function parseJsonObject(text) {
|
|
557
|
+
const trimmed = text.trim();
|
|
558
|
+
if (!trimmed.startsWith("{") || !trimmed.endsWith("}"))
|
|
559
|
+
return {};
|
|
560
|
+
try {
|
|
561
|
+
const parsed = JSON.parse(trimmed);
|
|
562
|
+
return isRecord(parsed) ? parsed : {};
|
|
563
|
+
} catch {
|
|
564
|
+
return {};
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
function readParams(options) {
|
|
568
|
+
const maybe = isRecord(options) && isRecord(options.parameters) ? options.parameters : {};
|
|
569
|
+
return maybe;
|
|
570
|
+
}
|
|
571
|
+
function mergedInput(message, options) {
|
|
572
|
+
return {
|
|
573
|
+
...parseJsonObject(message.content.text ?? ""),
|
|
574
|
+
...readParams(options)
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
function readString(params, ...keys) {
|
|
578
|
+
for (const key of keys) {
|
|
579
|
+
const value = params[key];
|
|
580
|
+
if (typeof value === "string" && value.trim())
|
|
581
|
+
return value.trim();
|
|
582
|
+
}
|
|
583
|
+
return null;
|
|
584
|
+
}
|
|
585
|
+
function readNumber(params, ...keys) {
|
|
586
|
+
for (const key of keys) {
|
|
587
|
+
const value = params[key];
|
|
588
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
589
|
+
return value;
|
|
590
|
+
if (typeof value === "string" && value.trim()) {
|
|
591
|
+
const parsed = Number(value);
|
|
592
|
+
if (Number.isFinite(parsed))
|
|
593
|
+
return parsed;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
function readBoolean(params, ...keys) {
|
|
599
|
+
for (const key of keys) {
|
|
600
|
+
const value = params[key];
|
|
601
|
+
if (typeof value === "boolean")
|
|
602
|
+
return value;
|
|
603
|
+
if (typeof value === "string") {
|
|
604
|
+
const normalized = value.trim().toLowerCase();
|
|
605
|
+
if (normalized === "true")
|
|
606
|
+
return true;
|
|
607
|
+
if (normalized === "false")
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return null;
|
|
612
|
+
}
|
|
613
|
+
function parseVec3(params, text) {
|
|
614
|
+
const x = readNumber(params, "x");
|
|
615
|
+
const y = readNumber(params, "y");
|
|
616
|
+
const z4 = readNumber(params, "z");
|
|
617
|
+
if (x !== null && y !== null && z4 !== null)
|
|
618
|
+
return { x, y, z: z4 };
|
|
619
|
+
return extractVec3(text);
|
|
620
|
+
}
|
|
621
|
+
function isPlaceFace(value) {
|
|
622
|
+
return value === "up" || value === "down" || value === "north" || value === "south" || value === "east" || value === "west";
|
|
623
|
+
}
|
|
624
|
+
function callbackContent(actionName, text, source) {
|
|
625
|
+
return {
|
|
626
|
+
text,
|
|
627
|
+
actions: [actionName],
|
|
628
|
+
source: typeof source === "string" ? source : undefined
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
async function emit(actionName, callback, text, source, result) {
|
|
632
|
+
const content = callbackContent(actionName, text.slice(0, MAX_MINECRAFT_TEXT_LENGTH), source);
|
|
633
|
+
await callback?.(content, actionName);
|
|
634
|
+
return { text: content.text ?? text, ...result };
|
|
635
|
+
}
|
|
636
|
+
async function withMinecraftTimeout(promise, label) {
|
|
637
|
+
return Promise.race([
|
|
638
|
+
promise,
|
|
639
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(`${label} timed out`)), MINECRAFT_ACTION_TIMEOUT_MS))
|
|
640
|
+
]);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// src/actions/mc.ts
|
|
644
|
+
var ACTION_NAME = "MC";
|
|
645
|
+
var MC_OPS = [
|
|
646
|
+
"connect",
|
|
647
|
+
"disconnect",
|
|
648
|
+
"goto",
|
|
649
|
+
"stop",
|
|
650
|
+
"look",
|
|
651
|
+
"control",
|
|
652
|
+
"waypoint_goto",
|
|
653
|
+
"dig",
|
|
654
|
+
"place",
|
|
655
|
+
"chat",
|
|
656
|
+
"attack",
|
|
657
|
+
"waypoint_set",
|
|
658
|
+
"waypoint_delete"
|
|
659
|
+
];
|
|
660
|
+
function normalizeOp(value) {
|
|
661
|
+
if (typeof value !== "string")
|
|
662
|
+
return null;
|
|
663
|
+
const normalized = value.trim().replace(/[\s-]+/g, "_").toLowerCase();
|
|
664
|
+
switch (normalized) {
|
|
665
|
+
case "connect":
|
|
666
|
+
case "join":
|
|
667
|
+
case "mc_connect":
|
|
668
|
+
return "connect";
|
|
669
|
+
case "disconnect":
|
|
670
|
+
case "leave":
|
|
671
|
+
case "quit":
|
|
672
|
+
case "mc_disconnect":
|
|
673
|
+
return "disconnect";
|
|
674
|
+
case "goto":
|
|
675
|
+
case "go_to":
|
|
676
|
+
case "move":
|
|
677
|
+
case "walk":
|
|
678
|
+
case "pathfind":
|
|
679
|
+
return "goto";
|
|
680
|
+
case "stop":
|
|
681
|
+
case "cancel":
|
|
682
|
+
return "stop";
|
|
683
|
+
case "look":
|
|
684
|
+
case "view":
|
|
685
|
+
case "turn":
|
|
686
|
+
return "look";
|
|
687
|
+
case "control":
|
|
688
|
+
case "press":
|
|
689
|
+
case "key":
|
|
690
|
+
return "control";
|
|
691
|
+
case "waypoint_goto":
|
|
692
|
+
case "waypointgoto":
|
|
693
|
+
case "navigate":
|
|
694
|
+
return "waypoint_goto";
|
|
695
|
+
case "dig":
|
|
696
|
+
case "mine":
|
|
697
|
+
case "break":
|
|
698
|
+
return "dig";
|
|
699
|
+
case "place":
|
|
700
|
+
case "build":
|
|
701
|
+
return "place";
|
|
702
|
+
case "chat":
|
|
703
|
+
case "say":
|
|
704
|
+
case "tell":
|
|
705
|
+
case "message":
|
|
706
|
+
case "mc_chat":
|
|
707
|
+
return "chat";
|
|
708
|
+
case "attack":
|
|
709
|
+
case "hit":
|
|
710
|
+
case "mc_attack":
|
|
711
|
+
return "attack";
|
|
712
|
+
case "waypoint_set":
|
|
713
|
+
case "waypointset":
|
|
714
|
+
case "save_waypoint":
|
|
715
|
+
return "waypoint_set";
|
|
716
|
+
case "waypoint_delete":
|
|
717
|
+
case "waypointdelete":
|
|
718
|
+
case "delete_waypoint":
|
|
719
|
+
return "waypoint_delete";
|
|
720
|
+
default:
|
|
721
|
+
return MC_OPS.includes(normalized) ? normalized : null;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
function parseConnectOverrides(params) {
|
|
725
|
+
const out = {};
|
|
726
|
+
const host = readString(params, "host");
|
|
727
|
+
const port = readNumber(params, "port");
|
|
728
|
+
const username = readString(params, "username");
|
|
729
|
+
const auth = readString(params, "auth");
|
|
730
|
+
const version = readString(params, "version");
|
|
731
|
+
if (host)
|
|
732
|
+
out.host = host;
|
|
733
|
+
if (port !== null && Number.isInteger(port) && port > 0)
|
|
734
|
+
out.port = port;
|
|
735
|
+
if (username)
|
|
736
|
+
out.username = username;
|
|
737
|
+
if (auth === "offline" || auth === "microsoft")
|
|
738
|
+
out.auth = auth;
|
|
739
|
+
if (version)
|
|
740
|
+
out.version = version;
|
|
741
|
+
return out;
|
|
742
|
+
}
|
|
743
|
+
function parseControl(params, text) {
|
|
744
|
+
const control = readString(params, "control", "key", "direction");
|
|
745
|
+
const state = readBoolean(params, "state", "pressed", "enabled");
|
|
746
|
+
const durationMs = readNumber(params, "durationMs", "duration");
|
|
747
|
+
if (control && state !== null) {
|
|
748
|
+
return durationMs && durationMs > 0 ? { control, state, durationMs } : { control, state };
|
|
749
|
+
}
|
|
750
|
+
const match = text.trim().match(/^(\S+)\s+(true|false)(?:\s+(\d+))?$/i);
|
|
751
|
+
if (!match)
|
|
752
|
+
return null;
|
|
753
|
+
const parsedDuration = match[3] ? Number(match[3]) : undefined;
|
|
754
|
+
if (parsedDuration !== undefined && !Number.isFinite(parsedDuration))
|
|
755
|
+
return null;
|
|
756
|
+
return parsedDuration ? {
|
|
757
|
+
control: match[1],
|
|
758
|
+
state: match[2].toLowerCase() === "true",
|
|
759
|
+
durationMs: parsedDuration
|
|
760
|
+
} : { control: match[1], state: match[2].toLowerCase() === "true" };
|
|
761
|
+
}
|
|
762
|
+
function parseLook(params, text) {
|
|
763
|
+
const yaw = readNumber(params, "yaw");
|
|
764
|
+
const pitch = readNumber(params, "pitch");
|
|
765
|
+
if (yaw !== null && pitch !== null)
|
|
766
|
+
return { yaw, pitch };
|
|
767
|
+
const match = text.trim().match(/(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)/);
|
|
768
|
+
if (!match)
|
|
769
|
+
return null;
|
|
770
|
+
const parsedYaw = Number(match[1]);
|
|
771
|
+
const parsedPitch = Number(match[2]);
|
|
772
|
+
if (!Number.isFinite(parsedYaw) || !Number.isFinite(parsedPitch))
|
|
773
|
+
return null;
|
|
774
|
+
return { yaw: parsedYaw, pitch: parsedPitch };
|
|
775
|
+
}
|
|
776
|
+
function parseWaypointName(text, params) {
|
|
777
|
+
const explicit = readString(params, "name", "waypointName", "waypoint", "target");
|
|
778
|
+
if (explicit)
|
|
779
|
+
return explicit;
|
|
780
|
+
const stripped = text.trim().replace(/\b(?:minecraft|mc|waypoints?|set|save|create|delete|remove|goto|go to|navigate|to)\b/gi, " ").replace(/\s+/g, " ").trim();
|
|
781
|
+
return stripped || null;
|
|
782
|
+
}
|
|
783
|
+
function parsePlaceFace(params, text) {
|
|
784
|
+
const explicit = readString(params, "face");
|
|
785
|
+
if (isPlaceFace(explicit))
|
|
786
|
+
return explicit;
|
|
787
|
+
const match = text.trim().match(/\b(up|down|north|south|east|west)\b/i);
|
|
788
|
+
if (!match)
|
|
789
|
+
return null;
|
|
790
|
+
const candidate = match[1].toLowerCase();
|
|
791
|
+
return isPlaceFace(candidate) ? candidate : null;
|
|
792
|
+
}
|
|
793
|
+
function parseEntityId(params, text) {
|
|
794
|
+
const fromParams = readNumber(params, "entityId", "entity");
|
|
795
|
+
if (fromParams !== null)
|
|
796
|
+
return fromParams;
|
|
797
|
+
const match = text.trim().match(/\b(?:entity\s*)?(\d+)\b/i);
|
|
798
|
+
if (!match)
|
|
799
|
+
return null;
|
|
800
|
+
const entityId = Number(match[1]);
|
|
801
|
+
return Number.isFinite(entityId) ? entityId : null;
|
|
802
|
+
}
|
|
803
|
+
var MC_SIMILES = [
|
|
804
|
+
"MC_ATTACK",
|
|
805
|
+
"MC_HIT",
|
|
806
|
+
"MC_BLOCK",
|
|
807
|
+
"MC_DIG",
|
|
808
|
+
"MC_PLACE",
|
|
809
|
+
"MC_BUILD",
|
|
810
|
+
"MC_MINE",
|
|
811
|
+
"MC_CHAT",
|
|
812
|
+
"MC_SAY",
|
|
813
|
+
"MC_MESSAGE",
|
|
814
|
+
"MC_CONNECT",
|
|
815
|
+
"MC_JOIN",
|
|
816
|
+
"MINECRAFT_CONNECT",
|
|
817
|
+
"MC_DISCONNECT",
|
|
818
|
+
"MC_LEAVE",
|
|
819
|
+
"MC_QUIT",
|
|
820
|
+
"MC_LOCOMOTE",
|
|
821
|
+
"MC_MOVE",
|
|
822
|
+
"MC_GOTO",
|
|
823
|
+
"MC_STOP",
|
|
824
|
+
"MC_LOOK",
|
|
825
|
+
"MC_CONTROL",
|
|
826
|
+
"MC_WAYPOINT",
|
|
827
|
+
"MC_WAYPOINT_SET",
|
|
828
|
+
"MC_WAYPOINT_DELETE",
|
|
829
|
+
"MC_WAYPOINT_GOTO"
|
|
830
|
+
];
|
|
831
|
+
var minecraftAction = {
|
|
832
|
+
name: ACTION_NAME,
|
|
833
|
+
contexts: ["connectors", "automation", "media", "messaging", "memory"],
|
|
834
|
+
contextGate: {
|
|
835
|
+
anyOf: ["connectors", "automation", "media", "messaging", "memory"]
|
|
836
|
+
},
|
|
837
|
+
roleGate: { minRole: "USER" },
|
|
838
|
+
similes: MC_SIMILES,
|
|
839
|
+
description: "Drive a Minecraft bot. Choose one action: connect (host?,port?,username?,auth?,version?), disconnect, goto (x,y,z), stop, look (yaw,pitch), control (control,state,durationMs?), waypoint_goto (name), dig (x,y,z), place (x,y,z,face), chat (message), attack (entityId), waypoint_set (name), waypoint_delete (name).",
|
|
840
|
+
descriptionCompressed: "minecraft ops: connect|disconnect|goto|stop|look|control|waypoint_*|dig|place|chat|attack",
|
|
841
|
+
parameters: [
|
|
842
|
+
{
|
|
843
|
+
name: "action",
|
|
844
|
+
description: "Operation to run.",
|
|
845
|
+
descriptionCompressed: "Action.",
|
|
846
|
+
required: true,
|
|
847
|
+
schema: { type: "string", enum: MC_OPS }
|
|
848
|
+
},
|
|
849
|
+
{
|
|
850
|
+
name: "params",
|
|
851
|
+
description: "Optional JSON object containing the fields required by the chosen op.",
|
|
852
|
+
descriptionCompressed: "Op fields.",
|
|
853
|
+
required: false,
|
|
854
|
+
schema: { type: "object" }
|
|
855
|
+
}
|
|
856
|
+
],
|
|
857
|
+
validate: async (runtime, _message, _state) => {
|
|
858
|
+
return runtime.getService(MINECRAFT_SERVICE_TYPE) != null;
|
|
859
|
+
},
|
|
860
|
+
handler: async (runtime, message, _state, options, callback) => {
|
|
861
|
+
const service = runtime.getService(MINECRAFT_SERVICE_TYPE);
|
|
862
|
+
if (!service)
|
|
863
|
+
return { text: "Minecraft service is not available", success: false };
|
|
864
|
+
const params = mergedInput(message, options);
|
|
865
|
+
const text = message.content.text ?? "";
|
|
866
|
+
const op = normalizeOp(params.action ?? params.subaction ?? params.op ?? params.actionType ?? params.type);
|
|
867
|
+
if (!op) {
|
|
868
|
+
return emit(ACTION_NAME, callback, `MC requires action: one of ${MC_OPS.join("|")}.`, message.content.source, { success: false });
|
|
869
|
+
}
|
|
870
|
+
try {
|
|
871
|
+
switch (op) {
|
|
872
|
+
case "connect": {
|
|
873
|
+
const session = await withMinecraftTimeout(service.createBot(parseConnectOverrides(params)), "minecraft connect");
|
|
874
|
+
return await emit(ACTION_NAME, callback, `Connected Minecraft bot (botId=${session.botId}).`, message.content.source, {
|
|
875
|
+
success: true,
|
|
876
|
+
data: { botId: session.botId },
|
|
877
|
+
values: { connected: true }
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
case "disconnect": {
|
|
881
|
+
const session = service.getCurrentSession();
|
|
882
|
+
if (!session) {
|
|
883
|
+
return emit(ACTION_NAME, callback, "No Minecraft bot is connected.", message.content.source, { success: false });
|
|
884
|
+
}
|
|
885
|
+
await withMinecraftTimeout(service.destroyBot(session.botId), "minecraft disconnect");
|
|
886
|
+
return await emit(ACTION_NAME, callback, "Disconnected Minecraft bot.", message.content.source, { success: true, values: { connected: false } });
|
|
887
|
+
}
|
|
888
|
+
case "stop": {
|
|
889
|
+
await withMinecraftTimeout(service.request("stop", {}), "minecraft stop");
|
|
890
|
+
return await emit(ACTION_NAME, callback, "Stopped movement.", message.content.source, {
|
|
891
|
+
success: true
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
case "goto": {
|
|
895
|
+
const vec = parseVec3(params, text);
|
|
896
|
+
if (!vec) {
|
|
897
|
+
return emit(ACTION_NAME, callback, "Missing coordinates (x y z).", message.content.source, { success: false });
|
|
898
|
+
}
|
|
899
|
+
await withMinecraftTimeout(service.request("goto", { x: vec.x, y: vec.y, z: vec.z }), "minecraft goto");
|
|
900
|
+
return await emit(ACTION_NAME, callback, `Moving to (${vec.x}, ${vec.y}, ${vec.z}).`, message.content.source, { success: true });
|
|
901
|
+
}
|
|
902
|
+
case "look": {
|
|
903
|
+
const req = parseLook(params, text);
|
|
904
|
+
if (!req) {
|
|
905
|
+
return emit(ACTION_NAME, callback, "Missing yaw/pitch.", message.content.source, {
|
|
906
|
+
success: false
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
await withMinecraftTimeout(service.request("look", { yaw: req.yaw, pitch: req.pitch }), "minecraft look");
|
|
910
|
+
return await emit(ACTION_NAME, callback, "Adjusted view.", message.content.source, {
|
|
911
|
+
success: true
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
case "control": {
|
|
915
|
+
const req = parseControl(params, text);
|
|
916
|
+
if (!req) {
|
|
917
|
+
return emit(ACTION_NAME, callback, "Missing control command.", message.content.source, {
|
|
918
|
+
success: false
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
await withMinecraftTimeout(service.request("control", {
|
|
922
|
+
control: req.control,
|
|
923
|
+
state: req.state,
|
|
924
|
+
...typeof req.durationMs === "number" ? { durationMs: Math.min(req.durationMs, 1e4) } : {}
|
|
925
|
+
}), "minecraft control");
|
|
926
|
+
return await emit(ACTION_NAME, callback, `Set control ${req.control}=${String(req.state)}${req.durationMs ? ` for ${req.durationMs}ms` : ""}.`, message.content.source, { success: true });
|
|
927
|
+
}
|
|
928
|
+
case "waypoint_goto": {
|
|
929
|
+
const waypoints = runtime.getService(WAYPOINTS_SERVICE_TYPE);
|
|
930
|
+
if (!waypoints) {
|
|
931
|
+
return emit(ACTION_NAME, callback, "Waypoints service not available.", message.content.source, { success: false });
|
|
932
|
+
}
|
|
933
|
+
const name = parseWaypointName(text, params);
|
|
934
|
+
if (!name) {
|
|
935
|
+
return emit(ACTION_NAME, callback, "Missing waypoint name.", message.content.source, {
|
|
936
|
+
success: false
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
const wp = waypoints.getWaypoint(name);
|
|
940
|
+
if (!wp) {
|
|
941
|
+
return emit(ACTION_NAME, callback, `No waypoint named "${name}".`, message.content.source, { success: false });
|
|
942
|
+
}
|
|
943
|
+
await withMinecraftTimeout(service.request("goto", { x: wp.x, y: wp.y, z: wp.z }), "minecraft waypoint goto");
|
|
944
|
+
return await emit(ACTION_NAME, callback, `Navigating to waypoint "${wp.name}" at (${wp.x.toFixed(1)}, ${wp.y.toFixed(1)}, ${wp.z.toFixed(1)}).`, message.content.source, { success: true });
|
|
945
|
+
}
|
|
946
|
+
case "dig": {
|
|
947
|
+
const vec = parseVec3(params, text);
|
|
948
|
+
if (!vec) {
|
|
949
|
+
return emit(ACTION_NAME, callback, "Missing coordinates (x y z).", message.content.source, { success: false });
|
|
950
|
+
}
|
|
951
|
+
const data = await withMinecraftTimeout(service.request("dig", { x: vec.x, y: vec.y, z: vec.z }), "minecraft dig");
|
|
952
|
+
const blockName = typeof data.blockName === "string" ? data.blockName : "block";
|
|
953
|
+
return await emit(ACTION_NAME, callback, `Dug ${blockName} at (${vec.x}, ${vec.y}, ${vec.z}).`, message.content.source, { success: true, data });
|
|
954
|
+
}
|
|
955
|
+
case "place": {
|
|
956
|
+
const vec = parseVec3(params, text);
|
|
957
|
+
if (!vec) {
|
|
958
|
+
return emit(ACTION_NAME, callback, "Missing coordinates (x y z).", message.content.source, { success: false });
|
|
959
|
+
}
|
|
960
|
+
const face = parsePlaceFace(params, text);
|
|
961
|
+
if (!face) {
|
|
962
|
+
return emit(ACTION_NAME, callback, "Missing placement face (up/down/north/south/east/west).", message.content.source, { success: false });
|
|
963
|
+
}
|
|
964
|
+
await withMinecraftTimeout(service.request("place", { x: vec.x, y: vec.y, z: vec.z, face }), "minecraft place");
|
|
965
|
+
return await emit(ACTION_NAME, callback, `Placed block at (${vec.x}, ${vec.y}, ${vec.z}) face=${face}.`, message.content.source, { success: true });
|
|
966
|
+
}
|
|
967
|
+
case "chat": {
|
|
968
|
+
const msg = readString(params, "message", "text") ?? text.trim();
|
|
969
|
+
if (!msg) {
|
|
970
|
+
return emit(ACTION_NAME, callback, "No chat message provided.", message.content.source, { success: false });
|
|
971
|
+
}
|
|
972
|
+
const maxChatPreviewLength = 500;
|
|
973
|
+
await withMinecraftTimeout(service.chat(msg), "minecraft chat");
|
|
974
|
+
return await emit(ACTION_NAME, callback, `Sent Minecraft chat: ${msg.slice(0, maxChatPreviewLength)}`, message.content.source, { success: true, values: { sent: true } });
|
|
975
|
+
}
|
|
976
|
+
case "attack": {
|
|
977
|
+
const entityId = parseEntityId(params, text);
|
|
978
|
+
if (entityId === null) {
|
|
979
|
+
return emit(ACTION_NAME, callback, "Missing entityId.", message.content.source, {
|
|
980
|
+
success: false
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
await withMinecraftTimeout(service.request("attack", { entityId }), "minecraft attack");
|
|
984
|
+
return await emit(ACTION_NAME, callback, `Attacked entity ${entityId}.`, message.content.source, { success: true });
|
|
985
|
+
}
|
|
986
|
+
case "waypoint_set": {
|
|
987
|
+
const waypoints = runtime.getService(WAYPOINTS_SERVICE_TYPE);
|
|
988
|
+
if (!waypoints) {
|
|
989
|
+
return emit(ACTION_NAME, callback, "Waypoints service not available.", message.content.source, { success: false });
|
|
990
|
+
}
|
|
991
|
+
const name = parseWaypointName(text, params);
|
|
992
|
+
if (!name) {
|
|
993
|
+
return emit(ACTION_NAME, callback, "Missing waypoint name.", message.content.source, {
|
|
994
|
+
success: false
|
|
995
|
+
});
|
|
996
|
+
}
|
|
997
|
+
const worldState = await withMinecraftTimeout(service.getWorldState(), "minecraft world state");
|
|
998
|
+
const pos = worldState.position;
|
|
999
|
+
if (!pos) {
|
|
1000
|
+
return emit(ACTION_NAME, callback, "No position available (is the bot connected?).", message.content.source, { success: false });
|
|
1001
|
+
}
|
|
1002
|
+
const wp = await waypoints.setWaypoint(name, pos.x, pos.y, pos.z);
|
|
1003
|
+
return await emit(ACTION_NAME, callback, `Saved waypoint "${wp.name}" at (${wp.x.toFixed(1)}, ${wp.y.toFixed(1)}, ${wp.z.toFixed(1)}).`, message.content.source, {
|
|
1004
|
+
success: true,
|
|
1005
|
+
data: {
|
|
1006
|
+
name: wp.name,
|
|
1007
|
+
x: wp.x,
|
|
1008
|
+
y: wp.y,
|
|
1009
|
+
z: wp.z,
|
|
1010
|
+
createdAt: wp.createdAt.toISOString()
|
|
1011
|
+
}
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
case "waypoint_delete": {
|
|
1015
|
+
const waypoints = runtime.getService(WAYPOINTS_SERVICE_TYPE);
|
|
1016
|
+
if (!waypoints) {
|
|
1017
|
+
return emit(ACTION_NAME, callback, "Waypoints service not available.", message.content.source, { success: false });
|
|
1018
|
+
}
|
|
1019
|
+
const name = parseWaypointName(text, params);
|
|
1020
|
+
if (!name) {
|
|
1021
|
+
return emit(ACTION_NAME, callback, "Missing waypoint name.", message.content.source, {
|
|
1022
|
+
success: false
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
const deleted = await waypoints.deleteWaypoint(name);
|
|
1026
|
+
return await emit(ACTION_NAME, callback, deleted ? `Deleted waypoint "${name}".` : `No waypoint named "${name}".`, message.content.source, { success: deleted, values: { deleted } });
|
|
1027
|
+
}
|
|
1028
|
+
default: {
|
|
1029
|
+
const _exhaustive = op;
|
|
1030
|
+
return emit(ACTION_NAME, callback, "Unknown MC op.", message.content.source, {
|
|
1031
|
+
success: false
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
} catch (err) {
|
|
1036
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1037
|
+
return emit(ACTION_NAME, callback, `MC ${op} failed: ${msg}`, message.content.source, {
|
|
1038
|
+
success: false,
|
|
1039
|
+
data: { error: msg }
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
},
|
|
1043
|
+
examples: []
|
|
1044
|
+
};
|
|
1045
|
+
// src/providers/vision.ts
|
|
1046
|
+
var MAX_NEARBY_ENTITIES_IN_STATE = 24;
|
|
1047
|
+
var MAX_NEARBY_BLOCKS_IN_STATE = 24;
|
|
1048
|
+
var minecraftVisionProvider = {
|
|
1049
|
+
name: "MC_VISION",
|
|
1050
|
+
description: "Semantic environment context: biome, what I'm looking at, key nearby blocks (logs/ores), nearby entities",
|
|
1051
|
+
descriptionCompressed: "Read live Minecraft biome, looked-at block, nearby blocks, and nearby entities.",
|
|
1052
|
+
dynamic: true,
|
|
1053
|
+
contexts: ["automation", "agent_internal"],
|
|
1054
|
+
contextGate: { anyOf: ["automation", "agent_internal"] },
|
|
1055
|
+
cacheStable: false,
|
|
1056
|
+
cacheScope: "turn",
|
|
1057
|
+
get: async (runtime, _message, _state) => {
|
|
1058
|
+
const mc = runtime.getService(MINECRAFT_SERVICE_TYPE);
|
|
1059
|
+
if (!mc) {
|
|
1060
|
+
return {
|
|
1061
|
+
text: "Minecraft service not available",
|
|
1062
|
+
values: { connected: false },
|
|
1063
|
+
data: {}
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
try {
|
|
1067
|
+
const ws = await mc.getWorldState();
|
|
1068
|
+
if (!ws.connected) {
|
|
1069
|
+
return {
|
|
1070
|
+
text: "Minecraft bot not connected",
|
|
1071
|
+
values: { connected: false },
|
|
1072
|
+
data: {}
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
const scan = await mc.request("scan", {
|
|
1076
|
+
blocks: [
|
|
1077
|
+
"oak_log",
|
|
1078
|
+
"spruce_log",
|
|
1079
|
+
"birch_log",
|
|
1080
|
+
"jungle_log",
|
|
1081
|
+
"acacia_log",
|
|
1082
|
+
"dark_oak_log",
|
|
1083
|
+
"stone",
|
|
1084
|
+
"coal_ore",
|
|
1085
|
+
"iron_ore"
|
|
1086
|
+
],
|
|
1087
|
+
radius: 16,
|
|
1088
|
+
maxResults: MAX_NEARBY_BLOCKS_IN_STATE
|
|
1089
|
+
});
|
|
1090
|
+
const blocks = Array.isArray(scan.blocks) ? scan.blocks.slice(0, MAX_NEARBY_BLOCKS_IN_STATE) : [];
|
|
1091
|
+
const nearbyEntities = Array.isArray(ws.nearbyEntities) ? ws.nearbyEntities.slice(0, MAX_NEARBY_ENTITIES_IN_STATE) : [];
|
|
1092
|
+
const pos = ws.position ? `(${ws.position.x.toFixed(1)}, ${ws.position.y.toFixed(1)}, ${ws.position.z.toFixed(1)})` : "(unknown)";
|
|
1093
|
+
const biomeName = ws.biome && typeof ws.biome === "object" && "name" in ws.biome && typeof ws.biome.name === "string" ? ws.biome.name : null;
|
|
1094
|
+
const lookingAt = ws.lookingAt;
|
|
1095
|
+
const laName = lookingAt && typeof lookingAt.name === "string" ? lookingAt.name : null;
|
|
1096
|
+
const laPos = lookingAt?.position ? {
|
|
1097
|
+
x: typeof lookingAt.position.x === "number" ? lookingAt.position.x : null,
|
|
1098
|
+
y: typeof lookingAt.position.y === "number" ? lookingAt.position.y : null,
|
|
1099
|
+
z: typeof lookingAt.position.z === "number" ? lookingAt.position.z : null
|
|
1100
|
+
} : null;
|
|
1101
|
+
const lookingText = laName && laPos && laPos.x !== null && laPos.y !== null && laPos.z !== null ? `Looking at: ${laName} at (${laPos.x}, ${laPos.y}, ${laPos.z})` : "Looking at: (unknown)";
|
|
1102
|
+
const entityCount = Array.isArray(ws.nearbyEntities) ? ws.nearbyEntities.length : 0;
|
|
1103
|
+
return {
|
|
1104
|
+
text: `Biome: ${biomeName ?? "unknown"}
|
|
1105
|
+
Position: ${pos}
|
|
1106
|
+
${lookingText}
|
|
1107
|
+
NearbyEntities: ${entityCount}
|
|
1108
|
+
NearbyBlocksFound: ${blocks.length}`,
|
|
1109
|
+
values: {
|
|
1110
|
+
connected: true,
|
|
1111
|
+
biome: biomeName ?? null,
|
|
1112
|
+
entityCount,
|
|
1113
|
+
shownEntityCount: nearbyEntities.length,
|
|
1114
|
+
blocksFound: blocks.length
|
|
1115
|
+
},
|
|
1116
|
+
data: {
|
|
1117
|
+
biome: ws.biome ?? null,
|
|
1118
|
+
position: ws.position ?? null,
|
|
1119
|
+
lookingAt: ws.lookingAt ?? null,
|
|
1120
|
+
nearbyEntities,
|
|
1121
|
+
nearbyBlocks: blocks
|
|
1122
|
+
}
|
|
1123
|
+
};
|
|
1124
|
+
} catch (error) {
|
|
1125
|
+
return {
|
|
1126
|
+
text: "Unable to load Minecraft vision context",
|
|
1127
|
+
values: { connected: false, error: true },
|
|
1128
|
+
data: { error: error instanceof Error ? error.message : String(error) }
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
};
|
|
1133
|
+
// src/providers/waypoints.ts
|
|
1134
|
+
var MAX_WAYPOINTS_IN_STATE = 50;
|
|
1135
|
+
var minecraftWaypointsProvider = {
|
|
1136
|
+
name: "MC_WAYPOINTS",
|
|
1137
|
+
description: "Saved Minecraft waypoints (names and coordinates)",
|
|
1138
|
+
descriptionCompressed: "List saved Minecraft waypoint names and coordinates.",
|
|
1139
|
+
dynamic: true,
|
|
1140
|
+
contexts: ["automation", "agent_internal"],
|
|
1141
|
+
contextGate: { anyOf: ["automation", "agent_internal"] },
|
|
1142
|
+
cacheStable: false,
|
|
1143
|
+
cacheScope: "turn",
|
|
1144
|
+
get: async (runtime, _message, _state) => {
|
|
1145
|
+
const service = runtime.getService(WAYPOINTS_SERVICE_TYPE);
|
|
1146
|
+
if (!service) {
|
|
1147
|
+
return {
|
|
1148
|
+
text: "Waypoints service not available",
|
|
1149
|
+
values: { count: 0 },
|
|
1150
|
+
data: { waypoints: [] }
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
try {
|
|
1154
|
+
const list = service.listWaypoints();
|
|
1155
|
+
const shown = list.slice(0, MAX_WAYPOINTS_IN_STATE);
|
|
1156
|
+
const truncated = list.length > shown.length;
|
|
1157
|
+
const lines = shown.map((w) => `- ${w.name}: (${w.x.toFixed(1)}, ${w.y.toFixed(1)}, ${w.z.toFixed(1)})`);
|
|
1158
|
+
return {
|
|
1159
|
+
text: list.length ? `Waypoints (${shown.length}/${list.length}${truncated ? ", truncated" : ""}):
|
|
1160
|
+
${lines.join(`
|
|
1161
|
+
`)}` : "No waypoints saved.",
|
|
1162
|
+
values: { count: list.length, shown: shown.length, truncated },
|
|
1163
|
+
data: {
|
|
1164
|
+
waypoints: shown.map((w) => ({
|
|
1165
|
+
name: w.name,
|
|
1166
|
+
x: w.x,
|
|
1167
|
+
y: w.y,
|
|
1168
|
+
z: w.z,
|
|
1169
|
+
createdAt: w.createdAt.toISOString()
|
|
1170
|
+
}))
|
|
1171
|
+
}
|
|
1172
|
+
};
|
|
1173
|
+
} catch (error) {
|
|
1174
|
+
return {
|
|
1175
|
+
text: "Unable to load Minecraft waypoints",
|
|
1176
|
+
values: { count: 0, error: true },
|
|
1177
|
+
data: { waypoints: [], error: error instanceof Error ? error.message : String(error) }
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
};
|
|
1182
|
+
// src/providers/world-state.ts
|
|
1183
|
+
import {
|
|
1184
|
+
logger as logger4
|
|
1185
|
+
} from "@elizaos/core";
|
|
1186
|
+
var MAX_INVENTORY_ROWS_IN_STATE = 36;
|
|
1187
|
+
var MAX_ENTITY_ROWS_IN_STATE = 24;
|
|
1188
|
+
function isRecord2(value) {
|
|
1189
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1190
|
+
}
|
|
1191
|
+
function pickInventoryRows(inventory) {
|
|
1192
|
+
if (!Array.isArray(inventory))
|
|
1193
|
+
return [];
|
|
1194
|
+
const rows = [];
|
|
1195
|
+
for (const item of inventory) {
|
|
1196
|
+
if (!isRecord2(item))
|
|
1197
|
+
continue;
|
|
1198
|
+
const slot = typeof item.slot === "number" ? item.slot : null;
|
|
1199
|
+
const count = typeof item.count === "number" ? item.count : null;
|
|
1200
|
+
const name = typeof item.displayName === "string" ? item.displayName : typeof item.name === "string" ? item.name : null;
|
|
1201
|
+
if (slot === null || count === null || !name)
|
|
1202
|
+
continue;
|
|
1203
|
+
rows.push({ slot, name, count });
|
|
1204
|
+
}
|
|
1205
|
+
return rows;
|
|
1206
|
+
}
|
|
1207
|
+
function pickEntityRows(nearby) {
|
|
1208
|
+
if (!Array.isArray(nearby))
|
|
1209
|
+
return [];
|
|
1210
|
+
const rows = [];
|
|
1211
|
+
for (const ent of nearby) {
|
|
1212
|
+
if (!isRecord2(ent))
|
|
1213
|
+
continue;
|
|
1214
|
+
const id = typeof ent.id === "number" ? ent.id : null;
|
|
1215
|
+
const type = typeof ent.type === "string" ? ent.type : null;
|
|
1216
|
+
const username = typeof ent.username === "string" ? ent.username : null;
|
|
1217
|
+
const entName = typeof ent.name === "string" ? ent.name : null;
|
|
1218
|
+
const name = username ?? entName ?? type ?? "unknown";
|
|
1219
|
+
const pos = isRecord2(ent.position) ? ent.position : null;
|
|
1220
|
+
const x = pos && typeof pos.x === "number" ? pos.x : null;
|
|
1221
|
+
const y = pos && typeof pos.y === "number" ? pos.y : null;
|
|
1222
|
+
const z4 = pos && typeof pos.z === "number" ? pos.z : null;
|
|
1223
|
+
if (id === null || !type || x === null || y === null || z4 === null)
|
|
1224
|
+
continue;
|
|
1225
|
+
rows.push({
|
|
1226
|
+
id,
|
|
1227
|
+
type,
|
|
1228
|
+
name,
|
|
1229
|
+
x: Math.round(x * 10) / 10,
|
|
1230
|
+
y: Math.round(y * 10) / 10,
|
|
1231
|
+
z: Math.round(z4 * 10) / 10
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
return rows;
|
|
1235
|
+
}
|
|
1236
|
+
var minecraftWorldStateProvider = {
|
|
1237
|
+
name: "MC_WORLD_STATE",
|
|
1238
|
+
description: "Minecraft world state: connection, position, health, inventory, nearby entities",
|
|
1239
|
+
descriptionCompressed: "Read live Minecraft connection, position, health, inventory, and nearby entities.",
|
|
1240
|
+
dynamic: true,
|
|
1241
|
+
contexts: ["automation", "agent_internal"],
|
|
1242
|
+
contextGate: { anyOf: ["automation", "agent_internal"] },
|
|
1243
|
+
cacheStable: false,
|
|
1244
|
+
cacheScope: "turn",
|
|
1245
|
+
get: async (runtime, _message, _state) => {
|
|
1246
|
+
const service = runtime.getService(MINECRAFT_SERVICE_TYPE);
|
|
1247
|
+
if (!service) {
|
|
1248
|
+
return {
|
|
1249
|
+
text: "Minecraft service is not available",
|
|
1250
|
+
values: { connected: false },
|
|
1251
|
+
data: {}
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
try {
|
|
1255
|
+
const state = await service.getWorldState();
|
|
1256
|
+
if (!state.connected) {
|
|
1257
|
+
return {
|
|
1258
|
+
text: "Minecraft bot is not connected. Use MC_CONNECT to join a server.",
|
|
1259
|
+
values: { connected: false },
|
|
1260
|
+
data: {}
|
|
1261
|
+
};
|
|
1262
|
+
}
|
|
1263
|
+
const pos = state.position ? `(${state.position.x.toFixed(1)}, ${state.position.y.toFixed(1)}, ${state.position.z.toFixed(1)})` : "(unknown)";
|
|
1264
|
+
const inventoryRows = pickInventoryRows(state.inventory).slice(0, MAX_INVENTORY_ROWS_IN_STATE);
|
|
1265
|
+
const entityRows = pickEntityRows(state.nearbyEntities).slice(0, MAX_ENTITY_ROWS_IN_STATE);
|
|
1266
|
+
const headerLines = [
|
|
1267
|
+
`Minecraft: hp=${state.health ?? "?"} food=${state.food ?? "?"} pos=${pos} invItems=${inventoryRows.length} nearbyEntities=${entityRows.length}`
|
|
1268
|
+
];
|
|
1269
|
+
if (inventoryRows.length > 0) {
|
|
1270
|
+
headerLines.push(JSON.stringify({ inventory: inventoryRows }));
|
|
1271
|
+
}
|
|
1272
|
+
if (entityRows.length > 0) {
|
|
1273
|
+
headerLines.push(JSON.stringify({ nearbyEntities: entityRows }));
|
|
1274
|
+
}
|
|
1275
|
+
return {
|
|
1276
|
+
text: headerLines.join(`
|
|
1277
|
+
`),
|
|
1278
|
+
values: {
|
|
1279
|
+
connected: true,
|
|
1280
|
+
health: state.health ?? null,
|
|
1281
|
+
food: state.food ?? null,
|
|
1282
|
+
x: state.position?.x ?? null,
|
|
1283
|
+
y: state.position?.y ?? null,
|
|
1284
|
+
z: state.position?.z ?? null,
|
|
1285
|
+
inventoryCount: inventoryRows.length,
|
|
1286
|
+
nearbyEntitiesCount: entityRows.length
|
|
1287
|
+
},
|
|
1288
|
+
data: {
|
|
1289
|
+
...state,
|
|
1290
|
+
inventory: inventoryRows,
|
|
1291
|
+
nearbyEntities: entityRows
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1294
|
+
} catch (err) {
|
|
1295
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1296
|
+
logger4.error(`[Minecraft] Error getting world state: ${msg}`);
|
|
1297
|
+
return {
|
|
1298
|
+
text: "Error getting Minecraft world state",
|
|
1299
|
+
values: { connected: false, error: true },
|
|
1300
|
+
data: {}
|
|
1301
|
+
};
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
};
|
|
1305
|
+
// src/index.ts
|
|
1306
|
+
var configSchema = z4.object({
|
|
1307
|
+
MC_SERVER_PORT: z4.string().optional().default("3457"),
|
|
1308
|
+
MC_HOST: z4.string().optional().default("127.0.0.1"),
|
|
1309
|
+
MC_PORT: z4.string().optional().default("25565"),
|
|
1310
|
+
MC_USERNAME: z4.string().optional(),
|
|
1311
|
+
MC_AUTH: z4.string().optional().default("offline"),
|
|
1312
|
+
MC_VERSION: z4.string().optional()
|
|
1313
|
+
});
|
|
1314
|
+
var minecraftStateProvider = minecraftWorldStateProvider;
|
|
1315
|
+
var minecraftPlugin = {
|
|
1316
|
+
name: "plugin-minecraft",
|
|
1317
|
+
description: "Minecraft automation plugin (Mineflayer bridge)",
|
|
1318
|
+
config: {
|
|
1319
|
+
MC_SERVER_PORT: process.env.MC_SERVER_PORT ?? "3457",
|
|
1320
|
+
MC_HOST: process.env.MC_HOST ?? "127.0.0.1",
|
|
1321
|
+
MC_PORT: process.env.MC_PORT ?? "25565",
|
|
1322
|
+
MC_USERNAME: process.env.MC_USERNAME ?? null,
|
|
1323
|
+
MC_AUTH: process.env.MC_AUTH ?? "offline",
|
|
1324
|
+
MC_VERSION: process.env.MC_VERSION ?? null
|
|
1325
|
+
},
|
|
1326
|
+
async init(config, _runtime) {
|
|
1327
|
+
logger5.info("Initializing Minecraft plugin");
|
|
1328
|
+
const validatedConfig = await configSchema.parseAsync(config);
|
|
1329
|
+
for (const [key, value] of Object.entries(validatedConfig)) {
|
|
1330
|
+
if (value !== undefined && value !== null) {
|
|
1331
|
+
process.env[key] = String(value);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
logger5.info("Minecraft plugin initialized");
|
|
1335
|
+
},
|
|
1336
|
+
services: [MinecraftService, WaypointsService],
|
|
1337
|
+
actions: [minecraftAction],
|
|
1338
|
+
providers: [minecraftStateProvider, minecraftWaypointsProvider, minecraftVisionProvider],
|
|
1339
|
+
async dispose(runtime) {
|
|
1340
|
+
await runtime.getService(WaypointsService.serviceType)?.stop();
|
|
1341
|
+
await runtime.getService(MinecraftService.serviceType)?.stop();
|
|
1342
|
+
}
|
|
1343
|
+
};
|
|
1344
|
+
var src_default = minecraftPlugin;
|
|
1345
|
+
export {
|
|
1346
|
+
minecraftWorldStateSchema,
|
|
1347
|
+
minecraftWorldStateProvider,
|
|
1348
|
+
minecraftWaypointsProvider,
|
|
1349
|
+
minecraftVisionProvider,
|
|
1350
|
+
minecraftPlugin,
|
|
1351
|
+
minecraftAction,
|
|
1352
|
+
src_default as default,
|
|
1353
|
+
WaypointsService,
|
|
1354
|
+
WAYPOINTS_SERVICE_TYPE,
|
|
1355
|
+
Session,
|
|
1356
|
+
MinecraftWebSocketClient,
|
|
1357
|
+
MinecraftService,
|
|
1358
|
+
MinecraftProcessManager,
|
|
1359
|
+
MINECRAFT_SERVICE_TYPE
|
|
1360
|
+
};
|
|
1361
|
+
|
|
1362
|
+
//# debugId=81388854C4EA756164756E2164756E21
|