c64-debug-mcp 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/http.cjs CHANGED
@@ -1370,6 +1370,8 @@ var DEFAULT_INPUT_TAP_MS = 75;
1370
1370
  var DEFAULT_KEYBOARD_REPEAT_MS = 100;
1371
1371
  var VICE_PROCESS_LOG_PATH = import_node_path.default.join(import_node_os.default.tmpdir(), "c64-debug-mcp-x64sc.log");
1372
1372
  var DISPLAY_CAPTURE_DIR = import_node_path.default.resolve(process.cwd(), ".vice-debug-mcp-artifacts");
1373
+ var CLEANUP_ENABLED = !/^(0|false|no|off)$/i.test(process.env.C64_CLEANUP_SCREENSHOTS ?? "");
1374
+ var CLEANUP_MAX_AGE_MINUTES = Number.parseInt(process.env.C64_CLEANUP_MAX_AGE_MINUTES ?? "20", 10);
1373
1375
  var MIRROR_EMULATOR_LOGS_TO_STDERR = /^(1|true|yes|on)$/i.test(process.env.C64_DEBUG_CONSOLE_LOGS ?? "");
1374
1376
  var EXECUTION_EVENT_WAIT_MS = 1e3;
1375
1377
  var EXECUTION_SETTLE_DELAY_MS = 2e3;
@@ -1518,6 +1520,7 @@ var ViceSession = class {
1518
1520
  #displayOperationLock = null;
1519
1521
  constructor(portAllocator = new PortAllocator()) {
1520
1522
  this.#portAllocator = portAllocator;
1523
+ void this.#cleanupOldScreenshots();
1521
1524
  this.#client.on("response", (response) => {
1522
1525
  this.#lastResponseAt = nowIso();
1523
1526
  this.#writeProcessLogLine(`[monitor-response] type=${response.type} requestId=${response.requestId} errorCode=${response.errorCode}`);
@@ -1709,7 +1712,7 @@ var ViceSession = class {
1709
1712
  };
1710
1713
  }
1711
1714
  async readMemory(start, end, bank = 0) {
1712
- await this.#ensurePausedForDebug("memory_read");
1715
+ await this.#ensureReady();
1713
1716
  this.#validateRange(start, end);
1714
1717
  const response = await this.#client.readMemory(start, end, bank);
1715
1718
  return {
@@ -3160,6 +3163,45 @@ var ViceSession = class {
3160
3163
  }
3161
3164
  this.#syncMonitorRuntimeState();
3162
3165
  }
3166
+ async #cleanupOldScreenshots() {
3167
+ if (!CLEANUP_ENABLED) {
3168
+ return;
3169
+ }
3170
+ try {
3171
+ const maxAgeMinutes = Math.max(1, Math.min(525600, CLEANUP_MAX_AGE_MINUTES));
3172
+ const maxAgeMs = maxAgeMinutes * 60 * 1e3;
3173
+ const cutoffTime = Date.now() - maxAgeMs;
3174
+ this.#writeProcessLogLine(`[cleanup] scanning ${DISPLAY_CAPTURE_DIR} for screenshots older than ${maxAgeMinutes}m`);
3175
+ let entries;
3176
+ try {
3177
+ entries = await import_promises.default.readdir(DISPLAY_CAPTURE_DIR);
3178
+ } catch (error) {
3179
+ if (error.code === "ENOENT") {
3180
+ return;
3181
+ }
3182
+ throw error;
3183
+ }
3184
+ const pngFiles = entries.filter((name) => name.endsWith(".png") && name.startsWith("capture-"));
3185
+ let deletedCount = 0;
3186
+ let errorCount = 0;
3187
+ for (const filename of pngFiles) {
3188
+ try {
3189
+ const filePath = import_node_path.default.join(DISPLAY_CAPTURE_DIR, filename);
3190
+ const stats = await import_promises.default.stat(filePath);
3191
+ if (stats.mtime.getTime() < cutoffTime) {
3192
+ await import_promises.default.unlink(filePath);
3193
+ deletedCount++;
3194
+ }
3195
+ } catch (error) {
3196
+ errorCount++;
3197
+ this.#writeProcessLogLine(`[cleanup] failed to delete ${filename}: ${error instanceof Error ? error.message : String(error)}`);
3198
+ }
3199
+ }
3200
+ this.#writeProcessLogLine(`[cleanup] completed: ${deletedCount} deleted, ${errorCount} errors, ${pngFiles.length - deletedCount - errorCount} retained`);
3201
+ } catch (error) {
3202
+ this.#writeProcessLogLine(`[cleanup] failed: ${error instanceof Error ? error.message : String(error)}`);
3203
+ }
3204
+ }
3163
3205
  };
3164
3206
  function splitCommandLine(input) {
3165
3207
  const result = [];
@@ -3262,14 +3304,14 @@ var getMonitorStateTool = createViceTool({
3262
3304
  });
3263
3305
  var getSessionStateTool = createViceTool({
3264
3306
  id: "get_session_state",
3265
- description: "Returns richer emulator session state including transport/process status, auto-resume state, and the most recent hit checkpoint when known.",
3307
+ description: "Returns emulator session state including transport/process status, auto-resume state, and the most recent hit checkpoint.",
3266
3308
  inputSchema: noInputSchema,
3267
3309
  dataSchema: sessionStateResultSchema,
3268
3310
  execute: async () => c64Session.snapshot()
3269
3311
  });
3270
3312
  var getRegistersTool = createViceTool({
3271
3313
  id: "get_registers",
3272
- description: "Returns the current C64 register snapshot. This requires the emulator to already be stopped.",
3314
+ description: 'Returns the current C64 register snapshot. Requires emulator to be stopped - call execute(action="pause") first if running.',
3273
3315
  inputSchema: noInputSchema,
3274
3316
  dataSchema: import_zod4.z.object({
3275
3317
  registers: c64RegisterValueSchema
@@ -3278,7 +3320,7 @@ var getRegistersTool = createViceTool({
3278
3320
  });
3279
3321
  var setRegistersTool = createViceTool({
3280
3322
  id: "set_registers",
3281
- description: "Sets one or more C64 registers by field name.",
3323
+ description: 'Sets one or more C64 registers by field name. Requires emulator to be stopped - call execute(action="pause") first if running.',
3282
3324
  inputSchema: import_zod4.z.object({
3283
3325
  registers: c64PartialRegisterValueSchema
3284
3326
  }),
@@ -3314,7 +3356,7 @@ var readMemoryTool = createViceTool({
3314
3356
  });
3315
3357
  var writeMemoryTool = createViceTool({
3316
3358
  id: "memory_write",
3317
- description: "Writes raw byte values into the active C64 memory space.",
3359
+ description: 'Writes raw byte values into the active C64 memory space. Requires emulator to be stopped - call execute(action="pause") first if running.',
3318
3360
  inputSchema: import_zod4.z.object({
3319
3361
  address: address16Schema.describe("Start address in the 16-bit C64 address space"),
3320
3362
  data: byteArraySchema.min(1).describe("Raw bytes to write into memory")
@@ -3430,7 +3472,7 @@ var programLoadTool = createViceTool({
3430
3472
  });
3431
3473
  var captureDisplayTool = createViceTool({
3432
3474
  id: "capture_display",
3433
- description: "Captures the current screen to a PNG file and returns the saved image path. If the emulator was running, the server restores running state before returning, subject to timeout.",
3475
+ description: "Captures the current screen to a PNG file and returns the saved image path.",
3434
3476
  inputSchema: import_zod4.z.object({
3435
3477
  useVic: import_zod4.z.boolean().default(true).describe("Whether to capture the VIC-II display when supported")
3436
3478
  }),
@@ -3439,21 +3481,21 @@ var captureDisplayTool = createViceTool({
3439
3481
  });
3440
3482
  var getDisplayStateTool = createViceTool({
3441
3483
  id: "get_display_state",
3442
- description: "Returns screen RAM, color RAM, the current graphics mode, screen memory addresses, and the current border and background colors. If the emulator was running, the server restores running state before returning, subject to timeout.",
3484
+ description: "Returns screen RAM, color RAM, the current graphics mode, screen memory addresses, and the current border and background colors.",
3443
3485
  inputSchema: noInputSchema,
3444
3486
  dataSchema: displayStateResultSchema,
3445
3487
  execute: async () => await c64Session.getDisplayState()
3446
3488
  });
3447
3489
  var getDisplayTextTool = createViceTool({
3448
3490
  id: "get_display_text",
3449
- description: "Returns the current text screen as readable text when the C64 is in a text mode. If the emulator was running, the server restores running state before returning, subject to timeout.",
3491
+ description: "Returns the current text screen as readable text when the C64 is in a text mode.",
3450
3492
  inputSchema: noInputSchema,
3451
3493
  dataSchema: displayTextResultSchema,
3452
3494
  execute: async () => await c64Session.getDisplayText()
3453
3495
  });
3454
3496
  var writeTextTool = createViceTool({
3455
3497
  id: "write_text",
3456
- description: "Types text into the C64 while it is running. Supports escaped characters and PETSCII brace tokens like {RETURN}, {CLR}, {HOME}, {PI}, and color names; limit each request to 64 bytes.",
3498
+ description: 'Types text into the C64. Requires emulator to be running - call execute(action="resume") first if stopped. Supports escaped characters and PETSCII brace tokens like {RETURN}, {CLR}, {HOME}, {PI}, and color names. Limit 64 bytes per request.',
3457
3499
  inputSchema: import_zod4.z.object({
3458
3500
  text: import_zod4.z.string()
3459
3501
  }),
@@ -3465,7 +3507,7 @@ var writeTextTool = createViceTool({
3465
3507
  });
3466
3508
  var keyboardInputTool = createViceTool({
3467
3509
  id: "keyboard_input",
3468
- description: "Sends one to four keys or PETSCII tokens to the C64 while it is running. Use this for key presses, releases, and taps. If the emulator transiently stops, the server restores running state before returning, subject to timeout.",
3510
+ description: 'Sends one to four keys or PETSCII tokens to the C64. Requires emulator to be running - call execute(action="resume") first if stopped. Use for key presses, releases, and taps.',
3469
3511
  inputSchema: import_zod4.z.object({
3470
3512
  action: inputActionSchema.describe("Use tap for a single key event or press/release for repeated buffered input"),
3471
3513
  keys: import_zod4.z.array(import_zod4.z.string().min(1)).min(1).max(4).describe("One to four literal keys or PETSCII token names such as RETURN, CLR, HOME, PI, LEFT, RED, or F1"),
@@ -3476,7 +3518,7 @@ var keyboardInputTool = createViceTool({
3476
3518
  });
3477
3519
  var joystickInputTool = createViceTool({
3478
3520
  id: "joystick_input",
3479
- description: "Sends joystick input to C64 joystick port 1 or 2 while the C64 is running. If the emulator transiently stops, the server restores running state before returning, subject to timeout.",
3521
+ description: 'Sends joystick input to C64 joystick port 1 or 2. Requires emulator to be running - call execute(action="resume") first if stopped.',
3480
3522
  inputSchema: import_zod4.z.object({
3481
3523
  port: joystickPortSchema.describe("Joystick port number"),
3482
3524
  action: inputActionSchema.describe("Joystick action to apply"),
package/dist/http.js CHANGED
@@ -1347,6 +1347,8 @@ var DEFAULT_INPUT_TAP_MS = 75;
1347
1347
  var DEFAULT_KEYBOARD_REPEAT_MS = 100;
1348
1348
  var VICE_PROCESS_LOG_PATH = path.join(os.tmpdir(), "c64-debug-mcp-x64sc.log");
1349
1349
  var DISPLAY_CAPTURE_DIR = path.resolve(process.cwd(), ".vice-debug-mcp-artifacts");
1350
+ var CLEANUP_ENABLED = !/^(0|false|no|off)$/i.test(process.env.C64_CLEANUP_SCREENSHOTS ?? "");
1351
+ var CLEANUP_MAX_AGE_MINUTES = Number.parseInt(process.env.C64_CLEANUP_MAX_AGE_MINUTES ?? "20", 10);
1350
1352
  var MIRROR_EMULATOR_LOGS_TO_STDERR = /^(1|true|yes|on)$/i.test(process.env.C64_DEBUG_CONSOLE_LOGS ?? "");
1351
1353
  var EXECUTION_EVENT_WAIT_MS = 1e3;
1352
1354
  var EXECUTION_SETTLE_DELAY_MS = 2e3;
@@ -1495,6 +1497,7 @@ var ViceSession = class {
1495
1497
  #displayOperationLock = null;
1496
1498
  constructor(portAllocator = new PortAllocator()) {
1497
1499
  this.#portAllocator = portAllocator;
1500
+ void this.#cleanupOldScreenshots();
1498
1501
  this.#client.on("response", (response) => {
1499
1502
  this.#lastResponseAt = nowIso();
1500
1503
  this.#writeProcessLogLine(`[monitor-response] type=${response.type} requestId=${response.requestId} errorCode=${response.errorCode}`);
@@ -1686,7 +1689,7 @@ var ViceSession = class {
1686
1689
  };
1687
1690
  }
1688
1691
  async readMemory(start, end, bank = 0) {
1689
- await this.#ensurePausedForDebug("memory_read");
1692
+ await this.#ensureReady();
1690
1693
  this.#validateRange(start, end);
1691
1694
  const response = await this.#client.readMemory(start, end, bank);
1692
1695
  return {
@@ -3137,6 +3140,45 @@ var ViceSession = class {
3137
3140
  }
3138
3141
  this.#syncMonitorRuntimeState();
3139
3142
  }
3143
+ async #cleanupOldScreenshots() {
3144
+ if (!CLEANUP_ENABLED) {
3145
+ return;
3146
+ }
3147
+ try {
3148
+ const maxAgeMinutes = Math.max(1, Math.min(525600, CLEANUP_MAX_AGE_MINUTES));
3149
+ const maxAgeMs = maxAgeMinutes * 60 * 1e3;
3150
+ const cutoffTime = Date.now() - maxAgeMs;
3151
+ this.#writeProcessLogLine(`[cleanup] scanning ${DISPLAY_CAPTURE_DIR} for screenshots older than ${maxAgeMinutes}m`);
3152
+ let entries;
3153
+ try {
3154
+ entries = await fs.readdir(DISPLAY_CAPTURE_DIR);
3155
+ } catch (error) {
3156
+ if (error.code === "ENOENT") {
3157
+ return;
3158
+ }
3159
+ throw error;
3160
+ }
3161
+ const pngFiles = entries.filter((name) => name.endsWith(".png") && name.startsWith("capture-"));
3162
+ let deletedCount = 0;
3163
+ let errorCount = 0;
3164
+ for (const filename of pngFiles) {
3165
+ try {
3166
+ const filePath = path.join(DISPLAY_CAPTURE_DIR, filename);
3167
+ const stats = await fs.stat(filePath);
3168
+ if (stats.mtime.getTime() < cutoffTime) {
3169
+ await fs.unlink(filePath);
3170
+ deletedCount++;
3171
+ }
3172
+ } catch (error) {
3173
+ errorCount++;
3174
+ this.#writeProcessLogLine(`[cleanup] failed to delete ${filename}: ${error instanceof Error ? error.message : String(error)}`);
3175
+ }
3176
+ }
3177
+ this.#writeProcessLogLine(`[cleanup] completed: ${deletedCount} deleted, ${errorCount} errors, ${pngFiles.length - deletedCount - errorCount} retained`);
3178
+ } catch (error) {
3179
+ this.#writeProcessLogLine(`[cleanup] failed: ${error instanceof Error ? error.message : String(error)}`);
3180
+ }
3181
+ }
3140
3182
  };
3141
3183
  function splitCommandLine(input) {
3142
3184
  const result = [];
@@ -3239,14 +3281,14 @@ var getMonitorStateTool = createViceTool({
3239
3281
  });
3240
3282
  var getSessionStateTool = createViceTool({
3241
3283
  id: "get_session_state",
3242
- description: "Returns richer emulator session state including transport/process status, auto-resume state, and the most recent hit checkpoint when known.",
3284
+ description: "Returns emulator session state including transport/process status, auto-resume state, and the most recent hit checkpoint.",
3243
3285
  inputSchema: noInputSchema,
3244
3286
  dataSchema: sessionStateResultSchema,
3245
3287
  execute: async () => c64Session.snapshot()
3246
3288
  });
3247
3289
  var getRegistersTool = createViceTool({
3248
3290
  id: "get_registers",
3249
- description: "Returns the current C64 register snapshot. This requires the emulator to already be stopped.",
3291
+ description: 'Returns the current C64 register snapshot. Requires emulator to be stopped - call execute(action="pause") first if running.',
3250
3292
  inputSchema: noInputSchema,
3251
3293
  dataSchema: z3.object({
3252
3294
  registers: c64RegisterValueSchema
@@ -3255,7 +3297,7 @@ var getRegistersTool = createViceTool({
3255
3297
  });
3256
3298
  var setRegistersTool = createViceTool({
3257
3299
  id: "set_registers",
3258
- description: "Sets one or more C64 registers by field name.",
3300
+ description: 'Sets one or more C64 registers by field name. Requires emulator to be stopped - call execute(action="pause") first if running.',
3259
3301
  inputSchema: z3.object({
3260
3302
  registers: c64PartialRegisterValueSchema
3261
3303
  }),
@@ -3291,7 +3333,7 @@ var readMemoryTool = createViceTool({
3291
3333
  });
3292
3334
  var writeMemoryTool = createViceTool({
3293
3335
  id: "memory_write",
3294
- description: "Writes raw byte values into the active C64 memory space.",
3336
+ description: 'Writes raw byte values into the active C64 memory space. Requires emulator to be stopped - call execute(action="pause") first if running.',
3295
3337
  inputSchema: z3.object({
3296
3338
  address: address16Schema.describe("Start address in the 16-bit C64 address space"),
3297
3339
  data: byteArraySchema.min(1).describe("Raw bytes to write into memory")
@@ -3407,7 +3449,7 @@ var programLoadTool = createViceTool({
3407
3449
  });
3408
3450
  var captureDisplayTool = createViceTool({
3409
3451
  id: "capture_display",
3410
- description: "Captures the current screen to a PNG file and returns the saved image path. If the emulator was running, the server restores running state before returning, subject to timeout.",
3452
+ description: "Captures the current screen to a PNG file and returns the saved image path.",
3411
3453
  inputSchema: z3.object({
3412
3454
  useVic: z3.boolean().default(true).describe("Whether to capture the VIC-II display when supported")
3413
3455
  }),
@@ -3416,21 +3458,21 @@ var captureDisplayTool = createViceTool({
3416
3458
  });
3417
3459
  var getDisplayStateTool = createViceTool({
3418
3460
  id: "get_display_state",
3419
- description: "Returns screen RAM, color RAM, the current graphics mode, screen memory addresses, and the current border and background colors. If the emulator was running, the server restores running state before returning, subject to timeout.",
3461
+ description: "Returns screen RAM, color RAM, the current graphics mode, screen memory addresses, and the current border and background colors.",
3420
3462
  inputSchema: noInputSchema,
3421
3463
  dataSchema: displayStateResultSchema,
3422
3464
  execute: async () => await c64Session.getDisplayState()
3423
3465
  });
3424
3466
  var getDisplayTextTool = createViceTool({
3425
3467
  id: "get_display_text",
3426
- description: "Returns the current text screen as readable text when the C64 is in a text mode. If the emulator was running, the server restores running state before returning, subject to timeout.",
3468
+ description: "Returns the current text screen as readable text when the C64 is in a text mode.",
3427
3469
  inputSchema: noInputSchema,
3428
3470
  dataSchema: displayTextResultSchema,
3429
3471
  execute: async () => await c64Session.getDisplayText()
3430
3472
  });
3431
3473
  var writeTextTool = createViceTool({
3432
3474
  id: "write_text",
3433
- description: "Types text into the C64 while it is running. Supports escaped characters and PETSCII brace tokens like {RETURN}, {CLR}, {HOME}, {PI}, and color names; limit each request to 64 bytes.",
3475
+ description: 'Types text into the C64. Requires emulator to be running - call execute(action="resume") first if stopped. Supports escaped characters and PETSCII brace tokens like {RETURN}, {CLR}, {HOME}, {PI}, and color names. Limit 64 bytes per request.',
3434
3476
  inputSchema: z3.object({
3435
3477
  text: z3.string()
3436
3478
  }),
@@ -3442,7 +3484,7 @@ var writeTextTool = createViceTool({
3442
3484
  });
3443
3485
  var keyboardInputTool = createViceTool({
3444
3486
  id: "keyboard_input",
3445
- description: "Sends one to four keys or PETSCII tokens to the C64 while it is running. Use this for key presses, releases, and taps. If the emulator transiently stops, the server restores running state before returning, subject to timeout.",
3487
+ description: 'Sends one to four keys or PETSCII tokens to the C64. Requires emulator to be running - call execute(action="resume") first if stopped. Use for key presses, releases, and taps.',
3446
3488
  inputSchema: z3.object({
3447
3489
  action: inputActionSchema.describe("Use tap for a single key event or press/release for repeated buffered input"),
3448
3490
  keys: z3.array(z3.string().min(1)).min(1).max(4).describe("One to four literal keys or PETSCII token names such as RETURN, CLR, HOME, PI, LEFT, RED, or F1"),
@@ -3453,7 +3495,7 @@ var keyboardInputTool = createViceTool({
3453
3495
  });
3454
3496
  var joystickInputTool = createViceTool({
3455
3497
  id: "joystick_input",
3456
- description: "Sends joystick input to C64 joystick port 1 or 2 while the C64 is running. If the emulator transiently stops, the server restores running state before returning, subject to timeout.",
3498
+ description: 'Sends joystick input to C64 joystick port 1 or 2. Requires emulator to be running - call execute(action="resume") first if stopped.',
3457
3499
  inputSchema: z3.object({
3458
3500
  port: joystickPortSchema.describe("Joystick port number"),
3459
3501
  action: inputActionSchema.describe("Joystick action to apply"),
package/dist/stdio.cjs CHANGED
@@ -1367,6 +1367,8 @@ var DEFAULT_INPUT_TAP_MS = 75;
1367
1367
  var DEFAULT_KEYBOARD_REPEAT_MS = 100;
1368
1368
  var VICE_PROCESS_LOG_PATH = import_node_path.default.join(import_node_os.default.tmpdir(), "c64-debug-mcp-x64sc.log");
1369
1369
  var DISPLAY_CAPTURE_DIR = import_node_path.default.resolve(process.cwd(), ".vice-debug-mcp-artifacts");
1370
+ var CLEANUP_ENABLED = !/^(0|false|no|off)$/i.test(process.env.C64_CLEANUP_SCREENSHOTS ?? "");
1371
+ var CLEANUP_MAX_AGE_MINUTES = Number.parseInt(process.env.C64_CLEANUP_MAX_AGE_MINUTES ?? "20", 10);
1370
1372
  var MIRROR_EMULATOR_LOGS_TO_STDERR = /^(1|true|yes|on)$/i.test(process.env.C64_DEBUG_CONSOLE_LOGS ?? "");
1371
1373
  var EXECUTION_EVENT_WAIT_MS = 1e3;
1372
1374
  var EXECUTION_SETTLE_DELAY_MS = 2e3;
@@ -1515,6 +1517,7 @@ var ViceSession = class {
1515
1517
  #displayOperationLock = null;
1516
1518
  constructor(portAllocator = new PortAllocator()) {
1517
1519
  this.#portAllocator = portAllocator;
1520
+ void this.#cleanupOldScreenshots();
1518
1521
  this.#client.on("response", (response) => {
1519
1522
  this.#lastResponseAt = nowIso();
1520
1523
  this.#writeProcessLogLine(`[monitor-response] type=${response.type} requestId=${response.requestId} errorCode=${response.errorCode}`);
@@ -1706,7 +1709,7 @@ var ViceSession = class {
1706
1709
  };
1707
1710
  }
1708
1711
  async readMemory(start, end, bank = 0) {
1709
- await this.#ensurePausedForDebug("memory_read");
1712
+ await this.#ensureReady();
1710
1713
  this.#validateRange(start, end);
1711
1714
  const response = await this.#client.readMemory(start, end, bank);
1712
1715
  return {
@@ -3157,6 +3160,45 @@ var ViceSession = class {
3157
3160
  }
3158
3161
  this.#syncMonitorRuntimeState();
3159
3162
  }
3163
+ async #cleanupOldScreenshots() {
3164
+ if (!CLEANUP_ENABLED) {
3165
+ return;
3166
+ }
3167
+ try {
3168
+ const maxAgeMinutes = Math.max(1, Math.min(525600, CLEANUP_MAX_AGE_MINUTES));
3169
+ const maxAgeMs = maxAgeMinutes * 60 * 1e3;
3170
+ const cutoffTime = Date.now() - maxAgeMs;
3171
+ this.#writeProcessLogLine(`[cleanup] scanning ${DISPLAY_CAPTURE_DIR} for screenshots older than ${maxAgeMinutes}m`);
3172
+ let entries;
3173
+ try {
3174
+ entries = await import_promises.default.readdir(DISPLAY_CAPTURE_DIR);
3175
+ } catch (error) {
3176
+ if (error.code === "ENOENT") {
3177
+ return;
3178
+ }
3179
+ throw error;
3180
+ }
3181
+ const pngFiles = entries.filter((name) => name.endsWith(".png") && name.startsWith("capture-"));
3182
+ let deletedCount = 0;
3183
+ let errorCount = 0;
3184
+ for (const filename of pngFiles) {
3185
+ try {
3186
+ const filePath = import_node_path.default.join(DISPLAY_CAPTURE_DIR, filename);
3187
+ const stats = await import_promises.default.stat(filePath);
3188
+ if (stats.mtime.getTime() < cutoffTime) {
3189
+ await import_promises.default.unlink(filePath);
3190
+ deletedCount++;
3191
+ }
3192
+ } catch (error) {
3193
+ errorCount++;
3194
+ this.#writeProcessLogLine(`[cleanup] failed to delete ${filename}: ${error instanceof Error ? error.message : String(error)}`);
3195
+ }
3196
+ }
3197
+ this.#writeProcessLogLine(`[cleanup] completed: ${deletedCount} deleted, ${errorCount} errors, ${pngFiles.length - deletedCount - errorCount} retained`);
3198
+ } catch (error) {
3199
+ this.#writeProcessLogLine(`[cleanup] failed: ${error instanceof Error ? error.message : String(error)}`);
3200
+ }
3201
+ }
3160
3202
  };
3161
3203
  function splitCommandLine(input) {
3162
3204
  const result = [];
@@ -3259,14 +3301,14 @@ var getMonitorStateTool = createViceTool({
3259
3301
  });
3260
3302
  var getSessionStateTool = createViceTool({
3261
3303
  id: "get_session_state",
3262
- description: "Returns richer emulator session state including transport/process status, auto-resume state, and the most recent hit checkpoint when known.",
3304
+ description: "Returns emulator session state including transport/process status, auto-resume state, and the most recent hit checkpoint.",
3263
3305
  inputSchema: noInputSchema,
3264
3306
  dataSchema: sessionStateResultSchema,
3265
3307
  execute: async () => c64Session.snapshot()
3266
3308
  });
3267
3309
  var getRegistersTool = createViceTool({
3268
3310
  id: "get_registers",
3269
- description: "Returns the current C64 register snapshot. This requires the emulator to already be stopped.",
3311
+ description: 'Returns the current C64 register snapshot. Requires emulator to be stopped - call execute(action="pause") first if running.',
3270
3312
  inputSchema: noInputSchema,
3271
3313
  dataSchema: import_zod4.z.object({
3272
3314
  registers: c64RegisterValueSchema
@@ -3275,7 +3317,7 @@ var getRegistersTool = createViceTool({
3275
3317
  });
3276
3318
  var setRegistersTool = createViceTool({
3277
3319
  id: "set_registers",
3278
- description: "Sets one or more C64 registers by field name.",
3320
+ description: 'Sets one or more C64 registers by field name. Requires emulator to be stopped - call execute(action="pause") first if running.',
3279
3321
  inputSchema: import_zod4.z.object({
3280
3322
  registers: c64PartialRegisterValueSchema
3281
3323
  }),
@@ -3311,7 +3353,7 @@ var readMemoryTool = createViceTool({
3311
3353
  });
3312
3354
  var writeMemoryTool = createViceTool({
3313
3355
  id: "memory_write",
3314
- description: "Writes raw byte values into the active C64 memory space.",
3356
+ description: 'Writes raw byte values into the active C64 memory space. Requires emulator to be stopped - call execute(action="pause") first if running.',
3315
3357
  inputSchema: import_zod4.z.object({
3316
3358
  address: address16Schema.describe("Start address in the 16-bit C64 address space"),
3317
3359
  data: byteArraySchema.min(1).describe("Raw bytes to write into memory")
@@ -3427,7 +3469,7 @@ var programLoadTool = createViceTool({
3427
3469
  });
3428
3470
  var captureDisplayTool = createViceTool({
3429
3471
  id: "capture_display",
3430
- description: "Captures the current screen to a PNG file and returns the saved image path. If the emulator was running, the server restores running state before returning, subject to timeout.",
3472
+ description: "Captures the current screen to a PNG file and returns the saved image path.",
3431
3473
  inputSchema: import_zod4.z.object({
3432
3474
  useVic: import_zod4.z.boolean().default(true).describe("Whether to capture the VIC-II display when supported")
3433
3475
  }),
@@ -3436,21 +3478,21 @@ var captureDisplayTool = createViceTool({
3436
3478
  });
3437
3479
  var getDisplayStateTool = createViceTool({
3438
3480
  id: "get_display_state",
3439
- description: "Returns screen RAM, color RAM, the current graphics mode, screen memory addresses, and the current border and background colors. If the emulator was running, the server restores running state before returning, subject to timeout.",
3481
+ description: "Returns screen RAM, color RAM, the current graphics mode, screen memory addresses, and the current border and background colors.",
3440
3482
  inputSchema: noInputSchema,
3441
3483
  dataSchema: displayStateResultSchema,
3442
3484
  execute: async () => await c64Session.getDisplayState()
3443
3485
  });
3444
3486
  var getDisplayTextTool = createViceTool({
3445
3487
  id: "get_display_text",
3446
- description: "Returns the current text screen as readable text when the C64 is in a text mode. If the emulator was running, the server restores running state before returning, subject to timeout.",
3488
+ description: "Returns the current text screen as readable text when the C64 is in a text mode.",
3447
3489
  inputSchema: noInputSchema,
3448
3490
  dataSchema: displayTextResultSchema,
3449
3491
  execute: async () => await c64Session.getDisplayText()
3450
3492
  });
3451
3493
  var writeTextTool = createViceTool({
3452
3494
  id: "write_text",
3453
- description: "Types text into the C64 while it is running. Supports escaped characters and PETSCII brace tokens like {RETURN}, {CLR}, {HOME}, {PI}, and color names; limit each request to 64 bytes.",
3495
+ description: 'Types text into the C64. Requires emulator to be running - call execute(action="resume") first if stopped. Supports escaped characters and PETSCII brace tokens like {RETURN}, {CLR}, {HOME}, {PI}, and color names. Limit 64 bytes per request.',
3454
3496
  inputSchema: import_zod4.z.object({
3455
3497
  text: import_zod4.z.string()
3456
3498
  }),
@@ -3462,7 +3504,7 @@ var writeTextTool = createViceTool({
3462
3504
  });
3463
3505
  var keyboardInputTool = createViceTool({
3464
3506
  id: "keyboard_input",
3465
- description: "Sends one to four keys or PETSCII tokens to the C64 while it is running. Use this for key presses, releases, and taps. If the emulator transiently stops, the server restores running state before returning, subject to timeout.",
3507
+ description: 'Sends one to four keys or PETSCII tokens to the C64. Requires emulator to be running - call execute(action="resume") first if stopped. Use for key presses, releases, and taps.',
3466
3508
  inputSchema: import_zod4.z.object({
3467
3509
  action: inputActionSchema.describe("Use tap for a single key event or press/release for repeated buffered input"),
3468
3510
  keys: import_zod4.z.array(import_zod4.z.string().min(1)).min(1).max(4).describe("One to four literal keys or PETSCII token names such as RETURN, CLR, HOME, PI, LEFT, RED, or F1"),
@@ -3473,7 +3515,7 @@ var keyboardInputTool = createViceTool({
3473
3515
  });
3474
3516
  var joystickInputTool = createViceTool({
3475
3517
  id: "joystick_input",
3476
- description: "Sends joystick input to C64 joystick port 1 or 2 while the C64 is running. If the emulator transiently stops, the server restores running state before returning, subject to timeout.",
3518
+ description: 'Sends joystick input to C64 joystick port 1 or 2. Requires emulator to be running - call execute(action="resume") first if stopped.',
3477
3519
  inputSchema: import_zod4.z.object({
3478
3520
  port: joystickPortSchema.describe("Joystick port number"),
3479
3521
  action: inputActionSchema.describe("Joystick action to apply"),
package/dist/stdio.js CHANGED
@@ -1344,6 +1344,8 @@ var DEFAULT_INPUT_TAP_MS = 75;
1344
1344
  var DEFAULT_KEYBOARD_REPEAT_MS = 100;
1345
1345
  var VICE_PROCESS_LOG_PATH = path.join(os.tmpdir(), "c64-debug-mcp-x64sc.log");
1346
1346
  var DISPLAY_CAPTURE_DIR = path.resolve(process.cwd(), ".vice-debug-mcp-artifacts");
1347
+ var CLEANUP_ENABLED = !/^(0|false|no|off)$/i.test(process.env.C64_CLEANUP_SCREENSHOTS ?? "");
1348
+ var CLEANUP_MAX_AGE_MINUTES = Number.parseInt(process.env.C64_CLEANUP_MAX_AGE_MINUTES ?? "20", 10);
1347
1349
  var MIRROR_EMULATOR_LOGS_TO_STDERR = /^(1|true|yes|on)$/i.test(process.env.C64_DEBUG_CONSOLE_LOGS ?? "");
1348
1350
  var EXECUTION_EVENT_WAIT_MS = 1e3;
1349
1351
  var EXECUTION_SETTLE_DELAY_MS = 2e3;
@@ -1492,6 +1494,7 @@ var ViceSession = class {
1492
1494
  #displayOperationLock = null;
1493
1495
  constructor(portAllocator = new PortAllocator()) {
1494
1496
  this.#portAllocator = portAllocator;
1497
+ void this.#cleanupOldScreenshots();
1495
1498
  this.#client.on("response", (response) => {
1496
1499
  this.#lastResponseAt = nowIso();
1497
1500
  this.#writeProcessLogLine(`[monitor-response] type=${response.type} requestId=${response.requestId} errorCode=${response.errorCode}`);
@@ -1683,7 +1686,7 @@ var ViceSession = class {
1683
1686
  };
1684
1687
  }
1685
1688
  async readMemory(start, end, bank = 0) {
1686
- await this.#ensurePausedForDebug("memory_read");
1689
+ await this.#ensureReady();
1687
1690
  this.#validateRange(start, end);
1688
1691
  const response = await this.#client.readMemory(start, end, bank);
1689
1692
  return {
@@ -3134,6 +3137,45 @@ var ViceSession = class {
3134
3137
  }
3135
3138
  this.#syncMonitorRuntimeState();
3136
3139
  }
3140
+ async #cleanupOldScreenshots() {
3141
+ if (!CLEANUP_ENABLED) {
3142
+ return;
3143
+ }
3144
+ try {
3145
+ const maxAgeMinutes = Math.max(1, Math.min(525600, CLEANUP_MAX_AGE_MINUTES));
3146
+ const maxAgeMs = maxAgeMinutes * 60 * 1e3;
3147
+ const cutoffTime = Date.now() - maxAgeMs;
3148
+ this.#writeProcessLogLine(`[cleanup] scanning ${DISPLAY_CAPTURE_DIR} for screenshots older than ${maxAgeMinutes}m`);
3149
+ let entries;
3150
+ try {
3151
+ entries = await fs.readdir(DISPLAY_CAPTURE_DIR);
3152
+ } catch (error) {
3153
+ if (error.code === "ENOENT") {
3154
+ return;
3155
+ }
3156
+ throw error;
3157
+ }
3158
+ const pngFiles = entries.filter((name) => name.endsWith(".png") && name.startsWith("capture-"));
3159
+ let deletedCount = 0;
3160
+ let errorCount = 0;
3161
+ for (const filename of pngFiles) {
3162
+ try {
3163
+ const filePath = path.join(DISPLAY_CAPTURE_DIR, filename);
3164
+ const stats = await fs.stat(filePath);
3165
+ if (stats.mtime.getTime() < cutoffTime) {
3166
+ await fs.unlink(filePath);
3167
+ deletedCount++;
3168
+ }
3169
+ } catch (error) {
3170
+ errorCount++;
3171
+ this.#writeProcessLogLine(`[cleanup] failed to delete ${filename}: ${error instanceof Error ? error.message : String(error)}`);
3172
+ }
3173
+ }
3174
+ this.#writeProcessLogLine(`[cleanup] completed: ${deletedCount} deleted, ${errorCount} errors, ${pngFiles.length - deletedCount - errorCount} retained`);
3175
+ } catch (error) {
3176
+ this.#writeProcessLogLine(`[cleanup] failed: ${error instanceof Error ? error.message : String(error)}`);
3177
+ }
3178
+ }
3137
3179
  };
3138
3180
  function splitCommandLine(input) {
3139
3181
  const result = [];
@@ -3236,14 +3278,14 @@ var getMonitorStateTool = createViceTool({
3236
3278
  });
3237
3279
  var getSessionStateTool = createViceTool({
3238
3280
  id: "get_session_state",
3239
- description: "Returns richer emulator session state including transport/process status, auto-resume state, and the most recent hit checkpoint when known.",
3281
+ description: "Returns emulator session state including transport/process status, auto-resume state, and the most recent hit checkpoint.",
3240
3282
  inputSchema: noInputSchema,
3241
3283
  dataSchema: sessionStateResultSchema,
3242
3284
  execute: async () => c64Session.snapshot()
3243
3285
  });
3244
3286
  var getRegistersTool = createViceTool({
3245
3287
  id: "get_registers",
3246
- description: "Returns the current C64 register snapshot. This requires the emulator to already be stopped.",
3288
+ description: 'Returns the current C64 register snapshot. Requires emulator to be stopped - call execute(action="pause") first if running.',
3247
3289
  inputSchema: noInputSchema,
3248
3290
  dataSchema: z3.object({
3249
3291
  registers: c64RegisterValueSchema
@@ -3252,7 +3294,7 @@ var getRegistersTool = createViceTool({
3252
3294
  });
3253
3295
  var setRegistersTool = createViceTool({
3254
3296
  id: "set_registers",
3255
- description: "Sets one or more C64 registers by field name.",
3297
+ description: 'Sets one or more C64 registers by field name. Requires emulator to be stopped - call execute(action="pause") first if running.',
3256
3298
  inputSchema: z3.object({
3257
3299
  registers: c64PartialRegisterValueSchema
3258
3300
  }),
@@ -3288,7 +3330,7 @@ var readMemoryTool = createViceTool({
3288
3330
  });
3289
3331
  var writeMemoryTool = createViceTool({
3290
3332
  id: "memory_write",
3291
- description: "Writes raw byte values into the active C64 memory space.",
3333
+ description: 'Writes raw byte values into the active C64 memory space. Requires emulator to be stopped - call execute(action="pause") first if running.',
3292
3334
  inputSchema: z3.object({
3293
3335
  address: address16Schema.describe("Start address in the 16-bit C64 address space"),
3294
3336
  data: byteArraySchema.min(1).describe("Raw bytes to write into memory")
@@ -3404,7 +3446,7 @@ var programLoadTool = createViceTool({
3404
3446
  });
3405
3447
  var captureDisplayTool = createViceTool({
3406
3448
  id: "capture_display",
3407
- description: "Captures the current screen to a PNG file and returns the saved image path. If the emulator was running, the server restores running state before returning, subject to timeout.",
3449
+ description: "Captures the current screen to a PNG file and returns the saved image path.",
3408
3450
  inputSchema: z3.object({
3409
3451
  useVic: z3.boolean().default(true).describe("Whether to capture the VIC-II display when supported")
3410
3452
  }),
@@ -3413,21 +3455,21 @@ var captureDisplayTool = createViceTool({
3413
3455
  });
3414
3456
  var getDisplayStateTool = createViceTool({
3415
3457
  id: "get_display_state",
3416
- description: "Returns screen RAM, color RAM, the current graphics mode, screen memory addresses, and the current border and background colors. If the emulator was running, the server restores running state before returning, subject to timeout.",
3458
+ description: "Returns screen RAM, color RAM, the current graphics mode, screen memory addresses, and the current border and background colors.",
3417
3459
  inputSchema: noInputSchema,
3418
3460
  dataSchema: displayStateResultSchema,
3419
3461
  execute: async () => await c64Session.getDisplayState()
3420
3462
  });
3421
3463
  var getDisplayTextTool = createViceTool({
3422
3464
  id: "get_display_text",
3423
- description: "Returns the current text screen as readable text when the C64 is in a text mode. If the emulator was running, the server restores running state before returning, subject to timeout.",
3465
+ description: "Returns the current text screen as readable text when the C64 is in a text mode.",
3424
3466
  inputSchema: noInputSchema,
3425
3467
  dataSchema: displayTextResultSchema,
3426
3468
  execute: async () => await c64Session.getDisplayText()
3427
3469
  });
3428
3470
  var writeTextTool = createViceTool({
3429
3471
  id: "write_text",
3430
- description: "Types text into the C64 while it is running. Supports escaped characters and PETSCII brace tokens like {RETURN}, {CLR}, {HOME}, {PI}, and color names; limit each request to 64 bytes.",
3472
+ description: 'Types text into the C64. Requires emulator to be running - call execute(action="resume") first if stopped. Supports escaped characters and PETSCII brace tokens like {RETURN}, {CLR}, {HOME}, {PI}, and color names. Limit 64 bytes per request.',
3431
3473
  inputSchema: z3.object({
3432
3474
  text: z3.string()
3433
3475
  }),
@@ -3439,7 +3481,7 @@ var writeTextTool = createViceTool({
3439
3481
  });
3440
3482
  var keyboardInputTool = createViceTool({
3441
3483
  id: "keyboard_input",
3442
- description: "Sends one to four keys or PETSCII tokens to the C64 while it is running. Use this for key presses, releases, and taps. If the emulator transiently stops, the server restores running state before returning, subject to timeout.",
3484
+ description: 'Sends one to four keys or PETSCII tokens to the C64. Requires emulator to be running - call execute(action="resume") first if stopped. Use for key presses, releases, and taps.',
3443
3485
  inputSchema: z3.object({
3444
3486
  action: inputActionSchema.describe("Use tap for a single key event or press/release for repeated buffered input"),
3445
3487
  keys: z3.array(z3.string().min(1)).min(1).max(4).describe("One to four literal keys or PETSCII token names such as RETURN, CLR, HOME, PI, LEFT, RED, or F1"),
@@ -3450,7 +3492,7 @@ var keyboardInputTool = createViceTool({
3450
3492
  });
3451
3493
  var joystickInputTool = createViceTool({
3452
3494
  id: "joystick_input",
3453
- description: "Sends joystick input to C64 joystick port 1 or 2 while the C64 is running. If the emulator transiently stops, the server restores running state before returning, subject to timeout.",
3495
+ description: 'Sends joystick input to C64 joystick port 1 or 2. Requires emulator to be running - call execute(action="resume") first if stopped.',
3454
3496
  inputSchema: z3.object({
3455
3497
  port: joystickPortSchema.describe("Joystick port number"),
3456
3498
  action: inputActionSchema.describe("Joystick action to apply"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c64-debug-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Model Context Protocol server for C64 debugging via VICE emulator",
5
5
  "type": "module",
6
6
  "keywords": [