fix: v2.5.9 — Vietnamese in the terminal on macOS, every slash command, the right Claude account

Issue #62: a tmux client whose LC_ALL/LC_CTYPE/LANG names no UTF-8 prints `_`
for every non-ASCII character, and a macOS LaunchAgent starts the dashboard
with no locale at all — Vietnamese text and Claude Code's logo came out as
underscores in every browser terminal.

- shell-path.js: ensureUtf8Locale() defaults LC_CTYPE (en_US.UTF-8 on macOS,
  C.UTF-8 elsewhere) at start, before any tmux call; an explicit LC_ALL and
  Windows are left alone
- RunSetup.tsx: the `/` list now offers Claude Code's bundled skills
  (/code-review, /security-review, /simplify, /loop…) and newer built-ins, and
  every enabled plugin's commands and skills as /<plugin>:<name>
- Claude login decided by the TOKEN's owner (GET /api/oauth/profile, cached
  per token), not by .claude.json: a running session can leave the
  credentials store holding one account's token under another account's
  name. The Profiles modal, its Check quota and the status-bar quota now show
  the account requests are really billed to and warn on the mismatch; a
  switch saves the outgoing token into its OWNER's profile (credentials only)
  instead of overwriting the profile .claude.json names; saving a profile
  while they disagree is refused (EMISMATCH); a saved profile holding another
  account's token reports EWRONGACCOUNT instead of that account's usage
- Running Claude Code sessions follow a switch on their own (they re-read the
  credentials store when it changes), so the "close your sessions first"
  warning is gone
This commit is contained in:
2026-10-07 11:18:10 +07:00
parent 7543b8d62a
commit 8fdda66893
24 changed files with 504 additions and 59 deletions
+27 -2
View File
@@ -1,13 +1,14 @@
/**
* @file Tests for server/lib/shell-path.js — login-shell PATH hydration with a
* stubbed spawner (noisy rc output, errors, opt-out, dedupe) plus one real
* `/bin/sh` run to prove the argv works end to end.
* `/bin/sh` run to prove the argv works end to end, and the UTF-8 locale
* default that keeps tmux from printing `_` for every non-ASCII character.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
const { describe, it, beforeEach, afterEach } = require("node:test");
const assert = require("node:assert/strict");
const { hydrateShellPath, shellPath } = require("../lib/shell-path");
const { hydrateShellPath, shellPath, ensureUtf8Locale } = require("../lib/shell-path");
const M = "__AGENTHUB_PATH__";
const stub =
@@ -69,3 +70,27 @@ describe("hydrateShellPath", () => {
assert.ok(process.env.PATH.startsWith("/usr/bin:/bin"));
});
});
describe("ensureUtf8Locale", () => {
it("sets LC_CTYPE when no locale is set (a macOS LaunchAgent)", () => {
const env = {};
ensureUtf8Locale(env, "darwin");
assert.equal(env.LC_CTYPE, "en_US.UTF-8");
const linux = { LANG: "C" };
ensureUtf8Locale(linux, "linux");
assert.equal(linux.LC_CTYPE, "C.UTF-8");
});
it("leaves a UTF-8 locale, an explicit LC_ALL and Windows alone", () => {
for (const [env, platform] of [
[{ LANG: "vi_VN.UTF-8" }, "darwin"],
[{ LC_CTYPE: "en_US.utf8" }, "linux"],
[{ LC_ALL: "C" }, "darwin"],
[{}, "win32"],
]) {
const before = { ...env };
ensureUtf8Locale(env, platform);
assert.deepEqual(env, before);
}
});
});