@kzheart_/mc-pilot 0.7.0 → 0.8.0
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/commands/events.js +85 -14
- package/dist/commands/schema.d.ts +2 -0
- package/dist/commands/schema.js +10 -0
- package/dist/index.js +2 -0
- package/dist/schema.d.ts +63 -0
- package/dist/schema.js +77 -0
- package/package.json +1 -1
package/dist/commands/events.js
CHANGED
|
@@ -47,6 +47,66 @@ function readAllEvents(filePath) {
|
|
|
47
47
|
}
|
|
48
48
|
return out;
|
|
49
49
|
}
|
|
50
|
+
function filterEvents(events, options) {
|
|
51
|
+
let filtered = events;
|
|
52
|
+
if (options.sinceMs !== undefined) {
|
|
53
|
+
filtered = filtered.filter((event) => event.t >= options.sinceMs);
|
|
54
|
+
}
|
|
55
|
+
if (options.type) {
|
|
56
|
+
const wanted = new Set(options.type.split(",").map((s) => s.trim()).filter(Boolean));
|
|
57
|
+
filtered = filtered.filter((event) => wanted.has(event.type));
|
|
58
|
+
}
|
|
59
|
+
return filtered;
|
|
60
|
+
}
|
|
61
|
+
function buildPayloadText(event) {
|
|
62
|
+
return JSON.stringify({
|
|
63
|
+
type: event.type,
|
|
64
|
+
payload: event.payload ?? {}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function buildMatchPattern(raw) {
|
|
68
|
+
if (!raw)
|
|
69
|
+
return null;
|
|
70
|
+
try {
|
|
71
|
+
return new RegExp(raw);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
throw new MctError({ code: "INVALID_PARAMS", message: `Invalid --match pattern: ${raw}` }, 4);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function waitForEvent(filePath, options) {
|
|
78
|
+
const deadline = Date.now() + options.timeoutSeconds * 1000;
|
|
79
|
+
const matchPattern = buildMatchPattern(options.match);
|
|
80
|
+
const startedAt = Date.now();
|
|
81
|
+
while (Date.now() < deadline) {
|
|
82
|
+
const events = filterEvents(readAllEvents(filePath), {
|
|
83
|
+
sinceMs: options.sinceMs,
|
|
84
|
+
type: options.type
|
|
85
|
+
});
|
|
86
|
+
const matched = matchPattern
|
|
87
|
+
? events.find((event) => matchPattern.test(buildPayloadText(event)))
|
|
88
|
+
: events[0];
|
|
89
|
+
if (matched) {
|
|
90
|
+
return {
|
|
91
|
+
file: filePath,
|
|
92
|
+
matched: true,
|
|
93
|
+
waitedMs: Date.now() - startedAt,
|
|
94
|
+
event: matched
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
98
|
+
}
|
|
99
|
+
throw new MctError({
|
|
100
|
+
code: "TIMEOUT",
|
|
101
|
+
message: `Timed out after ${options.timeoutSeconds}s waiting for events in ${filePath}`,
|
|
102
|
+
details: {
|
|
103
|
+
file: filePath,
|
|
104
|
+
type: options.type ?? null,
|
|
105
|
+
match: options.match ?? null,
|
|
106
|
+
sinceMs: options.sinceMs
|
|
107
|
+
}
|
|
108
|
+
}, 2);
|
|
109
|
+
}
|
|
50
110
|
export function createEventsCommand() {
|
|
51
111
|
const command = new Command("events").description("Inspect the client event log (written by the mod to ~/.mct/logs/<client>/events.jsonl)");
|
|
52
112
|
command
|
|
@@ -61,15 +121,10 @@ export function createEventsCommand() {
|
|
|
61
121
|
const clientName = globalOptions.client ?? context.activeProfile?.clients[0];
|
|
62
122
|
const filePath = options.file ?? resolveEventsFile(clientName);
|
|
63
123
|
const events = readAllEvents(filePath);
|
|
64
|
-
let filtered = events
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
if (options.type) {
|
|
70
|
-
const wanted = new Set(options.type.split(",").map((s) => s.trim()).filter(Boolean));
|
|
71
|
-
filtered = filtered.filter((e) => wanted.has(e.type));
|
|
72
|
-
}
|
|
124
|
+
let filtered = filterEvents(events, {
|
|
125
|
+
sinceMs: parseSince(options.since, Date.now()),
|
|
126
|
+
type: options.type
|
|
127
|
+
});
|
|
73
128
|
if (!options.all) {
|
|
74
129
|
const tail = options.tail ?? 20;
|
|
75
130
|
if (filtered.length > tail) {
|
|
@@ -93,15 +148,31 @@ export function createEventsCommand() {
|
|
|
93
148
|
const tail = args[0] ? Number(args[0]) : 20;
|
|
94
149
|
const clientName = globalOptions.client ?? context.activeProfile?.clients[0];
|
|
95
150
|
const filePath = options.file ?? resolveEventsFile(clientName);
|
|
96
|
-
let events = readAllEvents(filePath)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
events = events.filter((e) => wanted.has(e.type));
|
|
100
|
-
}
|
|
151
|
+
let events = filterEvents(readAllEvents(filePath), {
|
|
152
|
+
type: options.type
|
|
153
|
+
});
|
|
101
154
|
if (events.length > tail)
|
|
102
155
|
events = events.slice(events.length - tail);
|
|
103
156
|
return { file: filePath, returned: events.length, events };
|
|
104
157
|
}));
|
|
158
|
+
command
|
|
159
|
+
.command("wait")
|
|
160
|
+
.description("Wait for a matching event. Defaults to events emitted after this command starts.")
|
|
161
|
+
.option("--timeout <seconds>", "Timeout in seconds (default 10)", Number)
|
|
162
|
+
.option("--since <duration>", "Also consider events since duration ago (e.g. 30s, 5m, 1h) or epoch ms")
|
|
163
|
+
.option("--type <types>", "Comma-separated list of event types to include")
|
|
164
|
+
.option("--match <pattern>", "Regex matched against event type and payload JSON")
|
|
165
|
+
.option("--file <path>", "Override the log file path")
|
|
166
|
+
.action(wrapCommand(async (context, { options, globalOptions }) => {
|
|
167
|
+
const clientName = globalOptions.client ?? context.activeProfile?.clients[0];
|
|
168
|
+
const filePath = options.file ?? resolveEventsFile(clientName);
|
|
169
|
+
return waitForEvent(filePath, {
|
|
170
|
+
timeoutSeconds: options.timeout ?? context.timeout("default"),
|
|
171
|
+
sinceMs: parseSince(options.since, Date.now()) ?? Date.now(),
|
|
172
|
+
type: options.type,
|
|
173
|
+
match: options.match
|
|
174
|
+
});
|
|
175
|
+
}));
|
|
105
176
|
command
|
|
106
177
|
.command("clear")
|
|
107
178
|
.description("Truncate the event log for the active client")
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { buildSchemaDocument } from "../schema.js";
|
|
3
|
+
import { wrapCommand } from "../util/command.js";
|
|
4
|
+
export function createSchemaCommand(getProgram) {
|
|
5
|
+
return new Command("schema")
|
|
6
|
+
.description("Output a machine-readable CLI and protocol schema")
|
|
7
|
+
.action(wrapCommand(async () => {
|
|
8
|
+
return buildSchemaDocument(getProgram());
|
|
9
|
+
}));
|
|
10
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,7 @@ import { createResourcepackCommand } from "./commands/resourcepack.js";
|
|
|
21
21
|
import { createRotationCommand } from "./commands/rotation.js";
|
|
22
22
|
import { createScreenCommand } from "./commands/screen.js";
|
|
23
23
|
import { createScreenshotCommand } from "./commands/screenshot.js";
|
|
24
|
+
import { createSchemaCommand } from "./commands/schema.js";
|
|
24
25
|
import { createServerCommand } from "./commands/server.js";
|
|
25
26
|
import { createSignCommand } from "./commands/sign.js";
|
|
26
27
|
import { createStatusCommand } from "./commands/status.js";
|
|
@@ -67,6 +68,7 @@ export function buildProgram() {
|
|
|
67
68
|
program.addCommand(createServerCommand());
|
|
68
69
|
program.addCommand(createClientCommand());
|
|
69
70
|
program.addCommand(createPluginCommand());
|
|
71
|
+
program.addCommand(createSchemaCommand(() => program));
|
|
70
72
|
// Game interaction commands
|
|
71
73
|
program.addCommand(createChatCommand());
|
|
72
74
|
program.addCommand(createInputCommand());
|
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Argument, Command, Option } from "commander";
|
|
2
|
+
interface ProtocolEntry {
|
|
3
|
+
name?: string;
|
|
4
|
+
code?: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
params?: string[];
|
|
7
|
+
exitCode?: number;
|
|
8
|
+
}
|
|
9
|
+
declare function serializeOption(option: Option): {
|
|
10
|
+
name: string;
|
|
11
|
+
flags: string;
|
|
12
|
+
description: string;
|
|
13
|
+
required: boolean;
|
|
14
|
+
mandatory: boolean;
|
|
15
|
+
defaultValue: any;
|
|
16
|
+
};
|
|
17
|
+
declare function serializeArgument(argument: Argument): {
|
|
18
|
+
name: string;
|
|
19
|
+
required: boolean;
|
|
20
|
+
variadic: boolean;
|
|
21
|
+
};
|
|
22
|
+
declare function serializeCommand(command: Command, parents?: string[]): {
|
|
23
|
+
name: string;
|
|
24
|
+
path: string;
|
|
25
|
+
description: string;
|
|
26
|
+
aliases: string[];
|
|
27
|
+
arguments: ReturnType<typeof serializeArgument>[];
|
|
28
|
+
options: ReturnType<typeof serializeOption>[];
|
|
29
|
+
subcommands: ReturnType<typeof serializeCommand>[];
|
|
30
|
+
leaf: boolean;
|
|
31
|
+
};
|
|
32
|
+
export declare function buildSchemaDocument(program: Command): {
|
|
33
|
+
schemaVersion: number;
|
|
34
|
+
cli: {
|
|
35
|
+
name: string;
|
|
36
|
+
description: string;
|
|
37
|
+
globalOptions: {
|
|
38
|
+
name: string;
|
|
39
|
+
flags: string;
|
|
40
|
+
description: string;
|
|
41
|
+
required: boolean;
|
|
42
|
+
mandatory: boolean;
|
|
43
|
+
defaultValue: any;
|
|
44
|
+
}[];
|
|
45
|
+
commands: {
|
|
46
|
+
name: string;
|
|
47
|
+
path: string;
|
|
48
|
+
description: string;
|
|
49
|
+
aliases: string[];
|
|
50
|
+
arguments: ReturnType<typeof serializeArgument>[];
|
|
51
|
+
options: ReturnType<typeof serializeOption>[];
|
|
52
|
+
subcommands: ReturnType<typeof serializeCommand>[];
|
|
53
|
+
leaf: boolean;
|
|
54
|
+
}[];
|
|
55
|
+
leafCommands: string[];
|
|
56
|
+
};
|
|
57
|
+
protocol: {
|
|
58
|
+
actions: number | ProtocolEntry[];
|
|
59
|
+
queries: number | ProtocolEntry[];
|
|
60
|
+
errors: number | ProtocolEntry[];
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
export {};
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
function resolveRepoRoot() {
|
|
5
|
+
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
6
|
+
}
|
|
7
|
+
function loadJsonFile(relativePath) {
|
|
8
|
+
const filePath = path.join(resolveRepoRoot(), relativePath);
|
|
9
|
+
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
10
|
+
}
|
|
11
|
+
function serializeOption(option) {
|
|
12
|
+
return {
|
|
13
|
+
name: option.name(),
|
|
14
|
+
flags: option.flags,
|
|
15
|
+
description: option.description,
|
|
16
|
+
required: option.required,
|
|
17
|
+
mandatory: option.mandatory,
|
|
18
|
+
defaultValue: option.defaultValue
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function serializeArgument(argument) {
|
|
22
|
+
return {
|
|
23
|
+
name: argument.name(),
|
|
24
|
+
required: argument.required,
|
|
25
|
+
variadic: argument.variadic
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function serializeCommand(command, parents = []) {
|
|
29
|
+
const pathSegments = [...parents, command.name()];
|
|
30
|
+
return {
|
|
31
|
+
name: command.name(),
|
|
32
|
+
path: pathSegments.join(" "),
|
|
33
|
+
description: command.description(),
|
|
34
|
+
aliases: command.aliases(),
|
|
35
|
+
arguments: command.registeredArguments.map(serializeArgument),
|
|
36
|
+
options: command.options.map(serializeOption),
|
|
37
|
+
subcommands: command.commands.map((subcommand) => serializeCommand(subcommand, pathSegments)),
|
|
38
|
+
leaf: command.commands.length === 0
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function collectLeafCommands(commands) {
|
|
42
|
+
const leaves = [];
|
|
43
|
+
const visit = (command) => {
|
|
44
|
+
if (command.leaf) {
|
|
45
|
+
leaves.push(command.path);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
for (const subcommand of command.subcommands) {
|
|
49
|
+
visit(subcommand);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
for (const command of commands) {
|
|
53
|
+
visit(command);
|
|
54
|
+
}
|
|
55
|
+
return leaves;
|
|
56
|
+
}
|
|
57
|
+
export function buildSchemaDocument(program) {
|
|
58
|
+
const actions = loadJsonFile("protocol/actions.json");
|
|
59
|
+
const queries = loadJsonFile("protocol/queries.json");
|
|
60
|
+
const errors = loadJsonFile("protocol/errors.json");
|
|
61
|
+
const commands = program.commands.map((command) => serializeCommand(command));
|
|
62
|
+
return {
|
|
63
|
+
schemaVersion: 1,
|
|
64
|
+
cli: {
|
|
65
|
+
name: program.name(),
|
|
66
|
+
description: program.description(),
|
|
67
|
+
globalOptions: program.options.map(serializeOption),
|
|
68
|
+
commands,
|
|
69
|
+
leafCommands: collectLeafCommands(commands)
|
|
70
|
+
},
|
|
71
|
+
protocol: {
|
|
72
|
+
actions: actions.actions ?? [],
|
|
73
|
+
queries: queries.queries ?? [],
|
|
74
|
+
errors: errors.errors ?? []
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|