Node's `os.platform()` returns `openharmony` on HarmonyPC and on
OpenHarmony's Linux ABI-compatible userspace. The npm wrapper's
platform-asset matrix only covered `linux` / `darwin` / `win32`,
so `npm i -g deepseek-tui` aborted on those hosts with
Unsupported platform: openharmony. Supported platforms: …
even though the existing Linux x64 / arm64 binaries run unchanged
on that environment (OpenHarmony is Linux-ABI-compatible at the
ELF level).
Added a `PLATFORM_ALIASES = { openharmony: "linux" }` indirection
that resolves the raw platform name through the alias map before
the `ASSET_MATRIX` lookup. Genuinely unsupported platforms still
report the raw `os.platform()` value in the error so OS-mismatch
bug reports stay diagnostic.
Four pure-JS regression tests pin the behaviour:
- openharmony x64 → linux x64 binaries
- openharmony arm64 → linux arm64 binaries
- known platforms unchanged by the alias map
- freebsd still reports `Unsupported platform: freebsd`
Harvested from PR #1499 by @CrepuscularIRIS
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary:
- Stop treating -v as an npm wrapper version fallback.
- Keep wrapper fallback for --version and -V.
- Add a Node regression test for wrapper version flag detection.
Test plan:
- npm test from npm/deepseek-tui
- git diff --check origin/main...HEAD
Supersedes #959.
CI on PR #684 caught two real issues that local checks missed:
**Lint failure (cargo fmt).** A regression test landed with a multi-line
`let ContentBlock::Text { text, .. } = real_user.content...` pattern
that local rustfmt accepted but CI's pinned toolchain collapsed onto a
single line. Reformatted to match.
**npm wrapper smoke failure ("Checksum manifest is missing
deepseek-<platform>").** Subtle Node.js streams interaction in
`install.js` introduced by the network-resilience cluster:
* `httpRequest` attaches a `data` event listener on the response to
re-arm the stall timer.
* Attaching a `data` listener on a `Readable` puts the stream into
flowing mode immediately.
* `downloadText` then ran `for await (const chunk of response)` to
collect the body — the async iterator expects paused-mode and
silently misses chunks that flow before / between iteration ticks.
* For small bodies (the ~100-byte SHA256 manifest), the entire
response could flow through the stall listener before the async
iterator's `read()` calls landed, leaving the joined body empty.
* Result: `parseChecksumManifest("")` returned an empty Map →
`verifyChecksum` saw no entries → "manifest is missing X" after
the actual binary download succeeded.
Binary downloads were unaffected because `download()` uses
`response.pipe(sink)` plus a `data` listener for progress — both
consume chunks via `data` events, no async iterator involved.
Fix: collect the response body in `downloadText` via direct `data`/
`end` event subscription. `data` listeners stack — both the stall
re-arm and the body collector fire on every chunk, no flowing-vs-
paused conflict. Stall detection still works.
Verified locally: `node scripts/release/npm-wrapper-smoke.js`
"npm wrapper smoke passed with local assets from <url>".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A community user from China reported `npm install deepseek-tui`
took 18 minutes through a CN npm mirror. The bottleneck is the
GitHub Releases binary fetch (~46 MB across two binaries), not the
npm tarball (which is 6.9 kB). The CN mirror does NOT proxy GitHub
release downloads, so any user behind a slow or lossy connection
is hitting the GitHub fetch directly with no resilience.
Four behaviors added to `npm/deepseek-tui/scripts/install.js`:
1. **Retry with exponential backoff.** Up to 5 attempts on network
errors (ECONNRESET, ECONNREFUSED, ETIMEDOUT, EAI_AGAIN,
network/host unreachable, EPIPE, ECONNABORTED) and 5xx upstream
responses. Backoff `1s, 2s, 4s, 8s, 16s` with ±20% jitter. 4xx
and checksum-mismatch are flagged non-retryable so we don't
thrash on permanent failures. Final error includes the underlying
message and the attempt count.
2. **Per-attempt total timeout + stall detector.** Total timeout
defaults to 5 minutes per attempt (`DEEPSEEK_TUI_DOWNLOAD_TIMEOUT_MS`,
alias `DEEPSEEK_DOWNLOAD_TIMEOUT_MS`). A stall detector aborts
the request when no bytes arrive for 30 s
(`DEEPSEEK_TUI_DOWNLOAD_STALL_MS`, alias
`DEEPSEEK_DOWNLOAD_STALL_MS`) so a hung connection doesn't waste
the whole timeout. Both budgets are surfaced in the error so the
user can dial them up if they're on a slow pipe.
3. **HTTPS_PROXY / HTTP_PROXY support — pure Node, no new
dependencies.** Detects `HTTPS_PROXY` / `HTTP_PROXY` (and the
lowercase variants) and routes through the proxy via CONNECT
tunneling. `NO_PROXY` exclusion list honored, with `*` and dotted-
suffix matching. Proxy auth via standard `user:pass@` URL form is
passed through as `Proxy-Authorization: Basic ...`. Pure-Node
implementation using `net` + `tls` + `http` + `https` builtins —
no `https-proxy-agent` dependency added.
4. **Download progress indicator.** Writes to stderr every ~1 MB
or every 2 s in TTY mode using `\r` to overwrite a single line.
Non-TTY mode (CI, piped) emits one line per 5 MB so logs stay
reasonable. Suppressed when `DEEPSEEK_TUI_QUIET_INSTALL=1` or
when `npm_config_loglevel` is `silent` or `error`. Falls back to
`N MB downloaded` when the response has no `Content-Length`.
Public API unchanged: existing callers of `getBinaryPath` and `run`
keep working identically when no new env vars are set. The escape
hatch `DEEPSEEK_TUI_DISABLE_INSTALL=1` still exits cleanly.
Verified locally:
* `node -c install.js` and module-load syntax checks.
* `DEEPSEEK_TUI_FORCE_DOWNLOAD=1 DEEPSEEK_TUI_VERSION=0.8.10 node
install.js` — real GitHub Releases download succeeded with
visible progress, both binaries landed.
* `HTTPS_PROXY=http://invalid.proxy.local:9999 ... node install.js`
— proxy path exercised, fails cleanly with the bad host named
in the error message after retries exhausted.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Linux installs currently succeed even when the host glibc is older than
what the prebuilt binary requires, leaving the user with a cryptic
`GLIBC_2.XX not found` runtime error.
Add a Linux-only preflight in `scripts/preflight-glibc.js` that runs
right after checksum verification:
- Read the highest required `GLIBC_X.Y` symbol from the downloaded
binary by scanning its bytes (no readelf dependency).
- Detect host glibc via `getconf GNU_LIBC_VERSION`, falling back to
`ldd --version`.
- If host < required, throw with a clear message pointing at the
build-from-source path (cargo install / git clone instructions).
- If glibc cannot be detected at all (musl/Alpine), surface the same
guidance instead of installing an incompatible binary.
- Skipped on macOS/Windows. `DEEPSEEK_TUI_SKIP_GLIBC_CHECK=1` (or the
legacy `DEEPSEEK_SKIP_GLIBC_CHECK=1`) bypasses the check.
The downloaded file is unlinked on failure, so a failed preflight
leaves nothing behind and npm exits non-zero.
Closes#560
Triggered by a Telegram report from a Chinese user trying to deploy
DeepSeek TUI on a HarmonyOS ARM64 thin-and-light: `npm i -g deepseek-tui`
exited with `Unsupported architecture: arm64 on platform linux` because
v0.8.7 only published x64 Linux artifacts. They worked around it with
`cargo install`, but the README never documented that path for ARM users.
This PR closes that gap on three layers:
- **Release workflow** — add `aarch64-unknown-linux-gnu` to the build
matrix using GitHub's `ubuntu-24.04-arm` runner. v0.8.8 will publish
`deepseek-linux-arm64` and `deepseek-tui-linux-arm64` alongside the
existing x64/macOS/Windows assets, plus add the row to the Release
body's manual-download table.
- **npm wrapper** — uncomment the linux/arm64 row in `ASSET_MATRIX`,
rewrite the `Unsupported architecture/platform` error to print the
full `cargo install deepseek-tui-cli deepseek-tui --locked` recipe
and link to docs/INSTALL.md, and add `DEEPSEEK_TUI_OPTIONAL_INSTALL=1`
so CI matrices that include unsupported platforms can keep running
without a binary.
- **Docs** — new docs/INSTALL.md covering every supported platform,
prebuilt vs. cargo install vs. manual download, cross-compiling x64
-> ARM64 with `cross` or `gcc-aarch64-linux-gnu`, China mirror setup,
and a troubleshooting section for the common arm64, MISSING_COMPANION_BINARY,
and self-update arch-mapping (#503) errors. README and README.zh-CN
now have an explicit Linux ARM64 quickstart pointing at `cargo install`
for v0.8.7 today and `npm i -g` for v0.8.8+; the v0.8.7 known-issue
block is updated to mention both #503 and the missing arm64 prebuilt.
https://claude.ai/code/session_01Fg1FKMtDxVnC4pp6bNBRCS
The wrapper re-downloaded the SHA-256 manifest from the GitHub release on every
invocation of `deepseek` / `deepseek-tui`, so any GitHub flake, captive portal,
proxy, or offline state broke every command — not just install.
Now ensureBinary returns immediately when the binary exists and its `.version`
marker matches. The manifest fetch is lazy and only runs when a download is
actually needed (first install or DEEPSEEK_TUI_FORCE_DOWNLOAD=1).
Bumps wrapper to 0.8.2; deepseekBinaryVersion stays on 0.8.1 (no new Rust
release required).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds first-class DeepSeek V4 Pro and Flash support, updates the default model to deepseek-v4-pro, aligns legacy aliases with the current V4 1M context behavior, and fixes thinking-mode request handling.
Key fixes:
- Send DeepSeek's raw Chat Completions `thinking` parameter at the top level instead of SDK-only `extra_body`.
- Preserve assistant `reasoning_content` for all prior thinking-mode tool-call turns so subsequent requests satisfy DeepSeek V4's replay requirement.
- Fix npm wrapper concurrent first-run downloads by using per-process temporary download paths.
- Add `.mailmap` so historical bot-attributed commits aggregate under Hunter Bown where mailmap is honored.
Verified with the full local Rust gate, live DeepSeek V4 smoke, npm wrapper temp-install smoke, and green PR CI across Linux, macOS, and Windows.
- Move src/* into crates/tui/src/ to create a proper workspace structure
- Add .claude/ and .trimtab/ directories for Trimtab closed-loop workflow
- Add DEPENDENCY_GRAPH.md and update documentation
- Update Cargo.toml files to reflect new crate dependencies
- Update CI workflows and npm package scripts
- All tests pass, release build works
- Add npm/deepseek-tui package that downloads prebuilt binaries from
GitHub releases (supports macOS, Linux, Windows)
- Published as deepseek-tui@0.3.28 on npmjs.com
- Update README to feature npm as primary install method
- Add npm badge