Repository navigation
Conversation
There was a problem hiding this comment.
Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.
An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.
There was a problem hiding this comment.
The approach looks sound, but I could not verify it: the bug needs a detached Windows 11 24H2+ process, and the review sandbox runs Linux. I reviewed the code instead, ran test/hidden-console.test.ts (2 pass, 1 Windows-only test skipped) and ran tsc -b for packages/cli (passes).
The allocation only runs in service mode on win32, after the incumbent check. It does nothing when a console is already attached (an interactive terminal, or the Desktop sidecar spawned with CREATE_NO_WINDOW). Restoring the standard handles keeps Service.ensure's stderr pipe intact. #hidden-console follows the same Bun/Node import-map pattern core already uses for #pty.
Public API is unchanged; #hidden-console is an internal import map.
Scope to note: Windows 10 and Windows 11 before 24H2 (build < 26100) still get the flashes from unhidden plugin, Bun.$ and execSync spawns, and so does the Node SEA build. That's worth saying explicitly when closing #52266, because some reporters may be on older builds.
Tests: the two tests that always run only exercise early-return paths (the Node stub returning undefined, and attach() on a non-Windows or attached process). They can be dropped, leaving the Windows probe test.
Related: #56704
There was a problem hiding this comment.
The new commit addresses my earlier point: the two always-on tests that only covered early returns are gone, and the Windows probe now also checks that calling attach() a second time is a no-op. I still couldn't verify the fix itself: it needs Windows 11 24H2+ (build 26100+), and the review sandbox runs Linux. test/hidden-console.test.ts gives 0 failures (its only test is Windows-only and was skipped here), and tsc -b for packages/cli passes.
The other changes look right. Effect.try with a warning log means a failed FFI call can no longer stop the service from starting. Returning before the handle restore when allocation fails is fine, since a failed call shouldn't have replaced the standard handles. An unparsable os.release() now skips allocation.
The public API is unchanged. No new concerns.
There was a problem hiding this comment.
The new commit looks correct and adds no new concerns. I couldn't verify it here: it needs Windows 11 24H2+, and the review sandbox runs Linux.
The out-params now use "buffer", so Bun reads the address when the call is made. The handles now travel as u64, and invalidHandle was changed to 0xffff_ffff_ffff_ffffn to match, so restoring the standard handles still skips INVALID_HANDLE_VALUE.
test/hidden-console.test.ts gives 0 failures (its one Windows-only test is skipped here), and tsc -b for packages/cli passes.
The public API is unchanged.
|
Independent confirmation + reproduction data (opencode v2.2.5, Windows 11 build 22000 (21H2), x64). We maintain oc-manager, a third-party Wails manager that launches
This matches the root cause analysis in this PR exactly: oc-manager sets One gap to flag: this PR is scoped to builds ≥ 26100 ( |
Fixes #52266
Closes #59598
Refs #56704
serve --serviceon Windows 11 24H2+ (packages/cli/src/services/hidden-console.bun.ts,packages/cli/src/server-process.ts):spawnServiceContenderlaunchesopencode serve --servicewithdetached: true(DETACHED_PROCESS), leaving the service with no attached console (AttachConsolefails withERROR_INVALID_HANDLE). While Core'sCrossSpawnSpawnersetswindowsHide: true(CREATE_NO_WINDOW), in-process server plugins and package helpers callingchild_process.execSync,Bun.spawn, orBun.$(which exposes nowindowsHideoption) trigger a visibleCASCADIA_HOSTING_WINDOW_CLASS/ConsoleWindowClasswindow andEVENT_SYSTEM_FOREGROUNDfocus steal per spawn underopencode-cli.exe's PID. CallingAllocConsoleWithOptions(ALLOC_CONSOLE_MODE_NO_WINDOW)after incumbent discovery gives the service a windowless console (GetConsoleWindow() === 0) that unhidden children inherit silently.packages/cli/src/services/hidden-console.bun.ts,packages/cli/src/services/hidden-console.node.ts): Skip allocation whenGetConsoleProcessListreports an already-attached console (such as interactiveopencode serve --servicein a terminal), save and restoreSTD_INPUT_HANDLE/STD_OUTPUT_HANDLE/STD_ERROR_HANDLEaround allocation soService.ensure's startup error pipe remains intact, wrap attachment inEffect.tryso FFI failures never abort service startup, and keep Windows builds< 26100(whereAllocConsoleWithOptionsis absent) and the Node SEA build on existing console-less behavior.bun:ffipointer guidance (packages/cli/src/services/hidden-console.bun.ts): Declare theALLOC_CONSOLE_OPTIONSand process-list out-params as"buffer"and pass theTypedArrayitself, so the engine reads the address off the live view at call time instead of aptr()number that can dangle when JSC relocates a small typed array's storage (Make bun:ffiptrfunction safer oven-sh/bun#9094, Typed-array reads after a bun:ffi call return stale values once the calling function is JIT-compiled (DFG load elimination across native calls) oven-sh/bun#32054).HANDLEvalues travel asu64, per the Bun docs' Windows note.flowchart LR Ensure["Service.ensure<br/>DETACHED_PROCESS (no console)"] --> Incumbent{"Incumbent<br/>running?"} Incumbent -- "yes" --> Exit["Exit contender"] Incumbent -- "no (Win11 26100+)" --> Alloc["AllocConsoleWithOptions<br/>ALLOC_CONSOLE_MODE_NO_WINDOW<br/>+ restore STD_*_HANDLE"] Alloc --> Core["CrossSpawnSpawner<br/>CREATE_NO_WINDOW → HWND 0x0"] Alloc --> Plugin["In-process Plugin / Bun.$ / execSync<br/>Inherits service console → HWND 0x0"]opencode serve --service(Win1110.0.26300)dev(SetWinEventHookevents)+ HiddenConsole)CrossSpawnSpawner(git,rg,powershell,.cmdshims)0(HWND 0x0)0(HWND 0x0).cmd/.exe, plus childexecSync("git"))0(HWND 0x0)0(HWND 0x0)child_process.execSync("git ...")SHOW+FOREGROUND(CASCADIA_HOSTING_WINDOW_CLASS)0(HWND 0x0inherited)Bun.spawn(["git", ...])(default)SHOW(visible = 1)0(HWND 0x0inherited)Bun.$`git ...`(nowindowsHideAPI)CREATE(ConsoleWindowClass)0(HWND 0x0inherited)Visual
Added by the opencode bot (AI-generated).
Related: #56704