toolcraft 0.0.25 → 0.0.26

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 (32) hide show
  1. package/dist/cli.js +11 -9
  2. package/dist/error-report.js +14 -11
  3. package/dist/redaction.d.ts +4 -0
  4. package/dist/redaction.js +70 -0
  5. package/node_modules/@poe-code/config-mutations/dist/execution/apply-mutation.js +33 -9
  6. package/node_modules/@poe-code/config-mutations/dist/formats/json.d.ts +2 -1
  7. package/node_modules/@poe-code/config-mutations/dist/formats/json.js +36 -9
  8. package/node_modules/@poe-code/config-mutations/dist/types.d.ts +2 -0
  9. package/node_modules/@poe-code/design-system/dist/components/browser.js +1 -1
  10. package/node_modules/@poe-code/design-system/dist/explorer/actions.js +1 -1
  11. package/node_modules/@poe-code/design-system/dist/explorer/keymap.js +11 -1
  12. package/node_modules/@poe-code/design-system/dist/explorer/reducer.js +64 -8
  13. package/node_modules/@poe-code/design-system/dist/explorer/render/detail.js +9 -11
  14. package/node_modules/@poe-code/design-system/dist/explorer/render/footer.js +18 -8
  15. package/node_modules/@poe-code/design-system/dist/explorer/render/header.js +11 -18
  16. package/node_modules/@poe-code/design-system/dist/explorer/render/index.js +2 -10
  17. package/node_modules/@poe-code/design-system/dist/explorer/render/list.js +32 -22
  18. package/node_modules/@poe-code/design-system/dist/explorer/render/modal.js +5 -9
  19. package/node_modules/@poe-code/design-system/dist/explorer/render/text.d.ts +12 -0
  20. package/node_modules/@poe-code/design-system/dist/explorer/render/text.js +81 -0
  21. package/node_modules/@poe-code/design-system/dist/explorer/state.d.ts +1 -0
  22. package/node_modules/@poe-code/design-system/dist/explorer/state.js +2 -0
  23. package/node_modules/@poe-code/design-system/dist/prompts/index.js +3 -3
  24. package/node_modules/@poe-code/process-runner/dist/docker/docker-execution-env.js +24 -3
  25. package/node_modules/@poe-code/process-runner/dist/docker/docker-runner.js +1 -0
  26. package/node_modules/auth-store/dist/keychain-store.js +20 -1
  27. package/node_modules/mcp-oauth/dist/client/auth-store-session-store.js +6 -3
  28. package/node_modules/tiny-mcp-client/dist/internal.d.ts +2 -0
  29. package/node_modules/tiny-mcp-client/dist/internal.js +30 -13
  30. package/node_modules/tiny-mcp-client/src/internal.ts +35 -16
  31. package/node_modules/tiny-mcp-client/src/transports.test.ts +68 -0
  32. package/package.json +2 -2
@@ -478,6 +478,9 @@ export class McpClient {
478
478
  const abortPromise = new Promise<CallToolResult>((_, reject) => {
479
479
  const rejectWithAbortReason = () => {
480
480
  sendCancellationNotification();
481
+ if (requestId !== undefined) {
482
+ messageLayer.cancelRequest(requestId, signal.reason);
483
+ }
481
484
  reject(signal.reason);
482
485
  };
483
486
 
@@ -2350,11 +2353,16 @@ export class StdioTransport implements McpTransport {
2350
2353
 
2351
2354
  this.readable = child.stdout;
2352
2355
  this.writable = child.stdin;
2356
+ const stderrDecoder = new TextDecoder();
2353
2357
  child.stderr.on("data", (chunk: unknown) => {
2354
- this.stderrOutput += chunkToString(chunk);
2355
- if (this.stderrOutput.length > StdioTransport.STDERR_MAX_LENGTH) {
2356
- this.stderrOutput = this.stderrOutput.slice(-StdioTransport.STDERR_MAX_LENGTH);
2357
- }
2358
+ const decoded =
2359
+ chunk instanceof Uint8Array
2360
+ ? stderrDecoder.decode(chunk, { stream: true })
2361
+ : `${stderrDecoder.decode()}${String(chunk)}`;
2362
+ this.appendStderrOutput(decoded);
2363
+ });
2364
+ child.stderr.once("end", () => {
2365
+ this.appendStderrOutput(stderrDecoder.decode());
2358
2366
  });
2359
2367
  this.closed = new Promise((resolve) => {
2360
2368
  let settled = false;
@@ -2405,6 +2413,17 @@ export class StdioTransport implements McpTransport {
2405
2413
  return this.stderrOutput;
2406
2414
  }
2407
2415
 
2416
+ private appendStderrOutput(chunk: string): void {
2417
+ if (chunk.length === 0) {
2418
+ return;
2419
+ }
2420
+
2421
+ this.stderrOutput += chunk;
2422
+ if (this.stderrOutput.length > StdioTransport.STDERR_MAX_LENGTH) {
2423
+ this.stderrOutput = this.stderrOutput.slice(-StdioTransport.STDERR_MAX_LENGTH);
2424
+ }
2425
+ }
2426
+
2408
2427
  dispose(reason = new Error("Stdio transport disposed")): void {
2409
2428
  void reason;
2410
2429
 
@@ -2965,18 +2984,6 @@ export function serializeJsonRpcMessage(message: JsonRpcMessage): string {
2965
2984
  return `${JSON.stringify(message)}\n`;
2966
2985
  }
2967
2986
 
2968
- function chunkToString(chunk: unknown): string {
2969
- if (typeof chunk === "string") {
2970
- return chunk;
2971
- }
2972
-
2973
- if (chunk instanceof Uint8Array) {
2974
- return Buffer.from(chunk).toString("utf8");
2975
- }
2976
-
2977
- return String(chunk);
2978
- }
2979
-
2980
2987
  function normalizeLine(line: string): string {
2981
2988
  return line.endsWith("\r") ? line.slice(0, -1) : line;
2982
2989
  }
@@ -3266,6 +3273,18 @@ export class JsonRpcMessageLayer {
3266
3273
  });
3267
3274
  }
3268
3275
 
3276
+ cancelRequest(requestId: RequestId, reason: unknown): boolean {
3277
+ const pending = this.pendingRequests.get(requestId);
3278
+ if (pending === undefined) {
3279
+ return false;
3280
+ }
3281
+
3282
+ this.pendingRequests.delete(requestId);
3283
+ clearTimeout(pending.timeout);
3284
+ pending.reject(reason);
3285
+ return true;
3286
+ }
3287
+
3269
3288
  dispose(reason = new Error("JSON-RPC message layer disposed")): void {
3270
3289
  if (this.disposedError !== undefined) {
3271
3290
  return;
@@ -1471,6 +1471,40 @@ describe("JsonRpcMessageLayer sendRequest", () => {
1471
1471
  vi.useRealTimers();
1472
1472
  }
1473
1473
  });
1474
+
1475
+ it("clears pending request state and timeout when a request is cancelled", async () => {
1476
+ vi.useFakeTimers();
1477
+ try {
1478
+ const input = new PassThrough();
1479
+ const output = new PassThrough();
1480
+ trackForCleanup(input, output);
1481
+ const layer = new JsonRpcMessageLayer(input, output, 25);
1482
+ const onTimeout = vi.fn();
1483
+ const pendingCount = () =>
1484
+ (
1485
+ layer as unknown as {
1486
+ pendingRequests: Map<unknown, unknown>;
1487
+ }
1488
+ ).pendingRequests.size;
1489
+
1490
+ const responsePromise = layer.sendRequest("slow/method", undefined, {
1491
+ onTimeout,
1492
+ });
1493
+ expect(pendingCount()).toBe(1);
1494
+
1495
+ expect(layer.cancelRequest(1, "user cancelled")).toBe(true);
1496
+
1497
+ await expect(responsePromise).rejects.toBe("user cancelled");
1498
+ expect(pendingCount()).toBe(0);
1499
+
1500
+ await vi.advanceTimersByTimeAsync(25);
1501
+
1502
+ expect(onTimeout).not.toHaveBeenCalled();
1503
+ expect(layer.cancelRequest(1, "already gone")).toBe(false);
1504
+ } finally {
1505
+ vi.useRealTimers();
1506
+ }
1507
+ });
1474
1508
  });
1475
1509
 
1476
1510
  describe("JsonRpcMessageLayer UTF-8 input", () => {
@@ -2643,6 +2677,22 @@ describe("StdioTransport stderr capture", () => {
2643
2677
  expect(transport.getStderrOutput()).toBe("first second");
2644
2678
  });
2645
2679
 
2680
+ it("preserves UTF-8 characters split across stderr chunks", () => {
2681
+ const child = createMockChildProcess();
2682
+ const spawn = vi.fn<StdioSpawn>(() => child);
2683
+
2684
+ const transport = new StdioTransport({
2685
+ command: "node",
2686
+ spawn,
2687
+ });
2688
+
2689
+ const encoded = Buffer.from("é", "utf8");
2690
+ child.stderr.write(encoded.subarray(0, 1));
2691
+ child.stderr.write(encoded.subarray(1));
2692
+
2693
+ expect(transport.getStderrOutput()).toBe("é");
2694
+ });
2695
+
2646
2696
  it("caps stderr at 64KB keeping the tail", () => {
2647
2697
  const child = createMockChildProcess();
2648
2698
  const spawn = vi.fn<StdioSpawn>(() => child);
@@ -7123,6 +7173,21 @@ describe("McpClient callTool", () => {
7123
7173
  throw new Error("Expected initialized notification line to be written");
7124
7174
  }
7125
7175
 
7176
+ const activeMessageLayer = (
7177
+ client as unknown as {
7178
+ messageLayer: JsonRpcMessageLayer | null;
7179
+ }
7180
+ ).messageLayer;
7181
+ if (activeMessageLayer === null) {
7182
+ throw new Error("Expected message layer to exist after connect");
7183
+ }
7184
+ const pendingCount = () =>
7185
+ (
7186
+ activeMessageLayer as unknown as {
7187
+ pendingRequests: Map<unknown, unknown>;
7188
+ }
7189
+ ).pendingRequests.size;
7190
+
7126
7191
  const abortController = new AbortController();
7127
7192
  const callToolPromise = client.callTool(
7128
7193
  {
@@ -7142,6 +7207,8 @@ describe("McpClient callTool", () => {
7142
7207
  const callToolRequest = JSON.parse(callToolLineResult.value) as {
7143
7208
  id: number;
7144
7209
  };
7210
+ expect(pendingCount()).toBe(1);
7211
+
7145
7212
  abortController.abort("user cancelled");
7146
7213
 
7147
7214
  const cancelledLineResult = await iterator.next();
@@ -7158,6 +7225,7 @@ describe("McpClient callTool", () => {
7158
7225
  });
7159
7226
 
7160
7227
  await callToolRejection;
7228
+ expect(pendingCount()).toBe(0);
7161
7229
  await client.close();
7162
7230
  });
7163
7231
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft",
3
- "version": "0.0.25",
3
+ "version": "0.0.26",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -45,7 +45,7 @@
45
45
  "dependencies": {
46
46
  "@clack/core": "^1.0.0",
47
47
  "@clack/prompts": "^1.0.0",
48
- "toolcraft-schema": "0.0.25",
48
+ "toolcraft-schema": "0.0.26",
49
49
  "commander": "^14.0.3",
50
50
  "jose": "^6.1.2",
51
51
  "jsonc-parser": "^3.3.1",