Skip to content

Commit 6b74ca3

Browse files
committed
cli/multiselect: guard finish + defer teardown to microtask (fix BindingError on enter); bump v0.1.3
1 parent eafdfa3 commit 6b74ca3

3 files changed

Lines changed: 30 additions & 14 deletions

File tree

Formula/agentlock.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
class Agentlock < Formula
22
desc "Locally-hosted, open-source firewall for AI coding agents"
33
homepage "https://openagentlock.github.io/OpenAgentLock"
4-
url "https://registry.npmjs.org/@openagentlock/cli/-/cli-0.1.2.tgz"
5-
sha256 "5bae7033ab61e64c1ef1723f5e0dab12a5fb3f2d20ecc54f8de06d95389bab8e"
4+
url "https://registry.npmjs.org/@openagentlock/cli/-/cli-0.1.3.tgz"
5+
sha256 "REPLACE_AFTER_NPM_PUBLISH"
66
license "FSL-1.1-Apache-2.0"
7-
version "0.1.2"
7+
version "0.1.3"
88

99
depends_on "bun"
1010

cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@openagentlock/cli",
3-
"version": "0.1.2",
3+
"version": "0.1.3",
44
"type": "module",
55
"license": "SEE LICENSE IN LICENSE",
66
"description": "OpenAgentLock CLI — a firewall for AI coding agents. Detects local agent harnesses (Claude Code, Codex CLI, Cursor, OpenCode, Cline, Gemini CLI, Continue, Copilot), gates risky tool calls via a Go control plane, anchors decisions in a Rust Merkle ledger.",

cli/src/tui/multiselect.tsx

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -194,18 +194,34 @@ export async function multiselect<T extends string>(
194194
const root = createRoot(renderer);
195195

196196
return await new Promise<T[] | null>((resolve) => {
197+
let finished = false;
197198
const finish = (result: T[] | null): void => {
198-
try {
199-
root.unmount();
200-
} catch {
201-
// best-effort
202-
}
203-
try {
204-
renderer.destroy();
205-
} catch {
206-
// best-effort
207-
}
199+
// Guard against the keypress handler firing finish twice (Enter
200+
// can produce both `return` and `enter` on some terminals; q/esc
201+
// similarly). React's reconciler does not tolerate a second
202+
// unmount commit on a torn-down container — symptom is
203+
// BindingError "Expected null or instance of Node" inside
204+
// commitBeforeMutationEffects → clearContainer.
205+
if (finished) return;
206+
finished = true;
207+
// Resolve first so the caller's `await` settles before teardown.
208+
// Defer unmount + destroy to the next microtask so React's
209+
// current commit cycle (the keypress that triggered finish)
210+
// finishes before the reconciler tries to mutate a container we
211+
// are about to clear.
208212
resolve(result);
213+
queueMicrotask(() => {
214+
try {
215+
root.unmount();
216+
} catch {
217+
// best-effort
218+
}
219+
try {
220+
renderer.destroy();
221+
} catch {
222+
// best-effort
223+
}
224+
});
209225
};
210226

211227
root.render(

0 commit comments

Comments
 (0)