c64bridge 0.9.2 → 1.0.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/AGENTS.md +24 -3
- package/README.md +271 -161
- package/data/basic/rom-routines.md +269 -0
- package/data/context/bootstrap.md +1 -1
- package/data/io/cia-spec.md +2 -2
- package/data/{memory/kernal-memory-map.md → kernal/rom-routines.md} +3 -2
- package/data/memory/low-memory-map.md +1 -0
- package/data/memory/memory-map.md +13 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/c64Client.js +127 -12
- package/dist/config.js +34 -8
- package/dist/device.js +75 -45
- package/dist/mcp-server.js +28 -8
- package/dist/platform.js +13 -2
- package/dist/rag/knowledgeIndex.js +54 -9
- package/dist/rag/resourceUris.js +2 -0
- package/dist/tools/audio.js +1 -1
- package/dist/tools/developer.js +6 -6
- package/dist/tools/graphics.js +4 -4
- package/dist/tools/input.js +129 -12
- package/dist/tools/machineControl.js +85 -6
- package/dist/tools/memory.js +1 -1
- package/dist/tools/meta/index.js +1 -1
- package/dist/tools/programRunners.js +5 -5
- package/dist/tools/rag.js +1 -1
- package/dist/tools/registry/batch.js +1 -1
- package/dist/tools/registry/config.js +5 -5
- package/dist/tools/registry/disk.js +2 -2
- package/dist/tools/registry/drive.js +2 -2
- package/dist/tools/registry/graphics.js +2 -2
- package/dist/tools/registry/memory.js +2 -2
- package/dist/tools/registry/platform.js +7 -5
- package/dist/tools/registry/program.js +5 -5
- package/dist/tools/registry/rag.js +1 -1
- package/dist/tools/registry/sound.js +4 -4
- package/dist/tools/registry/system.js +13 -3
- package/dist/tools/storage.js +1 -1
- package/doc/c64u/c64-openapi.yaml +1130 -774
- package/doc/developer.md +3 -2
- package/doc/u2/u2-openapi.yaml +1192 -0
- package/generated/c64/index.js +142 -9
- package/generated/openapi/c64u/index.js +1117 -0
- package/generated/openapi/u2/index.js +929 -0
- package/mcp/resources.json +60 -7
- package/mcp/schemas/c64_graphics.schema.json +3 -3
- package/mcp/schemas/c64_input.schema.json +247 -6
- package/mcp/schemas/c64_memory.schema.json +3 -3
- package/mcp/schemas/c64_program.schema.json +3 -3
- package/mcp/schemas/c64_select_backend.schema.json +2 -0
- package/mcp/schemas/c64_system.schema.json +41 -1
- package/mcp/server.json +1 -1
- package/mcp/tools.json +382 -41
- package/mcp.json +21 -5
- package/package.json +14 -8
- package/scripts/mockC64Server.mjs +40 -0
- package/scripts/prepare-release.mjs +13 -2
- package/scripts/update-readme.ts +19 -30
- package/server.json +2 -2
|
@@ -286,7 +286,14 @@ const KNOWLEDGE_BUNDLES = [
|
|
|
286
286
|
summary: "Page-by-page breakdown of the 64 KB address space with hardware, ROM, and RAM regions.",
|
|
287
287
|
prompts: ["memory-debug", "assembly-program"],
|
|
288
288
|
tools: ["read", "write", "verify_and_write_memory"],
|
|
289
|
-
relatedResources: [
|
|
289
|
+
relatedResources: [
|
|
290
|
+
RESOURCE_URIS.memory.zeroPageAndWorkspace,
|
|
291
|
+
RESOURCE_URIS.basic.romRoutines,
|
|
292
|
+
RESOURCE_URIS.kernal.romRoutines,
|
|
293
|
+
RESOURCE_URIS.io.spec,
|
|
294
|
+
RESOURCE_URIS.io.cia.spec,
|
|
295
|
+
RESOURCE_URIS.assembly.spec6510,
|
|
296
|
+
],
|
|
290
297
|
tags: ["memory", "hardware"],
|
|
291
298
|
},
|
|
292
299
|
{
|
|
@@ -298,19 +305,48 @@ const KNOWLEDGE_BUNDLES = [
|
|
|
298
305
|
summary: "Documents zero-page variables, BASIC pointers, and KERNAL workspace addresses.",
|
|
299
306
|
prompts: ["memory-debug", "assembly-program"],
|
|
300
307
|
tools: ["read", "write", "verify_and_write_memory"],
|
|
301
|
-
relatedResources: [
|
|
308
|
+
relatedResources: [
|
|
309
|
+
RESOURCE_URIS.memory.map,
|
|
310
|
+
RESOURCE_URIS.basic.romRoutines,
|
|
311
|
+
RESOURCE_URIS.kernal.romRoutines,
|
|
312
|
+
RESOURCE_URIS.assembly.spec6510,
|
|
313
|
+
],
|
|
302
314
|
tags: ["memory", "zero-page"],
|
|
303
315
|
},
|
|
316
|
+
{
|
|
317
|
+
uri: RESOURCE_URIS.basic.romRoutines,
|
|
318
|
+
name: "BASIC ROM Routines Memory Map",
|
|
319
|
+
description: "Memory-map-style breakdown of BASIC ROM routines, tables, parser helpers, and entry points.",
|
|
320
|
+
relativePath: "data/basic/rom-routines.md",
|
|
321
|
+
priority: "reference",
|
|
322
|
+
summary: "Maps BASIC ROM landmarks, reusable interpreter routines, numeric helpers, string helpers, and C64-specific BASIC continuation entries.",
|
|
323
|
+
prompts: ["memory-debug", "assembly-program", "basic-program"],
|
|
324
|
+
tools: ["read", "verify_and_write_memory"],
|
|
325
|
+
relatedResources: [
|
|
326
|
+
RESOURCE_URIS.memory.map,
|
|
327
|
+
RESOURCE_URIS.memory.zeroPageAndWorkspace,
|
|
328
|
+
RESOURCE_URIS.kernal.romRoutines,
|
|
329
|
+
RESOURCE_URIS.basic.spec,
|
|
330
|
+
RESOURCE_URIS.assembly.spec6510,
|
|
331
|
+
],
|
|
332
|
+
tags: ["memory", "basic", "rom"],
|
|
333
|
+
},
|
|
304
334
|
{
|
|
305
335
|
uri: RESOURCE_URIS.kernal.romRoutines,
|
|
306
|
-
name: "KERNAL Memory Map",
|
|
307
|
-
description: "
|
|
308
|
-
relativePath: "data/
|
|
336
|
+
name: "KERNAL ROM Routines Memory Map",
|
|
337
|
+
description: "Memory-map-style breakdown of KERNAL ROM routines, vectors, tables, and entry points.",
|
|
338
|
+
relativePath: "data/kernal/rom-routines.md",
|
|
309
339
|
priority: "reference",
|
|
310
|
-
summary: "
|
|
340
|
+
summary: "Maps KERNAL ROM vectors, jump table entries, internal routines, serial/tape/screen services, and reset handlers.",
|
|
311
341
|
prompts: ["memory-debug", "assembly-program"],
|
|
312
342
|
tools: ["read", "verify_and_write_memory"],
|
|
313
|
-
relatedResources: [
|
|
343
|
+
relatedResources: [
|
|
344
|
+
RESOURCE_URIS.memory.map,
|
|
345
|
+
RESOURCE_URIS.memory.zeroPageAndWorkspace,
|
|
346
|
+
RESOURCE_URIS.basic.romRoutines,
|
|
347
|
+
RESOURCE_URIS.io.spec,
|
|
348
|
+
RESOURCE_URIS.assembly.spec6510,
|
|
349
|
+
],
|
|
314
350
|
tags: ["memory", "kernal"],
|
|
315
351
|
},
|
|
316
352
|
{
|
|
@@ -322,7 +358,12 @@ const KNOWLEDGE_BUNDLES = [
|
|
|
322
358
|
summary: "Covers VIC-II, SID, CIA, and system control registers with address ranges and usage notes.",
|
|
323
359
|
prompts: ["memory-debug", "assembly-program"],
|
|
324
360
|
tools: ["read", "write", "verify_and_write_memory", "debugreg_read", "debugreg_write"],
|
|
325
|
-
relatedResources: [
|
|
361
|
+
relatedResources: [
|
|
362
|
+
RESOURCE_URIS.memory.map,
|
|
363
|
+
RESOURCE_URIS.memory.zeroPageAndWorkspace,
|
|
364
|
+
RESOURCE_URIS.io.cia.spec,
|
|
365
|
+
RESOURCE_URIS.assembly.spec6510,
|
|
366
|
+
],
|
|
326
367
|
tags: ["io", "hardware"],
|
|
327
368
|
},
|
|
328
369
|
{
|
|
@@ -334,7 +375,11 @@ const KNOWLEDGE_BUNDLES = [
|
|
|
334
375
|
summary: "Details CIA 1/2 registers, timers, interrupts, and keyboard matrix layout.",
|
|
335
376
|
prompts: ["memory-debug", "assembly-program"],
|
|
336
377
|
tools: ["read", "write", "verify_and_write_memory", "debugreg_read"],
|
|
337
|
-
relatedResources: [
|
|
378
|
+
relatedResources: [
|
|
379
|
+
RESOURCE_URIS.io.spec,
|
|
380
|
+
RESOURCE_URIS.memory.map,
|
|
381
|
+
RESOURCE_URIS.memory.zeroPageAndWorkspace,
|
|
382
|
+
],
|
|
338
383
|
tags: ["io", "hardware"],
|
|
339
384
|
},
|
|
340
385
|
],
|
package/dist/rag/resourceUris.js
CHANGED
|
@@ -10,6 +10,7 @@ export const RESOURCE_URIS = {
|
|
|
10
10
|
basic: {
|
|
11
11
|
spec: "c64://basic/spec",
|
|
12
12
|
pitfalls: "c64://basic/pitfalls",
|
|
13
|
+
romRoutines: "c64://basic/rom-routines",
|
|
13
14
|
},
|
|
14
15
|
assembly: {
|
|
15
16
|
spec6510: "c64://assembly/6510-spec",
|
|
@@ -80,6 +81,7 @@ export const CANONICAL_KNOWLEDGE_RESOURCE_URIS = Object.freeze([
|
|
|
80
81
|
RESOURCE_URIS.graphics.spriteCharset.bestPractices,
|
|
81
82
|
RESOURCE_URIS.memory.map,
|
|
82
83
|
RESOURCE_URIS.memory.zeroPageAndWorkspace,
|
|
84
|
+
RESOURCE_URIS.basic.romRoutines,
|
|
83
85
|
RESOURCE_URIS.kernal.romRoutines,
|
|
84
86
|
RESOURCE_URIS.io.spec,
|
|
85
87
|
RESOURCE_URIS.io.cia.spec,
|
package/dist/tools/audio.js
CHANGED
|
@@ -403,7 +403,7 @@ export const audioModule = defineToolModule({
|
|
|
403
403
|
],
|
|
404
404
|
prompts: ["sid-music"],
|
|
405
405
|
defaultTags: ["sid", "audio"],
|
|
406
|
-
supportedPlatforms: ["c64u", "vice"],
|
|
406
|
+
supportedPlatforms: ["c64u", "u2", "vice"],
|
|
407
407
|
workflowHints: [
|
|
408
408
|
"Reach for SID helpers when the user talks about sound design, playback quality, or stuck notes.",
|
|
409
409
|
"After changing playback state, suggest verify-by-ear steps such as analyze_audio so the user gets concrete feedback.",
|
package/dist/tools/developer.js
CHANGED
|
@@ -146,7 +146,7 @@ export const developerModule = defineToolModule({
|
|
|
146
146
|
workflowHints: [
|
|
147
147
|
"Run first to enumerate categories before reading or writing specific settings; summarise the top-level sections for the user.",
|
|
148
148
|
],
|
|
149
|
-
supportedPlatforms: ["c64u", "vice"],
|
|
149
|
+
supportedPlatforms: ["c64u", "u2", "vice"],
|
|
150
150
|
async execute(args, ctx) {
|
|
151
151
|
try {
|
|
152
152
|
noArgsSchema.parse(args ?? {});
|
|
@@ -182,7 +182,7 @@ export const developerModule = defineToolModule({
|
|
|
182
182
|
workflowHints: [
|
|
183
183
|
"Use after config_list to fetch details; restate category/item so the user can cross-check values before changes.",
|
|
184
184
|
],
|
|
185
|
-
supportedPlatforms: ["c64u", "vice"],
|
|
185
|
+
supportedPlatforms: ["c64u", "u2", "vice"],
|
|
186
186
|
async execute(args, ctx) {
|
|
187
187
|
try {
|
|
188
188
|
const parsed = configGetArgsSchema.parse(args ?? {});
|
|
@@ -218,7 +218,7 @@ export const developerModule = defineToolModule({
|
|
|
218
218
|
workflowHints: [
|
|
219
219
|
"Confirm the user-supplied value before writing; mention whether a reboot is required for the change to take effect.",
|
|
220
220
|
],
|
|
221
|
-
supportedPlatforms: ["c64u", "vice"],
|
|
221
|
+
supportedPlatforms: ["c64u", "u2", "vice"],
|
|
222
222
|
async execute(args, ctx) {
|
|
223
223
|
try {
|
|
224
224
|
const parsed = configSetArgsSchema.parse(args ?? {});
|
|
@@ -262,7 +262,7 @@ export const developerModule = defineToolModule({
|
|
|
262
262
|
workflowHints: [
|
|
263
263
|
"Use when multiple categories must be updated together; remind the user to review validation errors closely.",
|
|
264
264
|
],
|
|
265
|
-
supportedPlatforms: ["c64u", "vice"],
|
|
265
|
+
supportedPlatforms: ["c64u", "u2", "vice"],
|
|
266
266
|
async execute(args, ctx) {
|
|
267
267
|
try {
|
|
268
268
|
const payload = configBatchSchema.parse(args ?? {});
|
|
@@ -405,7 +405,7 @@ export const developerModule = defineToolModule({
|
|
|
405
405
|
name: "version",
|
|
406
406
|
description: "Retrieve firmware or emulator version information.",
|
|
407
407
|
summary: "Calls the firmware version endpoint (Ultimate) or returns emulator info (VICE).",
|
|
408
|
-
supportedPlatforms: ["c64u", "vice"],
|
|
408
|
+
supportedPlatforms: ["c64u", "u2", "vice"],
|
|
409
409
|
inputSchema: noArgsSchema.jsonSchema,
|
|
410
410
|
relatedResources: ["c64://guide/bootstrap"],
|
|
411
411
|
tags: ["diagnostics", "version"],
|
|
@@ -437,7 +437,7 @@ export const developerModule = defineToolModule({
|
|
|
437
437
|
name: "info",
|
|
438
438
|
description: "Retrieve hardware or emulator information and status.",
|
|
439
439
|
summary: "Returns the diagnostics payload reported by the firmware (Ultimate) or emulator (VICE).",
|
|
440
|
-
supportedPlatforms: ["c64u", "vice"],
|
|
440
|
+
supportedPlatforms: ["c64u", "u2", "vice"],
|
|
441
441
|
inputSchema: noArgsSchema.jsonSchema,
|
|
442
442
|
relatedResources: ["c64://guide/bootstrap"],
|
|
443
443
|
tags: ["diagnostics", "info"],
|
package/dist/tools/graphics.js
CHANGED
|
@@ -577,7 +577,7 @@ export const graphicsModule = defineToolModule({
|
|
|
577
577
|
"Use when the user supplies sprite bytes or asks to preview graphics quickly; describe resulting coordinates and colours.",
|
|
578
578
|
"Remind the user that sprites live in banked memory so further tweaks may require write_memory calls.",
|
|
579
579
|
],
|
|
580
|
-
supportedPlatforms: ["c64u", "vice"],
|
|
580
|
+
supportedPlatforms: ["c64u", "u2", "vice"],
|
|
581
581
|
async execute(args, ctx) {
|
|
582
582
|
return executeGenerateSprite(args, ctx);
|
|
583
583
|
},
|
|
@@ -594,7 +594,7 @@ export const graphicsModule = defineToolModule({
|
|
|
594
594
|
"Use when the user wants to display an external image on screen rather than generate PETSCII art.",
|
|
595
595
|
"Call out the selected bitmap and screen RAM addresses so follow-up memory inspection or raster work stays grounded.",
|
|
596
596
|
],
|
|
597
|
-
supportedPlatforms: ["c64u", "vice"],
|
|
597
|
+
supportedPlatforms: ["c64u", "u2", "vice"],
|
|
598
598
|
async execute(args, ctx) {
|
|
599
599
|
return executeGenerateBitmap(args, ctx);
|
|
600
600
|
},
|
|
@@ -619,7 +619,7 @@ export const graphicsModule = defineToolModule({
|
|
|
619
619
|
"Call after generating PETSCII text or when the user wants border/background colour changes applied.",
|
|
620
620
|
"Echo the colour indices and mention CLEAR + PRINT so the user knows what ran.",
|
|
621
621
|
],
|
|
622
|
-
supportedPlatforms: ["c64u", "vice"],
|
|
622
|
+
supportedPlatforms: ["c64u", "u2", "vice"],
|
|
623
623
|
async execute(args, ctx) {
|
|
624
624
|
return executeRenderPetscii(args, ctx);
|
|
625
625
|
},
|
|
@@ -645,7 +645,7 @@ export const graphicsModule = defineToolModule({
|
|
|
645
645
|
"Response includes petsciiCodes array and glyphs for character-level inspection.",
|
|
646
646
|
"Provide follow-up suggestions like saving the PRG or capturing the screen after rendering.",
|
|
647
647
|
],
|
|
648
|
-
supportedPlatforms: ["c64u", "vice"],
|
|
648
|
+
supportedPlatforms: ["c64u", "u2", "vice"],
|
|
649
649
|
async execute(args, ctx) {
|
|
650
650
|
return executeCreatePetscii(args, ctx);
|
|
651
651
|
},
|
package/dist/tools/input.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { createOperationDispatcher, defineToolModule, discriminatedUnionSchema, } from "./types.js";
|
|
2
2
|
import { arraySchema, literalSchema, numberSchema, objectSchema, optionalSchema, stringSchema, } from "./schema.js";
|
|
3
|
-
import { textResult } from "./responses.js";
|
|
3
|
+
import { jsonResult, textResult } from "./responses.js";
|
|
4
4
|
import { ToolValidationError, toolErrorResult, unknownErrorResult, } from "./errors.js";
|
|
5
|
+
function isMissingNativeInputEndpoint(error) {
|
|
6
|
+
return typeof error === "object"
|
|
7
|
+
&& error !== null
|
|
8
|
+
&& "response" in error
|
|
9
|
+
&& error.response?.status === 404;
|
|
10
|
+
}
|
|
11
|
+
function nativeInputUnavailableResult(operation) {
|
|
12
|
+
return textResult(`Native physical input is unavailable on this firmware, so ${operation} was not sent. Use c64://platform/status before choosing native input; use key or write_text for ordinary C64 input.`, { success: false, code: "native_input_unavailable", fallback: "key_or_write_text" });
|
|
13
|
+
}
|
|
5
14
|
// ---------------------------------------------------------------------------
|
|
6
15
|
// PETSCII token expansion table
|
|
7
16
|
// Maps {TOKEN} placeholders to PETSCII/control-byte values that are queued via
|
|
@@ -96,6 +105,14 @@ const JOYSTICK_BIT = {
|
|
|
96
105
|
right: 3,
|
|
97
106
|
fire: 4,
|
|
98
107
|
};
|
|
108
|
+
const KEYBOARD_INPUTS = [
|
|
109
|
+
"inst_del", "return", "cursor_left_right", "f7", "f1", "f3", "f5", "cursor_up_down",
|
|
110
|
+
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
|
111
|
+
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
|
|
112
|
+
"left_shift", "right_shift", "plus", "minus", "period", "colon", "at", "comma", "pound", "star", "semicolon", "clr_home", "equals", "arrow_up", "slash", "arrow_left", "ctrl", "space", "commodore", "run_stop", "restore",
|
|
113
|
+
];
|
|
114
|
+
const JOYSTICK_INPUTS = ["up", "down", "left", "right", "fire", "fire2", "fire3"];
|
|
115
|
+
const INPUT_TRANSITIONS = ["press", "release", "tap"];
|
|
99
116
|
function joystickByte(controls) {
|
|
100
117
|
let mask = 0xff;
|
|
101
118
|
for (const ctrl of controls) {
|
|
@@ -157,7 +174,7 @@ export const keyArgsSchema = objectSchema({
|
|
|
157
174
|
additionalProperties: false,
|
|
158
175
|
});
|
|
159
176
|
export const joystickArgsSchema = objectSchema({
|
|
160
|
-
description: "Simulate joystick input
|
|
177
|
+
description: "Simulate joystick input. On C64U/U64 this uses machine:input (a C64U firmware version that provides it, or U64 3.15+); VICE writes CIA1 registers.",
|
|
161
178
|
properties: {
|
|
162
179
|
op: literalSchema("joystick"),
|
|
163
180
|
port: numberSchema({
|
|
@@ -168,7 +185,7 @@ export const joystickArgsSchema = objectSchema({
|
|
|
168
185
|
}),
|
|
169
186
|
controls: arraySchema(stringSchema({
|
|
170
187
|
description: "Control to activate.",
|
|
171
|
-
enum:
|
|
188
|
+
enum: JOYSTICK_INPUTS,
|
|
172
189
|
}), {
|
|
173
190
|
description: "List of controls to activate simultaneously.",
|
|
174
191
|
minItems: 0,
|
|
@@ -188,6 +205,32 @@ export const joystickArgsSchema = objectSchema({
|
|
|
188
205
|
required: ["op", "port", "controls", "action"],
|
|
189
206
|
additionalProperties: false,
|
|
190
207
|
});
|
|
208
|
+
export const keyboardArgsSchema = objectSchema({
|
|
209
|
+
description: "Send physical C64 keyboard matrix events through machine:input (a C64U firmware version that provides it, or U64 3.15+).",
|
|
210
|
+
properties: {
|
|
211
|
+
op: literalSchema("keyboard"),
|
|
212
|
+
inputs: arraySchema(stringSchema({ description: "Physical keyboard input.", enum: KEYBOARD_INPUTS }), {
|
|
213
|
+
description: "One or more keys to transition together (for example left_shift + a).",
|
|
214
|
+
minItems: 1,
|
|
215
|
+
maxItems: 8,
|
|
216
|
+
}),
|
|
217
|
+
transition: stringSchema({ description: "press holds, release frees, tap presses and releases.", enum: INPUT_TRANSITIONS }),
|
|
218
|
+
},
|
|
219
|
+
required: ["op", "inputs", "transition"],
|
|
220
|
+
additionalProperties: false,
|
|
221
|
+
});
|
|
222
|
+
const releaseAllArgsSchema = objectSchema({
|
|
223
|
+
description: "Release every key and joystick control injected through machine:input (a C64U firmware version that provides it, or U64 3.15+).",
|
|
224
|
+
properties: { op: literalSchema("release_all") },
|
|
225
|
+
required: ["op"],
|
|
226
|
+
additionalProperties: false,
|
|
227
|
+
});
|
|
228
|
+
const inputStateArgsSchema = objectSchema({
|
|
229
|
+
description: "Read the keys and joystick controls held through machine:input (a C64U firmware version that provides it, or U64 3.15+).",
|
|
230
|
+
properties: { op: literalSchema("state") },
|
|
231
|
+
required: ["op"],
|
|
232
|
+
additionalProperties: false,
|
|
233
|
+
});
|
|
191
234
|
// ---------------------------------------------------------------------------
|
|
192
235
|
// Handlers
|
|
193
236
|
// ---------------------------------------------------------------------------
|
|
@@ -250,10 +293,26 @@ const inputOperationHandlers = {
|
|
|
250
293
|
try {
|
|
251
294
|
const parsed = joystickArgsSchema.parse(args);
|
|
252
295
|
const port = parsed.port;
|
|
253
|
-
const addr = JOYSTICK_PORT_ADDRESS[port];
|
|
254
296
|
const durationMs = parsed.durationMs ?? 80;
|
|
255
297
|
const pressedByte = joystickByte(parsed.controls);
|
|
256
298
|
const releasedByte = 0xff;
|
|
299
|
+
const platform = await ctx.client.getActiveBackendType();
|
|
300
|
+
if (platform === "c64u") {
|
|
301
|
+
if (parsed.controls.length === 0 && parsed.action === "release") {
|
|
302
|
+
const state = await ctx.client.sendInputEvents({ events: [{ kind: "release_all" }] });
|
|
303
|
+
return textResult("Released all REST-injected keyboard and joystick inputs.", { success: true, action: "release_all", state });
|
|
304
|
+
}
|
|
305
|
+
const state = await ctx.client.sendInputEvents({
|
|
306
|
+
events: [{ kind: "joystick", port, inputs: [...parsed.controls], transition: parsed.action }],
|
|
307
|
+
});
|
|
308
|
+
return textResult(`Joystick port ${port} ${parsed.action}: ${parsed.controls.join(", ")}.`, {
|
|
309
|
+
success: true, port, action: parsed.action, controls: parsed.controls, state,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
const addr = JOYSTICK_PORT_ADDRESS[port];
|
|
313
|
+
if (parsed.controls.some((control) => !(control in JOYSTICK_BIT))) {
|
|
314
|
+
throw new ToolValidationError("VICE joystick supports up, down, left, right, and fire only.", { path: "$.controls" });
|
|
315
|
+
}
|
|
257
316
|
if (parsed.action === "release") {
|
|
258
317
|
await ctx.client.viceMemSet(addr, Uint8Array.of(releasedByte));
|
|
259
318
|
ctx.logger.info("Released joystick", { port, address: `$${addr.toString(16).toUpperCase()}` });
|
|
@@ -274,6 +333,53 @@ const inputOperationHandlers = {
|
|
|
274
333
|
catch (error) {
|
|
275
334
|
if (error instanceof ToolValidationError)
|
|
276
335
|
return toolErrorResult(error);
|
|
336
|
+
if (isMissingNativeInputEndpoint(error))
|
|
337
|
+
return nativeInputUnavailableResult("the joystick event");
|
|
338
|
+
return unknownErrorResult(error);
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
keyboard: async (args, ctx) => {
|
|
342
|
+
try {
|
|
343
|
+
const parsed = keyboardArgsSchema.parse(args);
|
|
344
|
+
const state = await ctx.client.sendInputEvents({
|
|
345
|
+
events: [{ kind: "keyboard", inputs: [...parsed.inputs], transition: parsed.transition }],
|
|
346
|
+
});
|
|
347
|
+
return textResult(`Keyboard ${parsed.transition}: ${parsed.inputs.join(", ")}.`, {
|
|
348
|
+
success: true, inputs: parsed.inputs, transition: parsed.transition, state,
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
catch (error) {
|
|
352
|
+
if (error instanceof ToolValidationError)
|
|
353
|
+
return toolErrorResult(error);
|
|
354
|
+
if (isMissingNativeInputEndpoint(error))
|
|
355
|
+
return nativeInputUnavailableResult("the keyboard event");
|
|
356
|
+
return unknownErrorResult(error);
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
release_all: async (args, ctx) => {
|
|
360
|
+
try {
|
|
361
|
+
releaseAllArgsSchema.parse(args);
|
|
362
|
+
const state = await ctx.client.sendInputEvents({ events: [{ kind: "release_all" }] });
|
|
363
|
+
return textResult("Released all REST-injected keyboard and joystick inputs.", { success: true, state });
|
|
364
|
+
}
|
|
365
|
+
catch (error) {
|
|
366
|
+
if (error instanceof ToolValidationError)
|
|
367
|
+
return toolErrorResult(error);
|
|
368
|
+
if (isMissingNativeInputEndpoint(error))
|
|
369
|
+
return nativeInputUnavailableResult("the release request");
|
|
370
|
+
return unknownErrorResult(error);
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
state: async (args, ctx) => {
|
|
374
|
+
try {
|
|
375
|
+
inputStateArgsSchema.parse(args);
|
|
376
|
+
return jsonResult(await ctx.client.getInputState());
|
|
377
|
+
}
|
|
378
|
+
catch (error) {
|
|
379
|
+
if (error instanceof ToolValidationError)
|
|
380
|
+
return toolErrorResult(error);
|
|
381
|
+
if (isMissingNativeInputEndpoint(error))
|
|
382
|
+
return nativeInputUnavailableResult("the input-state query");
|
|
277
383
|
return unknownErrorResult(error);
|
|
278
384
|
}
|
|
279
385
|
},
|
|
@@ -283,30 +389,36 @@ const inputOperationHandlers = {
|
|
|
283
389
|
// ---------------------------------------------------------------------------
|
|
284
390
|
export const inputModule = defineToolModule({
|
|
285
391
|
domain: "input",
|
|
286
|
-
summary: "
|
|
287
|
-
supportedPlatforms: ["c64u", "vice"],
|
|
392
|
+
summary: "Keyboard input plus native Ultimate REST keyboard and joystick control.",
|
|
393
|
+
supportedPlatforms: ["c64u", "u2", "vice"],
|
|
288
394
|
resources: ["c64://guide/bootstrap", "c64://memory/map", "c64://io/cia/spec"],
|
|
289
395
|
prompts: [],
|
|
290
396
|
defaultTags: ["input"],
|
|
291
397
|
workflowHints: [
|
|
292
|
-
"Use write_text with {RETURN} tokens to automate BASIC entry;
|
|
398
|
+
"Use write_text with {RETURN} tokens to automate BASIC entry; use key for one PETSCII/KERNAL queue key. Neither is for navigating Ultimate firmware menus.",
|
|
293
399
|
"write_text and key inject through the KERNAL keyboard queue ($0277/$00C6) so they work on both C64U and VICE.",
|
|
294
|
-
"
|
|
400
|
+
"Use keyboard for physical C64 key combinations (including modifiers) on machine:input, and release_all to recover from interrupted holds. It requires a C64U firmware version that provides the endpoint, or U64 3.15+.",
|
|
401
|
+
"Before native input, read c64://platform/status. If machine:input is unavailable or unknown, use key or write_text for ordinary C64 input instead of attempting physical matrix events.",
|
|
402
|
+
"Change C64U/U64/U2 configuration through c64_config REST operations, never by keyboard menu navigation. Use keyboard navigation only where REST has no equivalent, such as the machine code monitor, visual SID editor, or Tool Menu.",
|
|
403
|
+
"Joystick uses machine:input on a C64U firmware version that provides the endpoint or U64 3.15+; VICE supports up/down/left/right/fire through its monitor.",
|
|
295
404
|
],
|
|
296
405
|
tools: [
|
|
297
406
|
{
|
|
298
407
|
name: "c64_input",
|
|
299
|
-
description: "
|
|
300
|
-
summary: "Types text,
|
|
408
|
+
description: "Cross-platform PETSCII typing plus native Ultimate keyboard and joystick events.",
|
|
409
|
+
summary: "Types text, sends physical key combinations, and controls joysticks.",
|
|
301
410
|
inputSchema: discriminatedUnionSchema({
|
|
302
|
-
description: "Input operations: write_text, key, joystick.",
|
|
411
|
+
description: "Input operations: write_text, key, keyboard, joystick, release_all, state.",
|
|
303
412
|
variants: [
|
|
304
413
|
writeTextArgsSchema.jsonSchema,
|
|
305
414
|
keyArgsSchema.jsonSchema,
|
|
306
415
|
joystickArgsSchema.jsonSchema,
|
|
416
|
+
keyboardArgsSchema.jsonSchema,
|
|
417
|
+
releaseAllArgsSchema.jsonSchema,
|
|
418
|
+
inputStateArgsSchema.jsonSchema,
|
|
307
419
|
],
|
|
308
420
|
}),
|
|
309
|
-
operationPlatforms: { joystick: ["vice"] },
|
|
421
|
+
operationPlatforms: { joystick: ["c64u", "vice"], keyboard: ["c64u"], release_all: ["c64u"], state: ["c64u"] },
|
|
310
422
|
tags: ["input", "keyboard", "joystick"],
|
|
311
423
|
examples: [
|
|
312
424
|
{
|
|
@@ -324,6 +436,11 @@ export const inputModule = defineToolModule({
|
|
|
324
436
|
description: "Brief rightward tap on joystick port 2",
|
|
325
437
|
arguments: { op: "joystick", port: 2, controls: ["right"], action: "tap", durationMs: 80 },
|
|
326
438
|
},
|
|
439
|
+
{
|
|
440
|
+
name: "Type an uppercase A",
|
|
441
|
+
description: "Tap a physical key chord through Ultimate REST input",
|
|
442
|
+
arguments: { op: "keyboard", inputs: ["left_shift", "a"], transition: "tap" },
|
|
443
|
+
},
|
|
327
444
|
{
|
|
328
445
|
name: "Press fire on port 1",
|
|
329
446
|
description: "Hold fire button on joystick port 1",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defineToolModule } from "./types.js";
|
|
2
2
|
import { objectSchema } from "./schema.js";
|
|
3
|
-
import { textResult } from "./responses.js";
|
|
3
|
+
import { jsonResult, textResult } from "./responses.js";
|
|
4
|
+
import { Buffer } from "node:buffer";
|
|
4
5
|
import { ToolError, ToolExecutionError, toolErrorResult, unknownErrorResult, } from "./errors.js";
|
|
5
6
|
function createNoArgsSchema(description) {
|
|
6
7
|
return objectSchema({
|
|
@@ -24,12 +25,20 @@ function normaliseFailure(details) {
|
|
|
24
25
|
}
|
|
25
26
|
return { value: details };
|
|
26
27
|
}
|
|
28
|
+
function isMissingMenuScreenEndpoint(error) {
|
|
29
|
+
return typeof error === "object"
|
|
30
|
+
&& error !== null
|
|
31
|
+
&& "response" in error
|
|
32
|
+
&& error.response?.status === 404;
|
|
33
|
+
}
|
|
27
34
|
const resetArgsSchema = createNoArgsSchema("No arguments required to reset the C64.");
|
|
28
35
|
const rebootArgsSchema = createNoArgsSchema("No arguments required to reboot the C64 firmware.");
|
|
29
36
|
const pauseArgsSchema = createNoArgsSchema("No arguments required to pause the machine.");
|
|
30
37
|
const resumeArgsSchema = createNoArgsSchema("No arguments required to resume the machine after a pause.");
|
|
31
38
|
const poweroffArgsSchema = createNoArgsSchema("No arguments required to power off the machine.");
|
|
39
|
+
const powerCycleArgsSchema = createNoArgsSchema("No arguments required to power cycle the active system.");
|
|
32
40
|
const menuButtonArgsSchema = createNoArgsSchema("No arguments required to toggle the Ultimate menu button.");
|
|
41
|
+
const readMenuScreenArgsSchema = createNoArgsSchema("No arguments required to read the active Ultimate menu screen matrix.");
|
|
33
42
|
export const machineControlModule = defineToolModule({
|
|
34
43
|
domain: "machine",
|
|
35
44
|
summary: "Power, reset, pause/resume, and diagnostic controls for the C64 and Ultimate hardware.",
|
|
@@ -54,7 +63,7 @@ export const machineControlModule = defineToolModule({
|
|
|
54
63
|
workflowHints: [
|
|
55
64
|
"Use when the user wants a quick restart without losing power; mention that memory contents may persist.",
|
|
56
65
|
],
|
|
57
|
-
supportedPlatforms: ["c64u", "vice"],
|
|
66
|
+
supportedPlatforms: ["c64u", "u2", "vice"],
|
|
58
67
|
async execute(args, ctx) {
|
|
59
68
|
try {
|
|
60
69
|
resetArgsSchema.parse(args ?? {});
|
|
@@ -92,7 +101,7 @@ export const machineControlModule = defineToolModule({
|
|
|
92
101
|
workflowHints: [
|
|
93
102
|
"Choose reboot when configuration changed or hardware is stuck; warn that it will interrupt any running program.",
|
|
94
103
|
],
|
|
95
|
-
supportedPlatforms: ["c64u", "vice"],
|
|
104
|
+
supportedPlatforms: ["c64u", "u2", "vice"],
|
|
96
105
|
async execute(args, ctx) {
|
|
97
106
|
try {
|
|
98
107
|
rebootArgsSchema.parse(args ?? {});
|
|
@@ -130,7 +139,7 @@ export const machineControlModule = defineToolModule({
|
|
|
130
139
|
workflowHints: [
|
|
131
140
|
"Pause when the user needs a stable memory snapshot; remind them to resume to continue execution.",
|
|
132
141
|
],
|
|
133
|
-
supportedPlatforms: ["c64u"],
|
|
142
|
+
supportedPlatforms: ["c64u", "u2"],
|
|
134
143
|
async execute(args, ctx) {
|
|
135
144
|
try {
|
|
136
145
|
pauseArgsSchema.parse(args ?? {});
|
|
@@ -168,7 +177,7 @@ export const machineControlModule = defineToolModule({
|
|
|
168
177
|
workflowHints: [
|
|
169
178
|
"Call after a pause or diagnostic halt and confirm the machine is running again.",
|
|
170
179
|
],
|
|
171
|
-
supportedPlatforms: ["c64u"],
|
|
180
|
+
supportedPlatforms: ["c64u", "u2"],
|
|
172
181
|
async execute(args, ctx) {
|
|
173
182
|
try {
|
|
174
183
|
resumeArgsSchema.parse(args ?? {});
|
|
@@ -231,6 +240,41 @@ export const machineControlModule = defineToolModule({
|
|
|
231
240
|
}
|
|
232
241
|
},
|
|
233
242
|
},
|
|
243
|
+
{
|
|
244
|
+
name: "power_cycle",
|
|
245
|
+
description: "Return the active system to a fresh state: C64U/U64 uses verified Tool Menu navigation, U2-family reboots through REST, and managed VICE restarts.",
|
|
246
|
+
summary: "Performs the platform-appropriate full power-cycle equivalent and reports the strategy used.",
|
|
247
|
+
inputSchema: powerCycleArgsSchema.jsonSchema,
|
|
248
|
+
tags: ["power", "reboot", "fresh-start"],
|
|
249
|
+
prerequisites: [],
|
|
250
|
+
examples: [
|
|
251
|
+
{ name: "Fresh start", description: "Power cycle the active system", arguments: {} },
|
|
252
|
+
],
|
|
253
|
+
workflowHints: [
|
|
254
|
+
"Use only when the user explicitly requests a fresh start or power cycle; it interrupts active work.",
|
|
255
|
+
"On C64U/U64, verify Tool Menu navigation through machine:menu_screen before selecting Power Cycle.",
|
|
256
|
+
],
|
|
257
|
+
supportedPlatforms: ["c64u", "u2", "vice"],
|
|
258
|
+
async execute(args, ctx) {
|
|
259
|
+
try {
|
|
260
|
+
powerCycleArgsSchema.parse(args ?? {});
|
|
261
|
+
ctx.logger.info("Power cycling active backend");
|
|
262
|
+
const result = await ctx.client.powerCycle();
|
|
263
|
+
if (!result.success) {
|
|
264
|
+
throw new ToolExecutionError("Power cycle did not complete", {
|
|
265
|
+
details: normaliseFailure(result.details),
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
const details = toRecord(result.details) ?? {};
|
|
269
|
+
return textResult("Power-cycle request completed.", { success: true, details });
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
if (error instanceof ToolError)
|
|
273
|
+
return toolErrorResult(error);
|
|
274
|
+
return unknownErrorResult(error);
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
},
|
|
234
278
|
{
|
|
235
279
|
name: "menu_button",
|
|
236
280
|
description: "Toggle the Ultimate 64 menu button.",
|
|
@@ -244,7 +288,7 @@ export const machineControlModule = defineToolModule({
|
|
|
244
288
|
workflowHints: [
|
|
245
289
|
"Use when the user needs to open or close the Ultimate menu; suggest following up with drive operations if relevant.",
|
|
246
290
|
],
|
|
247
|
-
supportedPlatforms: ["c64u"],
|
|
291
|
+
supportedPlatforms: ["c64u", "u2"],
|
|
248
292
|
async execute(args, ctx) {
|
|
249
293
|
try {
|
|
250
294
|
menuButtonArgsSchema.parse(args ?? {});
|
|
@@ -269,5 +313,40 @@ export const machineControlModule = defineToolModule({
|
|
|
269
313
|
}
|
|
270
314
|
},
|
|
271
315
|
},
|
|
316
|
+
{
|
|
317
|
+
name: "read_menu_screen",
|
|
318
|
+
description: "Read the raw character and colour matrix for the currently visible Ultimate menu screen.",
|
|
319
|
+
summary: "Returns the firmware-defined menu matrix bytes, including text and colour information.",
|
|
320
|
+
inputSchema: readMenuScreenArgsSchema.jsonSchema,
|
|
321
|
+
tags: ["menu", "screen", "diagnostic"],
|
|
322
|
+
prerequisites: ["menu_button"],
|
|
323
|
+
examples: [
|
|
324
|
+
{ name: "Inspect active menu", description: "Read the currently visible Ultimate menu", arguments: {} },
|
|
325
|
+
],
|
|
326
|
+
workflowHints: [
|
|
327
|
+
"Read c64://platform/status first. Call after opening the Ultimate menu; if the endpoint is unavailable or no menu is active, the tool returns fallback guidance instead of a transport error.",
|
|
328
|
+
"The firmware returns an opaque character-and-colour matrix, preserved as base64 without guessing a layout.",
|
|
329
|
+
],
|
|
330
|
+
supportedPlatforms: ["c64u", "u2"],
|
|
331
|
+
async execute(args, ctx) {
|
|
332
|
+
try {
|
|
333
|
+
readMenuScreenArgsSchema.parse(args ?? {});
|
|
334
|
+
const bytes = await ctx.client.readMenuScreen();
|
|
335
|
+
return jsonResult({
|
|
336
|
+
encoding: "base64",
|
|
337
|
+
byteLength: bytes.length,
|
|
338
|
+
matrix: Buffer.from(bytes).toString("base64"),
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
catch (error) {
|
|
342
|
+
if (error instanceof ToolError)
|
|
343
|
+
return toolErrorResult(error);
|
|
344
|
+
if (isMissingMenuScreenEndpoint(error)) {
|
|
345
|
+
return textResult("No menu matrix is available. Open the Ultimate menu and retry; if it remains unavailable, this firmware does not provide machine:menu_screen. Use REST configuration instead of menu navigation where possible.", { success: false, code: "native_menu_screen_unavailable", fallback: "rest_configuration_or_manual_menu" });
|
|
346
|
+
}
|
|
347
|
+
return unknownErrorResult(error);
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
},
|
|
272
351
|
],
|
|
273
352
|
});
|
package/dist/tools/memory.js
CHANGED
|
@@ -685,7 +685,7 @@ export { disassembleArgsSchema, copyMemoryArgsSchema, fillMemoryArgsSchema, sear
|
|
|
685
685
|
export const memoryModule = defineToolModule({
|
|
686
686
|
domain: "memory",
|
|
687
687
|
summary: "Screen, main memory, and low-level inspection utilities.",
|
|
688
|
-
supportedPlatforms: ["c64u", "vice"],
|
|
688
|
+
supportedPlatforms: ["c64u", "u2", "vice"],
|
|
689
689
|
resources: [
|
|
690
690
|
"c64://guide/bootstrap",
|
|
691
691
|
"c64://basic/spec",
|
package/dist/tools/meta/index.js
CHANGED
|
@@ -30,7 +30,7 @@ export const metaModule = defineToolModule({
|
|
|
30
30
|
summary: "High-level meta tools that orchestrate multiple MCP actions.",
|
|
31
31
|
resources: ["c64://guide/bootstrap", "c64://assembly/6510-spec"],
|
|
32
32
|
defaultTags: ["meta", "orchestration", "experimental"],
|
|
33
|
-
supportedPlatforms: ["c64u", "vice"],
|
|
33
|
+
supportedPlatforms: ["c64u", "u2", "vice"],
|
|
34
34
|
workflowHints: [
|
|
35
35
|
"Use meta tools to reduce round-trips by composing several steps into one.",
|
|
36
36
|
],
|