From 41d03f0a35a95daa7d54e7f29ab924776bedf6e4 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Thu, 15 Oct 2026 17:42:30 +0200 Subject: fix(cli): make the command-line client work on Windows Its suite had only run on Linux. On Windows, against a real hub and node and from the installed meshbay-cli.exe: - secrets.enc was written in text mode (os.open without O_BINARY): every 0x0A of the ciphertext became CR LF and the saved session "does not open with its key" on the next run. - isatty() is true for NUL, so a script run with `< NUL` or by the Task Scheduler was asked questions instead of told to pass --yes; output.terminal() checks for a real console. - Ctrl+C waited for the selector loop's next timer, up to half a minute in `chat --follow`; the loop now points the signal wakeup at its self-pipe, as the proactor does. - Right-click copy and paste raised: asyncio cannot start a process on the selector loop; the clipboard tool runs in a thread. - Tab completion of remote paths ran on the default (proactor) loop, which never opens a node, and wrote in the locale encoding; the PowerShell script now reads it as UTF-8. - `get -R` into a folder past MAX_PATH died with a traceback; get, put, the resume file and the interactive mode use the extended form at each disk call, as the node does. - `groups` showed a SQLite hub's naive UTC time as local time. The opt-in end-to-end suite now runs on Windows (16 tests: Chrome found under Program Files and started with the real environment, VLC as the player, Ctrl+C through a console of its own, PowerShell 5.1 completion of an accented name). Co-Authored-By: Claude Opus 5.5 --- packages/meshbay-cli/src/meshbay_cli/commands/account.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'packages/meshbay-cli/src/meshbay_cli/commands/account.py') diff --git a/packages/meshbay-cli/src/meshbay_cli/commands/account.py b/packages/meshbay-cli/src/meshbay_cli/commands/account.py index 4499f0b..1fbef13 100644 --- a/packages/meshbay-cli/src/meshbay_cli/commands/account.py +++ b/packages/meshbay-cli/src/meshbay_cli/commands/account.py @@ -35,7 +35,7 @@ from ..client.passphrase import passphrase_problem, solve_captcha from ..client.rewrap import Carried, carry from ..client.store import FILE_KEY_NOTICE, KEY_FILE from ..errors import AUTH, NOT_FOUND, REFUSED, USAGE, CliError -from ..output import confirm, err, safe, sentence +from ..output import confirm, err, safe, sentence, terminal from .context import Context HELP = ("your account: show it, register, verify, change or reset the passphrase, " @@ -102,7 +102,7 @@ async def run(ctx: Context) -> int: def _ask(prompt: str, given: str | None, what: str) -> str: if given: return given.strip() - if not sys.stdin.isatty(): + if not terminal(sys.stdin): raise CliError(USAGE, f"Name the {what}: there is no terminal to ask on.") value = input(prompt).strip() if not value: @@ -121,7 +121,7 @@ def _read_first_line(path: str) -> str: def _new_passphrase(given: str | None) -> str: """A new passphrase: `given`, else typed twice at a terminal; held to the floor.""" if given is None: - if not sys.stdin.isatty(): + if not terminal(sys.stdin): raise NoPassphrase("A new passphrase is needed and there is no terminal to " "ask on.") given = getpass.getpass("New passphrase: ") @@ -224,7 +224,7 @@ async def _register(ctx: Context) -> int: "Keep it somewhere safe, apart from your passphrase. If you forget the " "passphrase, it is what gives you your groups back.") del raw - if not sys.stdin.isatty() or ctx.args.json: + if not terminal(sys.stdin) or ctx.args.json: err(f"A code was sent to {safe(email)}. Then: meshbay-cli account verify CODE " f"--email {safe(email)}") return 0 @@ -355,7 +355,7 @@ async def _reset(ctx: Context) -> int: raise _hub_refusal(e, "No code sent") from e err(f"If {safe(username)} and {safe(email)} are an account's, a code is on its " "way there (valid one hour).") - if not sys.stdin.isatty(): + if not terminal(sys.stdin): err(f"Then: meshbay-cli account reset -u {safe(username)} --code CODE") return 0 code = _ask("The code: ", None, "code") @@ -365,7 +365,7 @@ async def _reset(ctx: Context) -> int: text = (sys.stdin.read() if a.recovery_key_file == "-" else Path(a.recovery_key_file).read_text(encoding="utf-8")) recovery = text.strip() - elif sys.stdin.isatty(): + elif terminal(sys.stdin): recovery = input("Your recovery key (Enter if you have none): ").strip() rkey = None if recovery: @@ -429,7 +429,7 @@ async def _email(ctx: Context, account: Account) -> int: await account.call(account.hub.change_email, address, ak) except HubError as e: raise _hub_refusal(e, "Not changed") from e - if ctx.args.json or not sys.stdin.isatty(): + if ctx.args.json or not terminal(sys.stdin): _say(ctx, {"email_verification_required": True, "email": address}, f"A code was sent to {safe(address)}. Then: meshbay-cli account email " "--code CODE") -- cgit v1.2.3