c64bridge 0.9.3 → 1.0.1

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.
Files changed (65) hide show
  1. package/AGENTS.md +19 -3
  2. package/README.md +190 -148
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/c64Client.js +277 -41
  5. package/dist/config.js +30 -18
  6. package/dist/device.js +191 -125
  7. package/dist/knowledge.js +1 -1
  8. package/dist/loggingHttpClient.js +15 -3
  9. package/dist/mcp-server.js +65 -16
  10. package/dist/platform.js +13 -2
  11. package/dist/rag/indexer.js +11 -8
  12. package/dist/rag/init.js +11 -8
  13. package/dist/sidwaveCompiler.js +2 -2
  14. package/dist/tools/audio.js +27 -6
  15. package/dist/tools/developer.js +6 -6
  16. package/dist/tools/graphics.js +4 -4
  17. package/dist/tools/input.js +136 -24
  18. package/dist/tools/machineControl.js +85 -6
  19. package/dist/tools/memory.js +21 -6
  20. package/dist/tools/meta/artifacts.js +20 -8
  21. package/dist/tools/meta/background.js +11 -1
  22. package/dist/tools/meta/index.js +1 -1
  23. package/dist/tools/pollValidator.js +19 -51
  24. package/dist/tools/printer.js +1 -0
  25. package/dist/tools/programRunners.js +5 -5
  26. package/dist/tools/rag.js +1 -1
  27. package/dist/tools/registry/batch.js +1 -1
  28. package/dist/tools/registry/config.js +5 -5
  29. package/dist/tools/registry/disk.js +2 -2
  30. package/dist/tools/registry/drive.js +2 -2
  31. package/dist/tools/registry/graphics.js +2 -2
  32. package/dist/tools/registry/memory.js +2 -2
  33. package/dist/tools/registry/platform.js +9 -5
  34. package/dist/tools/registry/printer.js +1 -0
  35. package/dist/tools/registry/program.js +5 -5
  36. package/dist/tools/registry/rag.js +1 -1
  37. package/dist/tools/registry/sound.js +4 -4
  38. package/dist/tools/registry/system.js +13 -3
  39. package/dist/tools/storage.js +1 -1
  40. package/dist/vice/mockServer.js +33 -0
  41. package/dist/vice/process.js +11 -1
  42. package/dist/vice/viceClient.js +35 -6
  43. package/doc/c64u/c64-openapi.yaml +1130 -774
  44. package/doc/reviews/hardening/01-fable/prompt-handover.md +125 -0
  45. package/doc/reviews/hardening/01-fable/prompt.md +98 -0
  46. package/doc/reviews/hardening/01-fable/review-prompt.md +468 -0
  47. package/doc/reviews/hardening/01-fable/review.md +1234 -0
  48. package/doc/u2/u2-openapi.yaml +1192 -0
  49. package/generated/c64/index.js +142 -9
  50. package/generated/openapi/c64u/index.js +1117 -0
  51. package/generated/openapi/u2/index.js +929 -0
  52. package/mcp/schemas/c64_graphics.schema.json +3 -3
  53. package/mcp/schemas/c64_input.schema.json +249 -8
  54. package/mcp/schemas/c64_memory.schema.json +3 -3
  55. package/mcp/schemas/c64_program.schema.json +3 -3
  56. package/mcp/schemas/c64_select_backend.schema.json +2 -0
  57. package/mcp/schemas/c64_system.schema.json +41 -1
  58. package/mcp/server.json +1 -1
  59. package/mcp/tools.json +386 -44
  60. package/mcp.json +14 -2
  61. package/package.json +4 -2
  62. package/scripts/mockC64Server.mjs +171 -2
  63. package/scripts/prepare-release.mjs +13 -2
  64. package/scripts/update-readme.ts +18 -30
  65. package/server.json +3 -3
@@ -53,6 +53,8 @@ export class ViceClient {
53
53
  }
54
54
  rejectAllPending(reason) {
55
55
  for (const [, pending] of this.pending) {
56
+ if (pending.timer)
57
+ clearTimeout(pending.timer);
56
58
  try {
57
59
  pending.reject(reason);
58
60
  }
@@ -66,7 +68,7 @@ export class ViceClient {
66
68
  // Frame: [0]=0x02, [1]=0x02, [2..5]=len, [6]=respType, [7]=err, [8..11]=reqId, [12..]=body
67
69
  while (this.buffer.length >= 12) {
68
70
  if (this.buffer[0] !== 0x02 || this.buffer[1] !== 0x02) {
69
- const idx = this.buffer.indexOf(0x02, 1);
71
+ const idx = this.buffer.indexOf(Buffer.from([0x02, 0x02]), 1);
70
72
  this.buffer = idx === -1 ? Buffer.alloc(0) : this.buffer.subarray(idx);
71
73
  continue;
72
74
  }
@@ -88,6 +90,8 @@ export class ViceClient {
88
90
  continue;
89
91
  }
90
92
  if (err !== 0x00) {
93
+ if (pending.timer)
94
+ clearTimeout(pending.timer);
91
95
  pending.reject(new Error(`BM error 0x${err.toString(16)}`));
92
96
  this.pending.delete(reqId);
93
97
  continue;
@@ -98,6 +102,8 @@ export class ViceClient {
98
102
  pending.onFrame(responseType, frame);
99
103
  }
100
104
  catch (error) {
105
+ if (pending.timer)
106
+ clearTimeout(pending.timer);
101
107
  pending.reject(error);
102
108
  this.pending.delete(reqId);
103
109
  }
@@ -108,6 +114,8 @@ export class ViceClient {
108
114
  continue;
109
115
  }
110
116
  pending.resolve(frame);
117
+ if (pending.timer)
118
+ clearTimeout(pending.timer);
111
119
  this.pending.delete(reqId);
112
120
  }
113
121
  }
@@ -115,7 +123,10 @@ export class ViceClient {
115
123
  if (!this.socket || this.socket.destroyed) {
116
124
  return Promise.reject(new Error("VICE monitor socket is not connected"));
117
125
  }
118
- const reqId = this.nextReqId++;
126
+ if (this.nextReqId === 0xffffffff) { // Reserved for unsolicited events (see onData above); skip it on wraparound.
127
+ this.nextReqId = 0;
128
+ }
129
+ const reqId = this.nextReqId++ >>> 0;
119
130
  const payload = body ?? Buffer.alloc(0);
120
131
  const header = Buffer.alloc(11);
121
132
  header[0] = 0x02; // STX
@@ -126,12 +137,20 @@ export class ViceClient {
126
137
  const packet = Buffer.concat([header, payload]);
127
138
  const expectedCmd = options?.responseType ?? cmd;
128
139
  const promise = new Promise((resolve, reject) => {
129
- this.pending.set(reqId, {
140
+ const pending = {
130
141
  cmd: expectedCmd,
131
142
  resolve,
132
143
  reject,
133
144
  onFrame: options?.onFrame,
134
- });
145
+ };
146
+ const timeoutMs = options?.timeoutMs ?? (cmd === 0x84 ? 30000 : 10000);
147
+ pending.timer = setTimeout(() => {
148
+ if (!this.pending.delete(reqId))
149
+ return;
150
+ reject(new Error(`VICE monitor request 0x${cmd.toString(16)} timed out after ${timeoutMs}ms`));
151
+ this.socket?.destroy();
152
+ }, timeoutMs);
153
+ this.pending.set(reqId, pending);
135
154
  });
136
155
  this.socket.write(packet);
137
156
  return promise;
@@ -178,6 +197,13 @@ export class ViceClient {
178
197
  const body = Buffer.concat([Buffer.from([bytes.length & 0xff]), bytes]);
179
198
  await this.send(0x72, body);
180
199
  }
200
+ /** Set the VICE joyport's active-low control byte (BM command 0xA2). */
201
+ async joyportSet(port, activeLowMask) {
202
+ const body = Buffer.alloc(4);
203
+ body.writeUInt16LE(port, 0);
204
+ body.writeUInt16LE(activeLowMask & 0xff, 2);
205
+ await this.send(0xA2, body);
206
+ }
181
207
  async checkpointGet(id) {
182
208
  const body = Buffer.alloc(4);
183
209
  body.writeUInt32LE(id >>> 0, 0);
@@ -189,8 +215,11 @@ export class ViceClient {
189
215
  const end = (options.end ?? options.start) & 0xffff;
190
216
  const stop = options.stopOnHit !== false;
191
217
  const enabled = options.enabled !== false;
192
- const ops = options.operations ?? { execute: true };
193
- const mask = (ops.load ? 0x01 : 0) | (ops.store ? 0x02 : 0) | (ops.execute === false ? 0 : 0x04);
218
+ const ops = options.operations;
219
+ const hasExplicitOperation = ops && (ops.load !== undefined || ops.store !== undefined || ops.execute !== undefined);
220
+ const mask = hasExplicitOperation
221
+ ? ((ops?.load ? 0x01 : 0) | (ops?.store ? 0x02 : 0) | (ops?.execute ? 0x04 : 0))
222
+ : 0x04;
194
223
  const temporary = options.temporary === true;
195
224
  const memspace = options.memspace ?? 0;
196
225
  const hasMemspace = options.memspace !== undefined;