[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
104
electron/bridges/mcpServerBridge.approvalTimeout.test.cjs
Normal file
104
electron/bridges/mcpServerBridge.approvalTimeout.test.cjs
Normal file
@@ -0,0 +1,104 @@
|
||||
"use strict";
|
||||
|
||||
const test = require("node:test");
|
||||
const assert = require("node:assert/strict");
|
||||
const { EventEmitter } = require("node:events");
|
||||
const { setTimeout: delay } = require("node:timers/promises");
|
||||
|
||||
const mcpServerBridge = require("./mcpServerBridge.cjs");
|
||||
|
||||
function createFakeWindow() {
|
||||
const contents = new EventEmitter();
|
||||
contents.id = Math.floor(Math.random() * 1e6);
|
||||
contents.send = (channel, payload) => {
|
||||
contents.emit("send", channel, payload);
|
||||
};
|
||||
contents.isDestroyed = () => false;
|
||||
return {
|
||||
isDestroyed: () => false,
|
||||
webContents: contents,
|
||||
};
|
||||
}
|
||||
|
||||
function stubNow(t, startMs) {
|
||||
const realNow = Date.now;
|
||||
let now = startMs;
|
||||
Date.now = () => now;
|
||||
t.after(() => {
|
||||
Date.now = realNow;
|
||||
});
|
||||
return {
|
||||
advance: (deltaMs) => {
|
||||
now += deltaMs;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
test("cancelApprovalTimeoutFromRenderer keeps the absolute MCP deadline armed", async (t) => {
|
||||
const win = createFakeWindow();
|
||||
mcpServerBridge.setMainWindowGetter(() => win);
|
||||
t.after(() => {
|
||||
mcpServerBridge.clearPendingApprovals();
|
||||
mcpServerBridge.setMainWindowGetter(() => null);
|
||||
});
|
||||
|
||||
const requests = [];
|
||||
const cleared = [];
|
||||
win.webContents.on("send", (channel, payload) => {
|
||||
if (channel === "netcatty:ai:mcp:approval-request") requests.push(payload);
|
||||
if (channel === "netcatty:ai:mcp:approval-cleared") {
|
||||
cleared.push(...(payload?.approvalIds || []));
|
||||
}
|
||||
});
|
||||
|
||||
const clock = stubNow(t, 1_000_000);
|
||||
const approvalPromise = mcpServerBridge.requestApprovalFromRenderer(
|
||||
"terminal_execute",
|
||||
{ command: "echo hi" },
|
||||
"chat-mcp-absolute",
|
||||
);
|
||||
|
||||
assert.equal(requests.length, 1);
|
||||
const approvalId = requests[0].approvalId;
|
||||
|
||||
// Jump close to the 110s absolute ceiling, then cancel idle. Remaining absolute ~40ms.
|
||||
clock.advance(109_960);
|
||||
assert.equal(mcpServerBridge.cancelApprovalTimeoutFromRenderer(approvalId), true);
|
||||
assert.equal(mcpServerBridge.cancelApprovalTimeoutFromRenderer(approvalId), false);
|
||||
|
||||
await delay(20);
|
||||
assert.equal(cleared.includes(approvalId), false, "must stay pending before absolute expiry");
|
||||
|
||||
const outcome = await Promise.race([
|
||||
approvalPromise.then((approved) => ({ approved })),
|
||||
delay(120).then(() => ({ approved: "timeout-wait" })),
|
||||
]);
|
||||
assert.deepEqual(outcome, { approved: false });
|
||||
assert.ok(cleared.includes(approvalId));
|
||||
});
|
||||
|
||||
test("cancelApprovalTimeoutFromRenderer still allows explicit approve before absolute expiry", async (t) => {
|
||||
const win = createFakeWindow();
|
||||
mcpServerBridge.setMainWindowGetter(() => win);
|
||||
t.after(() => {
|
||||
mcpServerBridge.clearPendingApprovals();
|
||||
mcpServerBridge.setMainWindowGetter(() => null);
|
||||
});
|
||||
|
||||
const requests = [];
|
||||
win.webContents.on("send", (channel, payload) => {
|
||||
if (channel === "netcatty:ai:mcp:approval-request") requests.push(payload);
|
||||
});
|
||||
|
||||
stubNow(t, 2_000_000);
|
||||
const approvalPromise = mcpServerBridge.requestApprovalFromRenderer(
|
||||
"sftp_write",
|
||||
{ path: "/tmp/x" },
|
||||
"chat-mcp-approve",
|
||||
);
|
||||
const approvalId = requests[0].approvalId;
|
||||
|
||||
assert.equal(mcpServerBridge.cancelApprovalTimeoutFromRenderer(approvalId), true);
|
||||
mcpServerBridge.resolveApprovalFromRenderer(approvalId, true);
|
||||
assert.equal(await approvalPromise, true);
|
||||
});
|
||||
Reference in New Issue
Block a user