toolcraft 0.0.25 → 0.0.27
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/cli.js +11 -9
- package/dist/error-report.js +109 -36
- package/dist/redaction.d.ts +4 -0
- package/dist/redaction.js +70 -0
- package/node_modules/@poe-code/config-mutations/dist/execution/apply-mutation.js +33 -9
- package/node_modules/@poe-code/config-mutations/dist/formats/json.d.ts +2 -1
- package/node_modules/@poe-code/config-mutations/dist/formats/json.js +36 -9
- package/node_modules/@poe-code/config-mutations/dist/types.d.ts +2 -0
- package/node_modules/@poe-code/design-system/dist/components/browser.js +1 -1
- package/node_modules/@poe-code/design-system/dist/explorer/actions.js +1 -1
- package/node_modules/@poe-code/design-system/dist/explorer/keymap.js +11 -1
- package/node_modules/@poe-code/design-system/dist/explorer/reducer.js +64 -8
- package/node_modules/@poe-code/design-system/dist/explorer/render/detail.js +9 -11
- package/node_modules/@poe-code/design-system/dist/explorer/render/footer.js +18 -8
- package/node_modules/@poe-code/design-system/dist/explorer/render/header.js +11 -18
- package/node_modules/@poe-code/design-system/dist/explorer/render/index.js +2 -10
- package/node_modules/@poe-code/design-system/dist/explorer/render/list.js +32 -22
- package/node_modules/@poe-code/design-system/dist/explorer/render/modal.js +5 -9
- package/node_modules/@poe-code/design-system/dist/explorer/render/text.d.ts +12 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/text.js +81 -0
- package/node_modules/@poe-code/design-system/dist/explorer/state.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/explorer/state.js +2 -0
- package/node_modules/@poe-code/design-system/dist/prompts/index.js +3 -3
- package/node_modules/@poe-code/process-runner/dist/docker/docker-execution-env.js +24 -3
- package/node_modules/@poe-code/process-runner/dist/docker/docker-runner.js +1 -0
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues-sync.js +8 -0
- package/node_modules/auth-store/dist/keychain-store.js +20 -1
- package/node_modules/mcp-oauth/dist/client/auth-store-session-store.js +6 -3
- package/node_modules/tiny-mcp-client/dist/internal.d.ts +2 -0
- package/node_modules/tiny-mcp-client/dist/internal.js +30 -13
- package/node_modules/tiny-mcp-client/src/internal.ts +35 -16
- package/node_modules/tiny-mcp-client/src/transports.test.ts +68 -0
- package/package.json +2 -2
|
@@ -126,6 +126,14 @@ function buildVerifyReport(lookup, opts) {
|
|
|
126
126
|
export async function syncGhProject(opts) {
|
|
127
127
|
const client = resolveGhClient(opts);
|
|
128
128
|
let lookup = await lookupProject(client, opts.owner, opts.number);
|
|
129
|
+
const initialReport = buildVerifyReport(lookup, opts);
|
|
130
|
+
if (initialReport.ok || opts.yes !== true) {
|
|
131
|
+
return {
|
|
132
|
+
...initialReport,
|
|
133
|
+
created: [],
|
|
134
|
+
updated: []
|
|
135
|
+
};
|
|
136
|
+
}
|
|
129
137
|
let resolvedNumber = opts.number;
|
|
130
138
|
const created = [];
|
|
131
139
|
if (lookup.project === null) {
|
|
@@ -64,6 +64,19 @@ function runSecurityCommand(command, args, options) {
|
|
|
64
64
|
});
|
|
65
65
|
let stdout = "";
|
|
66
66
|
let stderr = "";
|
|
67
|
+
let stdinErrorMessage;
|
|
68
|
+
const appendStderr = (message) => {
|
|
69
|
+
stderr = stderr.length === 0
|
|
70
|
+
? message
|
|
71
|
+
: `${stderr}${stderr.endsWith("\n") ? "" : "\n"}${message}`;
|
|
72
|
+
};
|
|
73
|
+
const appendStdinError = () => {
|
|
74
|
+
if (stdinErrorMessage === undefined) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
appendStderr(stdinErrorMessage);
|
|
78
|
+
stdinErrorMessage = undefined;
|
|
79
|
+
};
|
|
67
80
|
child.stdout?.setEncoding("utf8");
|
|
68
81
|
child.stdout?.on("data", (chunk) => {
|
|
69
82
|
stdout += chunk.toString();
|
|
@@ -73,17 +86,23 @@ function runSecurityCommand(command, args, options) {
|
|
|
73
86
|
stderr += chunk.toString();
|
|
74
87
|
});
|
|
75
88
|
if (options?.stdin !== undefined) {
|
|
89
|
+
child.stdin?.once("error", (error) => {
|
|
90
|
+
stdinErrorMessage = error instanceof Error ? error.message : String(error);
|
|
91
|
+
});
|
|
76
92
|
child.stdin?.end(options.stdin);
|
|
77
93
|
}
|
|
78
94
|
child.on("error", (error) => {
|
|
79
95
|
const message = error instanceof Error ? error.message : String(error ?? "Unknown error");
|
|
96
|
+
appendStdinError();
|
|
97
|
+
appendStderr(message);
|
|
80
98
|
resolve({
|
|
81
99
|
stdout,
|
|
82
|
-
stderr
|
|
100
|
+
stderr,
|
|
83
101
|
exitCode: 127
|
|
84
102
|
});
|
|
85
103
|
});
|
|
86
104
|
child.on("close", (code) => {
|
|
105
|
+
appendStdinError();
|
|
87
106
|
resolve({
|
|
88
107
|
stdout,
|
|
89
108
|
stderr,
|
|
@@ -61,12 +61,15 @@ export function createAuthStoreClientStore(options) {
|
|
|
61
61
|
}
|
|
62
62
|
function createNamedSecretStore(key, options, defaults) {
|
|
63
63
|
const hash = crypto.createHash("sha256").update(key).digest("hex");
|
|
64
|
-
const
|
|
64
|
+
const configuredFilePath = options.fileStore?.filePath;
|
|
65
|
+
const parsedFilePath = configuredFilePath === undefined ? null : path.parse(configuredFilePath);
|
|
65
66
|
const fileStore = {
|
|
66
67
|
...options.fileStore,
|
|
68
|
+
filePath: parsedFilePath === null
|
|
69
|
+
? undefined
|
|
70
|
+
: path.join(parsedFilePath.dir, `${parsedFilePath.name}-${hash}${parsedFilePath.ext || ".enc"}`),
|
|
67
71
|
salt: options.fileStore?.salt ?? defaults.salt,
|
|
68
|
-
defaultDirectory:
|
|
69
|
-
options.fileStore?.defaultDirectory ||
|
|
72
|
+
defaultDirectory: options.fileStore?.defaultDirectory ||
|
|
70
73
|
defaults.directory,
|
|
71
74
|
defaultFileName: parsedFilePath === null
|
|
72
75
|
? `${hash}.enc`
|
|
@@ -400,6 +400,7 @@ export declare class StdioTransport implements McpTransport {
|
|
|
400
400
|
private static readonly STDERR_MAX_LENGTH;
|
|
401
401
|
constructor({ command, args, cwd, env, spawn: spawnProcess, }: StdioTransportOptions);
|
|
402
402
|
getStderrOutput(): string;
|
|
403
|
+
private appendStderrOutput;
|
|
403
404
|
dispose(reason?: Error): void;
|
|
404
405
|
}
|
|
405
406
|
export declare class HttpTransport implements McpTransport {
|
|
@@ -542,6 +543,7 @@ export declare class JsonRpcMessageLayer {
|
|
|
542
543
|
onRequest(method: string, handler: JsonRpcRequestHandler): void;
|
|
543
544
|
onNotification(method: string, handler: JsonRpcNotificationHandler): void;
|
|
544
545
|
sendRequest(method: string, params?: unknown, options?: JsonRpcRequestOptions): Promise<unknown>;
|
|
546
|
+
cancelRequest(requestId: RequestId, reason: unknown): boolean;
|
|
545
547
|
dispose(reason?: Error): void;
|
|
546
548
|
private consumeInput;
|
|
547
549
|
private resolveInputStreamClosedReason;
|
|
@@ -285,6 +285,9 @@ export class McpClient {
|
|
|
285
285
|
const abortPromise = new Promise((_, reject) => {
|
|
286
286
|
const rejectWithAbortReason = () => {
|
|
287
287
|
sendCancellationNotification();
|
|
288
|
+
if (requestId !== undefined) {
|
|
289
|
+
messageLayer.cancelRequest(requestId, signal.reason);
|
|
290
|
+
}
|
|
288
291
|
reject(signal.reason);
|
|
289
292
|
};
|
|
290
293
|
abortListener = rejectWithAbortReason;
|
|
@@ -1511,11 +1514,15 @@ export class StdioTransport {
|
|
|
1511
1514
|
const child = this.child;
|
|
1512
1515
|
this.readable = child.stdout;
|
|
1513
1516
|
this.writable = child.stdin;
|
|
1517
|
+
const stderrDecoder = new TextDecoder();
|
|
1514
1518
|
child.stderr.on("data", (chunk) => {
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
+
const decoded = chunk instanceof Uint8Array
|
|
1520
|
+
? stderrDecoder.decode(chunk, { stream: true })
|
|
1521
|
+
: `${stderrDecoder.decode()}${String(chunk)}`;
|
|
1522
|
+
this.appendStderrOutput(decoded);
|
|
1523
|
+
});
|
|
1524
|
+
child.stderr.once("end", () => {
|
|
1525
|
+
this.appendStderrOutput(stderrDecoder.decode());
|
|
1519
1526
|
});
|
|
1520
1527
|
this.closed = new Promise((resolve) => {
|
|
1521
1528
|
let settled = false;
|
|
@@ -1555,6 +1562,15 @@ export class StdioTransport {
|
|
|
1555
1562
|
getStderrOutput() {
|
|
1556
1563
|
return this.stderrOutput;
|
|
1557
1564
|
}
|
|
1565
|
+
appendStderrOutput(chunk) {
|
|
1566
|
+
if (chunk.length === 0) {
|
|
1567
|
+
return;
|
|
1568
|
+
}
|
|
1569
|
+
this.stderrOutput += chunk;
|
|
1570
|
+
if (this.stderrOutput.length > StdioTransport.STDERR_MAX_LENGTH) {
|
|
1571
|
+
this.stderrOutput = this.stderrOutput.slice(-StdioTransport.STDERR_MAX_LENGTH);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1558
1574
|
dispose(reason = new Error("Stdio transport disposed")) {
|
|
1559
1575
|
void reason;
|
|
1560
1576
|
if (this.disposed) {
|
|
@@ -1966,15 +1982,6 @@ export class McpError extends Error {
|
|
|
1966
1982
|
export function serializeJsonRpcMessage(message) {
|
|
1967
1983
|
return `${JSON.stringify(message)}\n`;
|
|
1968
1984
|
}
|
|
1969
|
-
function chunkToString(chunk) {
|
|
1970
|
-
if (typeof chunk === "string") {
|
|
1971
|
-
return chunk;
|
|
1972
|
-
}
|
|
1973
|
-
if (chunk instanceof Uint8Array) {
|
|
1974
|
-
return Buffer.from(chunk).toString("utf8");
|
|
1975
|
-
}
|
|
1976
|
-
return String(chunk);
|
|
1977
|
-
}
|
|
1978
1985
|
function normalizeLine(line) {
|
|
1979
1986
|
return line.endsWith("\r") ? line.slice(0, -1) : line;
|
|
1980
1987
|
}
|
|
@@ -2168,6 +2175,16 @@ export class JsonRpcMessageLayer {
|
|
|
2168
2175
|
}
|
|
2169
2176
|
});
|
|
2170
2177
|
}
|
|
2178
|
+
cancelRequest(requestId, reason) {
|
|
2179
|
+
const pending = this.pendingRequests.get(requestId);
|
|
2180
|
+
if (pending === undefined) {
|
|
2181
|
+
return false;
|
|
2182
|
+
}
|
|
2183
|
+
this.pendingRequests.delete(requestId);
|
|
2184
|
+
clearTimeout(pending.timeout);
|
|
2185
|
+
pending.reject(reason);
|
|
2186
|
+
return true;
|
|
2187
|
+
}
|
|
2171
2188
|
dispose(reason = new Error("JSON-RPC message layer disposed")) {
|
|
2172
2189
|
if (this.disposedError !== undefined) {
|
|
2173
2190
|
return;
|
|
@@ -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
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
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.
|
|
3
|
+
"version": "0.0.27",
|
|
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.
|
|
48
|
+
"toolcraft-schema": "0.0.27",
|
|
49
49
|
"commander": "^14.0.3",
|
|
50
50
|
"jose": "^6.1.2",
|
|
51
51
|
"jsonc-parser": "^3.3.1",
|