feat(picker): open the Win32 folder dialog in-process over koffi

The modern IFileOpenDialog becomes the primary win32 tier: a koffi-driven
COM conversation on a worker_threads worker (the modal Show never blocks
the host event loop), per-monitor-v2 DPI via SetThreadDpiAwarenessContext,
and abort service by re-posting WM_CLOSE to the dialog thread's windows,
with terminate+unref as the last resort (Node cannot interrupt a thread
blocked in native code, and such a worker must never hold the process open).

The PowerShell chain stays as the fallback tier with its trigger widened
from ENOENT to any pwsh failure, closing the review-flagged PowerShell 6
regression (no WinForms: exit 1, not ENOENT, so 5.1 never ran).

Layering keeps per-file coverage honest on every host: pure sequencing and
the driver test against fakes anywhere; the bindings run against a mocked
koffi COM world (the session-persistence-jsonl technique); POSIX hosts
drive the real spawn plumbing to its koffi-load rejection; win32 hosts run
a real open-and-abort-close smoke. The smoke joins processBoundTests: a
worker blocked in a native modal wedges the threads pool's teardown, while
a fork contains it. The worker bundles as its own CJS tsdown entry
(workflow-workerthread's pattern; no TLA), and the host module is imported
statically so the node-half bundle stays chunk-free.

Built-plane and real-COM behavior verified on native Windows: standalone
probes for the source worker, the built CJS worker, and the driver's abort
path all open and close the real dialog.

Agent Notes: new implemented/feature/2026-08-02-win32-in-process-folder-dialog
(bilingual) owns the decision; the DPI note is re-scoped to the fallback tier
it now describes and its AutoUpgradeEnabled attribution corrected (.NET Core
3.0 rewrote FolderBrowserDialog; the opt-out arrived in .NET 6).
This commit is contained in:
Huanqi Cao
2026-08-03 00:06:47 +08:00
parent da1b1ff87d
commit 089f4dfad8
23 changed files with 1174 additions and 41 deletions

6
pnpm-lock.yaml generated
View File

@@ -3643,6 +3643,9 @@ importers:
'@deepseek-ai/dsh-native-command':
specifier: workspace:^
version: link:../../util/native-command
koffi:
specifier: ^3.1.0
version: 3.1.1
devDependencies:
'@deepseek-ai/dsh-client-runtime':
specifier: workspace:^
@@ -3665,6 +3668,9 @@ importers:
react:
specifier: ^18.2.0
version: 18.3.1
tsx:
specifier: ^4.19.2
version: 4.22.4
packages/host/webserver:
dependencies: