Conversation
A Windows console starts on an OEM code page (936/GBK on a Simplified Chinese system) and reinterprets the UTF-8 bytes bsk prints, so every non-ASCII character a page contributes reaches the user as mojibake: `微众银行` prints as `寰紬閾惰`. Observed with `bsk observe` against a Chinese-language application form in a stock zh-CN console. Switch the attached console to UTF-8 for the lifetime of the process and restore the previous code page on drop, so the user's shell is left as found. Redirected streams are untouched: they receive the raw bytes verbatim, with no code page in the path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
I drove
bskend to end from PowerShell on Windows 11 + Edge to fill a long,real-world application form: the campus-recruitment application for WeBank on
Moka (
app-tc.mokahr.com/campus-recruitment/webankhr/...), in my own logged-inprofile.
It is the shape of page
bskexists for, and none of it is plain HTML: ~30 fieldsacross 12 sections, built out of
semi-designwidgets —sd-Selectdropdowns, athree-level cascader for 籍贯 and 校招面试站点, month/year pickers, a tag-grid
geography picker, autocomplete inputs, hidden
input[type=file]for a photo and aPDF resume, and a two-step submit (
预览并提交→确认提交). The CLI had to readpage state back, verify thirty fields, and upload two attachments — which is where
this patch came from.
Problems hit
Items 2, 3 and 4 are reported separately, since they are unrelated to each other or
to this fix: #385 (upload permission is not reported up front), #386 (a
session reclaimed mid-use loses the page), #387 (a misplaced global flag fails
on stderr only). Only item 1 is fixed here.
1. Console output was mojibake for every non-ASCII page string — fixed here.
A Windows console starts on its OEM code page (936/GBK here).
bskwrites UTF-8,so every byte that came from the page is decoded against the wrong table. Taken
from a real
bsk snapshotof the Moka page — the first line is the captured UTF-8,the second is what an attached 936 console makes of the same bytes:
That is not cosmetic.
snapshot/observe/evaluateare how an agent readspage state back, and how it decides whether a custom widget actually committed its
value. With
首页arriving as棣栭〉there is no way to tell a real selection froma stale label, and matching an option by its expected text silently fails. I worked
around it by forcing UTF-8 on every invocation and reading redirected output as
UTF-8 — a wrapper per command, and one more moving part per step.
2. Attachment upload is blocked by a permission the tool cannot self-diagnose —
not fixed here.
bsk uploadfailed three ways before giving up:cdp_failed / "Not allowed" / reason=set_file_input_failed,then
element not visible (no content quads ...), thenthe upload trigger did not activate a file input. The root cause is theextension's "Allow access to file URLs" toggle being off. The error text does
say so, but only after the caller has burned two wrong-target attempts;
bsk doctordoes not check it, and the pre-flight check in the skill does notmention it.
effect_state=unknownis correctly marked "do not retry", so this PRdoes nothing about it — flagging it because the failure mode costs about ten
minutes every time.
3. A session is recycled while it is still in use — not fixed here.
My first Agent Window session was reclaimed while idle and
bsk session listreturned
(no active sessions). Moka keeps no server-side draft, so thetwenty-plus fields already filled into the page were gone and the whole form had to
be redone. There is no TTL, no warning, and no way for the caller to hold the
session; from the outside it is indistinguishable from a crash.
4.
--sessionbefore the subcommand reports nothing useful — not fixed here.bsk --session <id> click --selector '#x'exits 1 withunexpected argument '--session' foundon stderr only, while the redirectedstdout stays empty. Read from a script, that looks like "the click silently did
nothing", which sends you hunting through the page instead of the command line.
Global flags appear to belong after the subcommand; a one-line usage note or a
non-empty stdout error would have saved that detour.
What changed
crates/bsk-cli/src/cli/console_encoding.rs— aUtf8ConsoleRAII guard.enable()readsGetConsoleOutputCP(); if a console is attached and its codepage is neither 0 nor 65001, it calls
SetConsoleOutputCP(65001)and remembersthe old value, restoring it on
Dropso the user's shell is left exactly asfound. Inert on non-Windows hosts, when neither stdout nor stderr is a terminal,
and when the console is already UTF-8. Redirected streams are never touched —
they receive the raw bytes, so no code page is in their path and re-encoding them
would be a regression.
main.rs— binds the guard beforeCli::try_parse()so it outlives everywrite, including clap's usage output.
#[must_use]on the type keeps thelifetime mistake from compiling silently.
Cargo.toml— adds theWin32_System_Consolefeature to the existingwindows-sysdependency.CHANGELOG.md— entry under[Unreleased] → Fixed.Five unit tests cover the decision function (
redirected_output_is_left_alone,a_utf8_console_is_left_alone,a_missing_console_is_left_alone,an_oem_code_page_is_remembered_for_restore, and a guard-inertness check), so the"do nothing" branches are pinned without needing a console.
Verification
On this machine (Windows 11 Home China,
stable-x86_64-pc-windows-gnu):cargo build --release --lockedsucceeds; the resultingbsk.exeis the binarythis whole session ran on.
cargo test --locked -p bsk --lib console_encoding— 5 passed; 0 failed.cargo fmt --all -- --check— clean.node scripts/check-crate-skill.mjs— clean.cargo clippy --workspace --all-targets --locked -- -D warningsandcargo test --workspace --lockedfail on this branch, but not because of thispatch. All three clippy findings sit on Windows-only code paths, which is why
upstream CI stays green (it runs on
ubuntu-latest;main's latest run is asuccess):
daemon/file_transfer.rsset_private_filetakesfile: &Filewhosebody is
#[cfg(unix)],daemon/ipc.rs:793is a#[cfg(test)]helper, andskill_install/harness.rs:339is inside a#[cfg(windows)]block. A Windowsclippy -D warningsgate is therefore red onmainas it stands, for reasonsunrelated to this change.
cargo test --workspacethen aborts while building theintegration tests with
crate ... required to be available in rlib format, but was not found in this formand, on an earlier attempt,failed to mmap ... os error 1455(page file too small) — a local debug-build resource problem in thistree, not a code path. The
--librun above is the part that exercises thischange.
What is not covered by an end-to-end run. I drove everything from an
automation shell where stdout is always a pipe, so
IsTerminal()is false thereand the guard is inert by design — I could not watch an attached console change
code page from that context, and I am not claiming to have done so. The
attached-console path is covered to the extent the split-out decision function can
be: the four
restore_code_pagecases plus the guard-inertness check, and thebuild itself. Confirming the real thing needs one interactive
bsk snapshoton aGBK console, which is a ten-second check for anyone reviewing this on Windows.
Notes
mainat3f10983and is a single commit.the repo has no
CONTRIBUTINGor PR template to follow, so this descriptionborrows the shape of the existing fix commits. Happy to open an issue and
rename the branch to
fix/<n>-...if that is preferred.captured snapshot of this page, and the second line is those exact bytes decoded
as GBK. It is the reason the fix exists.