feat(npm): publish as codewhale; keep deepseek-tui as deprecation shim
Rename the npm wrapper directory and package from `deepseek-tui` to
`codewhale`. Move under `npm/codewhale/`:
- `package.json` renamed (name, bin, internal field) — keeps a
`deepseekBinaryVersion` fallback so old metadata still works.
- Bin entry points renamed to `bin/codewhale.js` and
`bin/codewhale-tui.js`; they spawn the corresponding canonical
binaries via the wrapper.
- `scripts/artifacts.js` switches to the canonical asset-name matrix
(`codewhale-*`, `codewhale-tui-*`) and `codewhale-artifacts-sha256.txt`.
- `scripts/run.js` exports `runCodewhale` and `runCodewhaleTui`; the
legacy `runDeepseek` exports are gone since nothing else inside the
package depended on them.
- `scripts/install.js`, `verify-release-assets.js`, `preflight-glibc.js`
update brand-mention strings + User-Agent headers. Env vars
(`DEEPSEEK_TUI_*`, `DEEPSEEK_*`) are explicitly anti-scope and are
left in place.
- Tests retargeted at the canonical asset names; all 19 still pass.
- README rewritten with the new install command and a deprecation
note about the old package.
Create a one-release deprecation shim at `npm/deepseek-tui/`:
- `package.json` with no `bin`, just a postinstall script that
prints a clear message telling the user to install `codewhale`
instead.
- `README.md` with the same migration note.
- Will be removed in v0.9.0 (or whenever Hunter retires the shims).
Release-side scripts in `scripts/release/` follow the rename:
- `prepare-local-release-assets.js` now requires `npm/codewhale/...`
and copies the canonical `codewhale*` binaries.
- `npm-wrapper-smoke.js` smokes the renamed package.
- `check-versions.sh` reads `npm/codewhale/package.json` for the
primary check and additionally pins the legacy shim package to
the same version.
- `check-published.sh` queries `codewhale@<version>` (with
`codewhaleBinaryVersion` lookup that falls back to the legacy
`deepseekBinaryVersion` field).
- `.github/workflows/auto-tag.yml` watches both `npm/codewhale/` and
`npm/deepseek-tui/` package.json for auto-tag triggers.
Verified:
- `npm test` inside `npm/codewhale/` passes 19/19.
- `npm install --dry-run --ignore-scripts` succeeds for both
`npm/codewhale/` and `npm/deepseek-tui/`.
- `scripts/release/check-versions.sh` reports OK.
- Rust gates re-run: `cargo check`, `cargo fmt --check`,
`cargo clippy -- -D warnings`, all clean.
No `npm publish` is run from this change — Hunter publishes manually
when the rebrand is ready to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@ on:
|
|||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
paths:
|
||||||
- 'Cargo.toml'
|
- 'Cargo.toml'
|
||||||
|
- 'npm/codewhale/package.json'
|
||||||
- 'npm/deepseek-tui/package.json'
|
- 'npm/deepseek-tui/package.json'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
# codewhale
|
||||||
|
|
||||||
|
Install and run the `codewhale` and `codewhale-tui` binaries from GitHub release artifacts.
|
||||||
|
|
||||||
|
> Previously published as `deepseek-tui`. See `docs/REBRAND.md` in the upstream
|
||||||
|
> repository for the migration notes; the legacy `deepseek-tui` npm package
|
||||||
|
> still exists as a deprecation shim for one release cycle.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install -g codewhale
|
||||||
|
# or
|
||||||
|
pnpm add -g codewhale
|
||||||
|
```
|
||||||
|
|
||||||
|
For project-local usage:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install codewhale
|
||||||
|
npx codewhale --help
|
||||||
|
```
|
||||||
|
|
||||||
|
`postinstall` tries to download platform binaries into `bin/downloads/` and
|
||||||
|
exposes `codewhale` and `codewhale-tui` commands. If GitHub release assets are
|
||||||
|
temporarily unreachable, install continues and the wrapper retries the download
|
||||||
|
on first run.
|
||||||
|
|
||||||
|
## First run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
codewhale login --api-key "YOUR_DEEPSEEK_API_KEY"
|
||||||
|
codewhale doctor
|
||||||
|
codewhale
|
||||||
|
```
|
||||||
|
|
||||||
|
The `codewhale` facade and `codewhale-tui` binary share `~/.deepseek/config.toml`
|
||||||
|
for DeepSeek auth and default model settings. Common TUI commands are available
|
||||||
|
directly through the facade, including `codewhale doctor`, `codewhale models`,
|
||||||
|
`codewhale sessions`, and `codewhale resume --last`.
|
||||||
|
|
||||||
|
The app talks to DeepSeek's documented OpenAI-compatible Chat Completions API.
|
||||||
|
Set `DEEPSEEK_BASE_URL` only if you need the China endpoint or DeepSeek beta
|
||||||
|
features such as strict tool mode, chat prefix completion, or FIM completion.
|
||||||
|
|
||||||
|
NVIDIA NIM-hosted DeepSeek V4 Pro is also supported:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
codewhale auth set --provider nvidia-nim --api-key "YOUR_NVIDIA_API_KEY"
|
||||||
|
codewhale --provider nvidia-nim
|
||||||
|
```
|
||||||
|
|
||||||
|
For a single process, set `DEEPSEEK_PROVIDER=nvidia-nim` and `NVIDIA_API_KEY`
|
||||||
|
or `NVIDIA_NIM_API_KEY` (with `DEEPSEEK_API_KEY` as a compatibility fallback).
|
||||||
|
The NIM default model is `deepseek-ai/deepseek-v4-pro` and the default base URL
|
||||||
|
is `https://integrate.api.nvidia.com/v1`. With `--provider nvidia-nim`,
|
||||||
|
`--model deepseek-v4-flash` maps to `deepseek-ai/deepseek-v4-flash`.
|
||||||
|
|
||||||
|
## Supported platforms
|
||||||
|
|
||||||
|
Prebuilt binaries for the GitHub release are downloaded automatically:
|
||||||
|
|
||||||
|
- Linux x64
|
||||||
|
- Linux arm64 (v0.8.8+)
|
||||||
|
- macOS x64 / arm64
|
||||||
|
- Windows x64
|
||||||
|
|
||||||
|
Other platform/architecture combinations (musl, riscv64, FreeBSD, …) aren't
|
||||||
|
shipped as prebuilts. Unsupported platforms, checksum failures, and glibc
|
||||||
|
compatibility problems still fail with a clear error pointing you at
|
||||||
|
`cargo install codewhale-cli codewhale-tui --locked` and the full
|
||||||
|
[docs/INSTALL.md](https://github.com/Hmbown/DeepSeek-TUI/blob/main/docs/INSTALL.md)
|
||||||
|
build-from-source guide.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
- Default binary version comes from `codewhaleBinaryVersion` in `package.json`
|
||||||
|
(with `deepseekBinaryVersion` as a backward-compat fallback).
|
||||||
|
- Set `DEEPSEEK_TUI_VERSION` or `DEEPSEEK_VERSION` to override the release version.
|
||||||
|
- Set `DEEPSEEK_TUI_GITHUB_REPO` or `DEEPSEEK_GITHUB_REPO` to override the source repo (defaults to `Hmbown/DeepSeek-TUI`).
|
||||||
|
- Set `DEEPSEEK_TUI_RELEASE_BASE_URL` to use an internal or mirrored
|
||||||
|
release-asset directory when GitHub Releases is unavailable. The directory
|
||||||
|
must contain `codewhale-artifacts-sha256.txt` and the platform binaries.
|
||||||
|
- Set `DEEPSEEK_TUI_FORCE_DOWNLOAD=1` to force download even when the cached binary is already present.
|
||||||
|
- Set `DEEPSEEK_TUI_DISABLE_INSTALL=1` to skip install-time download.
|
||||||
|
- Set `DEEPSEEK_TUI_OPTIONAL_INSTALL=1` to make install-time retryable download
|
||||||
|
failures warn and exit `0` instead of failing `npm install`.
|
||||||
|
|
||||||
|
## Release integrity
|
||||||
|
|
||||||
|
- `npm publish` runs a release-asset check to ensure all required binary assets
|
||||||
|
exist for the target GitHub release before publishing.
|
||||||
|
- Install-time downloads are verified against the release checksum manifest before
|
||||||
|
the wrapper marks them executable.
|
||||||
Executable
+8
@@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const { runCodewhaleTui } = require("../scripts/run");
|
||||||
|
|
||||||
|
runCodewhaleTui().catch((error) => {
|
||||||
|
console.error("Failed to start codewhale-tui:", error.message);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
Executable
+8
@@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const { runCodewhale } = require("../scripts/run");
|
||||||
|
|
||||||
|
runCodewhale().catch((error) => {
|
||||||
|
console.error("Failed to start codewhale:", error.message);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"name": "codewhale",
|
||||||
|
"version": "0.8.40",
|
||||||
|
"codewhaleBinaryVersion": "0.8.40",
|
||||||
|
"description": "Install and run the codewhale CLI dispatcher and codewhale-tui terminal UI from GitHub release artifacts.",
|
||||||
|
"author": "Hmbown",
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://github.com/Hmbown/DeepSeek-TUI",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/Hmbown/DeepSeek-TUI.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/Hmbown/DeepSeek-TUI/issues"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"codewhale",
|
||||||
|
"deepseek",
|
||||||
|
"cli",
|
||||||
|
"tui",
|
||||||
|
"rust",
|
||||||
|
"binary",
|
||||||
|
"terminal"
|
||||||
|
],
|
||||||
|
"type": "commonjs",
|
||||||
|
"bin": {
|
||||||
|
"codewhale": "bin/codewhale.js",
|
||||||
|
"codewhale-tui": "bin/codewhale-tui.js"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"release:check": "node scripts/verify-release-assets.js",
|
||||||
|
"postinstall": "node scripts/install.js --optional",
|
||||||
|
"prepublishOnly": "node scripts/verify-release-assets.js",
|
||||||
|
"prepack": "node scripts/install.js",
|
||||||
|
"test": "node --test test/*.test.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"preferGlobal": true,
|
||||||
|
"files": [
|
||||||
|
"bin/*.js",
|
||||||
|
"scripts/*.js",
|
||||||
|
"test/*.js",
|
||||||
|
"README.md",
|
||||||
|
"package.json"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
const os = require("os");
|
const os = require("os");
|
||||||
|
|
||||||
const CHECKSUM_MANIFEST = "deepseek-artifacts-sha256.txt";
|
const CHECKSUM_MANIFEST = "codewhale-artifacts-sha256.txt";
|
||||||
|
|
||||||
const ASSET_MATRIX = {
|
const ASSET_MATRIX = {
|
||||||
linux: {
|
linux: {
|
||||||
x64: ["deepseek-linux-x64", "deepseek-tui-linux-x64"],
|
x64: ["codewhale-linux-x64", "codewhale-tui-linux-x64"],
|
||||||
arm64: ["deepseek-linux-arm64", "deepseek-tui-linux-arm64"],
|
arm64: ["codewhale-linux-arm64", "codewhale-tui-linux-arm64"],
|
||||||
},
|
},
|
||||||
darwin: {
|
darwin: {
|
||||||
x64: ["deepseek-macos-x64", "deepseek-tui-macos-x64"],
|
x64: ["codewhale-macos-x64", "codewhale-tui-macos-x64"],
|
||||||
arm64: ["deepseek-macos-arm64", "deepseek-tui-macos-arm64"],
|
arm64: ["codewhale-macos-arm64", "codewhale-tui-macos-arm64"],
|
||||||
},
|
},
|
||||||
win32: {
|
win32: {
|
||||||
x64: ["deepseek-windows-x64.exe", "deepseek-tui-windows-x64.exe"],
|
x64: ["codewhale-windows-x64.exe", "codewhale-tui-windows-x64.exe"],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ function detectBinaryNames() {
|
|||||||
return {
|
return {
|
||||||
platform,
|
platform,
|
||||||
arch,
|
arch,
|
||||||
deepseek: pair[0],
|
codewhale: pair[0],
|
||||||
tui: pair[1],
|
tui: pair[1],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -55,11 +55,11 @@ function detectBinaryNames() {
|
|||||||
function unsupportedBuildHint() {
|
function unsupportedBuildHint() {
|
||||||
return [
|
return [
|
||||||
"No prebuilt binary is available for this platform/architecture combo.",
|
"No prebuilt binary is available for this platform/architecture combo.",
|
||||||
"You can still run DeepSeek TUI by building from source with Cargo:",
|
"You can still run codewhale by building from source with Cargo:",
|
||||||
"",
|
"",
|
||||||
" # Requires Rust 1.88+ (https://rustup.rs)",
|
" # Requires Rust 1.88+ (https://rustup.rs)",
|
||||||
" cargo install deepseek-tui-cli --locked # provides `deepseek`",
|
" cargo install codewhale-cli --locked # provides `codewhale`",
|
||||||
" cargo install deepseek-tui --locked # provides `deepseek-tui`",
|
" cargo install codewhale-tui --locked # provides `codewhale-tui`",
|
||||||
"",
|
"",
|
||||||
"Or build from a checkout:",
|
"Or build from a checkout:",
|
||||||
"",
|
"",
|
||||||
@@ -3,9 +3,9 @@ function assertSupportedNode() {
|
|||||||
const major = Number.parseInt(String(version).split(".")[0], 10);
|
const major = Number.parseInt(String(version).split(".")[0], 10);
|
||||||
if (Number.isNaN(major) || major < 18) {
|
if (Number.isNaN(major) || major < 18) {
|
||||||
process.stderr.write(
|
process.stderr.write(
|
||||||
"deepseek-tui: Node.js 18 or newer is required for npm installation. " +
|
"codewhale: Node.js 18 or newer is required for npm installation. " +
|
||||||
`Current Node.js version is ${version}. ` +
|
`Current Node.js version is ${version}. ` +
|
||||||
"Please upgrade Node.js and rerun `npm install -g deepseek-tui`.\n",
|
"Please upgrade Node.js and rerun `npm install -g codewhale`.\n",
|
||||||
);
|
);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
@@ -136,16 +136,16 @@ function maxAttempts(context = "runtime", env = process.env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function binaryPaths() {
|
function binaryPaths() {
|
||||||
const { deepseek, tui } = detectBinaryNames();
|
const { codewhale, tui } = detectBinaryNames();
|
||||||
const releaseDir = releaseBinaryDirectory();
|
const releaseDir = releaseBinaryDirectory();
|
||||||
return {
|
return {
|
||||||
deepseek: {
|
codewhale: {
|
||||||
asset: deepseek,
|
asset: codewhale,
|
||||||
target: path.join(releaseDir, process.platform === "win32" ? "deepseek.exe" : "deepseek"),
|
target: path.join(releaseDir, process.platform === "win32" ? "codewhale.exe" : "codewhale"),
|
||||||
},
|
},
|
||||||
tui: {
|
tui: {
|
||||||
asset: tui,
|
asset: tui,
|
||||||
target: path.join(releaseDir, process.platform === "win32" ? "deepseek-tui.exe" : "deepseek-tui"),
|
target: path.join(releaseDir, process.platform === "win32" ? "codewhale-tui.exe" : "codewhale-tui"),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -166,7 +166,7 @@ function logInfo(message) {
|
|||||||
if (isQuietInstall()) {
|
if (isQuietInstall()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
process.stderr.write(`deepseek-tui: ${message}\n`);
|
process.stderr.write(`codewhale: ${message}\n`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function installFailureHint(error) {
|
function installFailureHint(error) {
|
||||||
@@ -194,19 +194,19 @@ function installFailureHint(error) {
|
|||||||
|
|
||||||
if (releaseBase) {
|
if (releaseBase) {
|
||||||
return [
|
return [
|
||||||
"deepseek-tui install hint:",
|
"codewhale install hint:",
|
||||||
` DEEPSEEK_TUI_RELEASE_BASE_URL is set to ${releaseBase}`,
|
` DEEPSEEK_TUI_RELEASE_BASE_URL is set to ${releaseBase}`,
|
||||||
" Verify that this directory contains deepseek-artifacts-sha256.txt",
|
" Verify that this directory contains codewhale-artifacts-sha256.txt",
|
||||||
" plus the deepseek/deepseek-tui binary assets for your platform.",
|
" plus the codewhale/codewhale-tui binary assets for your platform.",
|
||||||
].join("\n");
|
].join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
"deepseek-tui install hint:",
|
"codewhale install hint:",
|
||||||
" The npm package downloads prebuilt binaries from GitHub Releases.",
|
" The npm package downloads prebuilt binaries from GitHub Releases.",
|
||||||
" If GitHub is unavailable on this network, mirror the release assets and set:",
|
" If GitHub is unavailable on this network, mirror the release assets and set:",
|
||||||
" DEEPSEEK_TUI_RELEASE_BASE_URL=https://<mirror>/<release-asset-directory>/",
|
" DEEPSEEK_TUI_RELEASE_BASE_URL=https://<mirror>/<release-asset-directory>/",
|
||||||
" The directory must contain deepseek-artifacts-sha256.txt and the platform binaries.",
|
" The directory must contain codewhale-artifacts-sha256.txt and the platform binaries.",
|
||||||
" See docs/INSTALL.md#npm-binary-download-times-out.",
|
" See docs/INSTALL.md#npm-binary-download-times-out.",
|
||||||
].join("\n");
|
].join("\n");
|
||||||
}
|
}
|
||||||
@@ -258,14 +258,14 @@ function createProgressReporter(assetName, totalBytes) {
|
|||||||
const render = (final) => {
|
const render = (final) => {
|
||||||
if (totalBytes && totalBytes > 0) {
|
if (totalBytes && totalBytes > 0) {
|
||||||
const pct = Math.min(100, Math.round((received / totalBytes) * 100));
|
const pct = Math.min(100, Math.round((received / totalBytes) * 100));
|
||||||
const line = `deepseek-tui: downloading ${assetName}: ${formatMb(received)} / ${formatMb(totalBytes)} MB (${pct}%)`;
|
const line = `codewhale: downloading ${assetName}: ${formatMb(received)} / ${formatMb(totalBytes)} MB (${pct}%)`;
|
||||||
if (interactive) {
|
if (interactive) {
|
||||||
process.stderr.write(`${line}\r`);
|
process.stderr.write(`${line}\r`);
|
||||||
} else {
|
} else {
|
||||||
process.stderr.write(`${line}\n`);
|
process.stderr.write(`${line}\n`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const line = `deepseek-tui: downloading ${assetName}: ${formatMb(received)} MB downloaded`;
|
const line = `codewhale: downloading ${assetName}: ${formatMb(received)} MB downloaded`;
|
||||||
if (interactive) {
|
if (interactive) {
|
||||||
process.stderr.write(`${line}\r`);
|
process.stderr.write(`${line}\r`);
|
||||||
} else {
|
} else {
|
||||||
@@ -295,7 +295,7 @@ function createProgressReporter(assetName, totalBytes) {
|
|||||||
// Move past the carriage-return line and emit a "done" footer.
|
// Move past the carriage-return line and emit a "done" footer.
|
||||||
process.stderr.write("\n");
|
process.stderr.write("\n");
|
||||||
}
|
}
|
||||||
process.stderr.write(`deepseek-tui: ${assetName} ... done.\n`);
|
process.stderr.write(`codewhale: ${assetName} ... done.\n`);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -406,7 +406,7 @@ function connectThroughProxy(proxy, targetHost, targetPort, timeoutMs) {
|
|||||||
const lines = [
|
const lines = [
|
||||||
`CONNECT ${targetHost}:${targetPort} HTTP/1.1`,
|
`CONNECT ${targetHost}:${targetPort} HTTP/1.1`,
|
||||||
`Host: ${targetHost}:${targetPort}`,
|
`Host: ${targetHost}:${targetPort}`,
|
||||||
"User-Agent: deepseek-tui-installer",
|
"User-Agent: codewhale-installer",
|
||||||
"Proxy-Connection: keep-alive",
|
"Proxy-Connection: keep-alive",
|
||||||
];
|
];
|
||||||
if (proxy.auth) {
|
if (proxy.auth) {
|
||||||
@@ -555,7 +555,7 @@ function httpRequest(rawUrl, opts = {}) {
|
|||||||
path: `${url.pathname}${url.search || ""}`,
|
path: `${url.pathname}${url.search || ""}`,
|
||||||
headers: {
|
headers: {
|
||||||
Host: url.host,
|
Host: url.host,
|
||||||
"User-Agent": "deepseek-tui-installer",
|
"User-Agent": "codewhale-installer",
|
||||||
Accept: "*/*",
|
Accept: "*/*",
|
||||||
Connection: "close",
|
Connection: "close",
|
||||||
},
|
},
|
||||||
@@ -641,7 +641,7 @@ function httpRequest(rawUrl, opts = {}) {
|
|||||||
path: rawUrl,
|
path: rawUrl,
|
||||||
headers: {
|
headers: {
|
||||||
Host: url.host,
|
Host: url.host,
|
||||||
"User-Agent": "deepseek-tui-installer",
|
"User-Agent": "codewhale-installer",
|
||||||
Accept: "*/*",
|
Accept: "*/*",
|
||||||
Connection: "close",
|
Connection: "close",
|
||||||
...(proxy.auth ? { "Proxy-Authorization": `Basic ${proxy.auth}` } : {}),
|
...(proxy.auth ? { "Proxy-Authorization": `Basic ${proxy.auth}` } : {}),
|
||||||
@@ -704,7 +704,7 @@ function httpRequest(rawUrl, opts = {}) {
|
|||||||
path: `${url.pathname}${url.search || ""}`,
|
path: `${url.pathname}${url.search || ""}`,
|
||||||
headers: {
|
headers: {
|
||||||
Host: url.host,
|
Host: url.host,
|
||||||
"User-Agent": "deepseek-tui-installer",
|
"User-Agent": "codewhale-installer",
|
||||||
Accept: "*/*",
|
Accept: "*/*",
|
||||||
Connection: "close",
|
Connection: "close",
|
||||||
},
|
},
|
||||||
@@ -1122,7 +1122,7 @@ async function run(options = {}) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
ensureBinary(paths.deepseek.target, paths.deepseek.asset, version, repo, getChecksums, { context }),
|
ensureBinary(paths.codewhale.target, paths.codewhale.asset, version, repo, getChecksums, { context }),
|
||||||
ensureBinary(paths.tui.target, paths.tui.asset, version, repo, getChecksums, { context }),
|
ensureBinary(paths.tui.target, paths.tui.asset, version, repo, getChecksums, { context }),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -1130,10 +1130,10 @@ async function run(options = {}) {
|
|||||||
async function getBinaryPath(name) {
|
async function getBinaryPath(name) {
|
||||||
await run({ context: "runtime" });
|
await run({ context: "runtime" });
|
||||||
const paths = binaryPaths();
|
const paths = binaryPaths();
|
||||||
if (name === "deepseek") {
|
if (name === "codewhale") {
|
||||||
return paths.deepseek.target;
|
return paths.codewhale.target;
|
||||||
}
|
}
|
||||||
if (name === "deepseek-tui") {
|
if (name === "codewhale-tui") {
|
||||||
return paths.tui.target;
|
return paths.tui.target;
|
||||||
}
|
}
|
||||||
throw new Error(`Unknown binary: ${name}`);
|
throw new Error(`Unknown binary: ${name}`);
|
||||||
@@ -1158,7 +1158,7 @@ module.exports = {
|
|||||||
|
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
run({ context: "install" }).catch((error) => {
|
run({ context: "install" }).catch((error) => {
|
||||||
console.error("deepseek-tui install failed:", error.message);
|
console.error("codewhale install failed:", error.message);
|
||||||
const hint = installFailureHint(error);
|
const hint = installFailureHint(error);
|
||||||
if (hint) {
|
if (hint) {
|
||||||
console.error(hint);
|
console.error(hint);
|
||||||
+3
-3
@@ -66,11 +66,11 @@ function detectBinaryRequiredGlibc(filePath) {
|
|||||||
|
|
||||||
function buildFromSourceHint() {
|
function buildFromSourceHint() {
|
||||||
return [
|
return [
|
||||||
"You can still run DeepSeek TUI by building from source with Cargo:",
|
"You can still run codewhale by building from source with Cargo:",
|
||||||
"",
|
"",
|
||||||
" # Requires Rust 1.88+ (https://rustup.rs)",
|
" # Requires Rust 1.88+ (https://rustup.rs)",
|
||||||
" cargo install deepseek-tui-cli --locked # provides `deepseek`",
|
" cargo install codewhale-cli --locked # provides `codewhale`",
|
||||||
" cargo install deepseek-tui --locked # provides `deepseek-tui`",
|
" cargo install codewhale-tui --locked # provides `codewhale-tui`",
|
||||||
"",
|
"",
|
||||||
"Or build from a checkout:",
|
"Or build from a checkout:",
|
||||||
"",
|
"",
|
||||||
@@ -9,7 +9,8 @@ function isVersionFlag(args = process.argv.slice(2)) {
|
|||||||
|
|
||||||
function handleVersionFallback(binaryName) {
|
function handleVersionFallback(binaryName) {
|
||||||
if (isVersionFlag()) {
|
if (isVersionFlag()) {
|
||||||
const binVersion = pkg.deepseekBinaryVersion || pkg.version;
|
const binVersion =
|
||||||
|
pkg.codewhaleBinaryVersion || pkg.deepseekBinaryVersion || pkg.version;
|
||||||
console.log(`${binaryName} (npm wrapper) v${pkg.version}`);
|
console.log(`${binaryName} (npm wrapper) v${pkg.version}`);
|
||||||
console.log(`binary version: v${binVersion}`);
|
console.log(`binary version: v${binVersion}`);
|
||||||
console.log(`repo: ${pkg.repository?.url || "N/A"}`);
|
console.log(`repo: ${pkg.repository?.url || "N/A"}`);
|
||||||
@@ -33,26 +34,26 @@ async function run(binaryName) {
|
|||||||
process.exit(result.status ?? 1);
|
process.exit(result.status ?? 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runDeepseek() {
|
async function runCodewhale() {
|
||||||
await run("deepseek");
|
await run("codewhale");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runDeepseekTui() {
|
async function runCodewhaleTui() {
|
||||||
await run("deepseek-tui");
|
await run("codewhale-tui");
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
run,
|
run,
|
||||||
runDeepseek,
|
runCodewhale,
|
||||||
runDeepseekTui,
|
runCodewhaleTui,
|
||||||
_internal: { isVersionFlag },
|
_internal: { isVersionFlag },
|
||||||
};
|
};
|
||||||
|
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
const command = process.argv[1] || "";
|
const command = process.argv[1] || "";
|
||||||
if (command.includes("tui")) {
|
if (command.includes("tui")) {
|
||||||
runDeepseekTui();
|
runCodewhaleTui();
|
||||||
} else {
|
} else {
|
||||||
runDeepseek();
|
runCodewhale();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3
-3
@@ -13,7 +13,7 @@ function resolveBinaryVersion() {
|
|||||||
const configuredVersion =
|
const configuredVersion =
|
||||||
process.env.DEEPSEEK_TUI_VERSION ||
|
process.env.DEEPSEEK_TUI_VERSION ||
|
||||||
process.env.DEEPSEEK_VERSION ||
|
process.env.DEEPSEEK_VERSION ||
|
||||||
pkg.deepseekBinaryVersion ||
|
pkg.codewhaleBinaryVersion || pkg.deepseekBinaryVersion ||
|
||||||
pkg.version;
|
pkg.version;
|
||||||
return String(configuredVersion).trim();
|
return String(configuredVersion).trim();
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,7 @@ function requestStatus(url, method = "HEAD", redirects = 0) {
|
|||||||
{
|
{
|
||||||
method,
|
method,
|
||||||
headers: {
|
headers: {
|
||||||
"User-Agent": "deepseek-tui-npm-release-check",
|
"User-Agent": "codewhale-npm-release-check",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
(res) => {
|
(res) => {
|
||||||
@@ -71,7 +71,7 @@ async function downloadText(url) {
|
|||||||
url,
|
url,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"User-Agent": "deepseek-tui-npm-release-check",
|
"User-Agent": "codewhale-npm-release-check",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
(res) => {
|
(res) => {
|
||||||
@@ -24,8 +24,8 @@ test("openharmony x64 resolves to linux x64 binaries", () => {
|
|||||||
withMockedOs("openharmony", "x64", () => {
|
withMockedOs("openharmony", "x64", () => {
|
||||||
const { detectBinaryNames } = require(ARTIFACTS_PATH);
|
const { detectBinaryNames } = require(ARTIFACTS_PATH);
|
||||||
const result = detectBinaryNames();
|
const result = detectBinaryNames();
|
||||||
assert.equal(result.deepseek, "deepseek-linux-x64");
|
assert.equal(result.codewhale, "codewhale-linux-x64");
|
||||||
assert.equal(result.tui, "deepseek-tui-linux-x64");
|
assert.equal(result.tui, "codewhale-tui-linux-x64");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -33,8 +33,8 @@ test("openharmony arm64 resolves to linux arm64 binaries", () => {
|
|||||||
withMockedOs("openharmony", "arm64", () => {
|
withMockedOs("openharmony", "arm64", () => {
|
||||||
const { detectBinaryNames } = require(ARTIFACTS_PATH);
|
const { detectBinaryNames } = require(ARTIFACTS_PATH);
|
||||||
const result = detectBinaryNames();
|
const result = detectBinaryNames();
|
||||||
assert.equal(result.deepseek, "deepseek-linux-arm64");
|
assert.equal(result.codewhale, "codewhale-linux-arm64");
|
||||||
assert.equal(result.tui, "deepseek-tui-linux-arm64");
|
assert.equal(result.tui, "codewhale-tui-linux-arm64");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -52,15 +52,15 @@ test("genuinely unsupported platform throws with raw platform name", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("known platforms are unaffected by alias map", () => {
|
test("known platforms are unaffected by alias map", () => {
|
||||||
for (const [platform, arch, expectedDeepseek] of [
|
for (const [platform, arch, expectedCodewhale] of [
|
||||||
["linux", "x64", "deepseek-linux-x64"],
|
["linux", "x64", "codewhale-linux-x64"],
|
||||||
["darwin", "arm64", "deepseek-macos-arm64"],
|
["darwin", "arm64", "codewhale-macos-arm64"],
|
||||||
["win32", "x64", "deepseek-windows-x64.exe"],
|
["win32", "x64", "codewhale-windows-x64.exe"],
|
||||||
]) {
|
]) {
|
||||||
withMockedOs(platform, arch, () => {
|
withMockedOs(platform, arch, () => {
|
||||||
const { detectBinaryNames } = require(ARTIFACTS_PATH);
|
const { detectBinaryNames } = require(ARTIFACTS_PATH);
|
||||||
const result = detectBinaryNames();
|
const result = detectBinaryNames();
|
||||||
assert.equal(result.deepseek, expectedDeepseek);
|
assert.equal(result.codewhale, expectedCodewhale);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -16,7 +16,7 @@ function sha256(content) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function makeTempDir(t) {
|
async function makeTempDir(t) {
|
||||||
const dir = await fs.promises.mkdtemp(path.join(os.tmpdir(), "deepseek-install-test-"));
|
const dir = await fs.promises.mkdtemp(path.join(os.tmpdir(), "codewhale-install-test-"));
|
||||||
t.after(() => fs.promises.rm(dir, { force: true, recursive: true }));
|
t.after(() => fs.promises.rm(dir, { force: true, recursive: true }));
|
||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
@@ -69,7 +69,7 @@ test("install failure hint explains release base override for blocked GitHub dow
|
|||||||
try {
|
try {
|
||||||
const error = Object.assign(
|
const error = Object.assign(
|
||||||
new Error(
|
new Error(
|
||||||
"fetch https://github.com/Hmbown/DeepSeek-TUI/releases/download/v0.8.19/deepseek-artifacts-sha256.txt failed after 5 attempts:\ngetaddrinfo ENOTFOUND github.com",
|
"fetch https://github.com/Hmbown/DeepSeek-TUI/releases/download/v0.8.19/codewhale-artifacts-sha256.txt failed after 5 attempts:\ngetaddrinfo ENOTFOUND github.com",
|
||||||
),
|
),
|
||||||
{ code: "ENOTFOUND" },
|
{ code: "ENOTFOUND" },
|
||||||
);
|
);
|
||||||
@@ -77,7 +77,7 @@ test("install failure hint explains release base override for blocked GitHub dow
|
|||||||
const hint = installFailureHint(error);
|
const hint = installFailureHint(error);
|
||||||
|
|
||||||
assert.match(hint, /DEEPSEEK_TUI_RELEASE_BASE_URL/);
|
assert.match(hint, /DEEPSEEK_TUI_RELEASE_BASE_URL/);
|
||||||
assert.match(hint, /deepseek-artifacts-sha256\.txt/);
|
assert.match(hint, /codewhale-artifacts-sha256\.txt/);
|
||||||
assert.match(hint, /platform binaries/);
|
assert.match(hint, /platform binaries/);
|
||||||
assert.match(hint, /#npm-binary-download-times-out/);
|
assert.match(hint, /#npm-binary-download-times-out/);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -100,7 +100,7 @@ test("install failure hint checks configured release base when override is alrea
|
|||||||
const hint = installFailureHint(error);
|
const hint = installFailureHint(error);
|
||||||
|
|
||||||
assert.match(hint, /is set to https:\/\/mirror\.example\/deepseek\//);
|
assert.match(hint, /is set to https:\/\/mirror\.example\/deepseek\//);
|
||||||
assert.match(hint, /deepseek-artifacts-sha256\.txt/);
|
assert.match(hint, /codewhale-artifacts-sha256\.txt/);
|
||||||
assert.doesNotMatch(hint, /If GitHub is unavailable/);
|
assert.doesNotMatch(hint, /If GitHub is unavailable/);
|
||||||
} finally {
|
} finally {
|
||||||
if (previous === undefined) {
|
if (previous === undefined) {
|
||||||
@@ -113,10 +113,10 @@ test("install failure hint checks configured release base when override is alrea
|
|||||||
|
|
||||||
test("ensureBinary adopts a manually placed target binary after checksum validation", async (t) => {
|
test("ensureBinary adopts a manually placed target binary after checksum validation", async (t) => {
|
||||||
const dir = await makeTempDir(t);
|
const dir = await makeTempDir(t);
|
||||||
const target = path.join(dir, process.platform === "win32" ? "deepseek.exe" : "deepseek");
|
const target = path.join(dir, process.platform === "win32" ? "codewhale.exe" : "codewhale");
|
||||||
const assetName = process.platform === "win32" ? "deepseek-windows-x64.exe" : "deepseek-linux-x64";
|
const assetName = process.platform === "win32" ? "codewhale-windows-x64.exe" : "codewhale-linux-x64";
|
||||||
const version = "0.8.25";
|
const version = "0.8.25";
|
||||||
const content = Buffer.from("manual deepseek binary");
|
const content = Buffer.from("manual codewhale binary");
|
||||||
let checksumLoads = 0;
|
let checksumLoads = 0;
|
||||||
|
|
||||||
await fs.promises.writeFile(target, content, { mode: 0o600 });
|
await fs.promises.writeFile(target, content, { mode: 0o600 });
|
||||||
@@ -139,8 +139,8 @@ test("ensureBinary adopts a manually placed target binary after checksum validat
|
|||||||
|
|
||||||
test("ensureBinary adopts an official release-named binary placed in downloads", async (t) => {
|
test("ensureBinary adopts an official release-named binary placed in downloads", async (t) => {
|
||||||
const dir = await makeTempDir(t);
|
const dir = await makeTempDir(t);
|
||||||
const target = path.join(dir, process.platform === "win32" ? "deepseek.exe" : "deepseek");
|
const target = path.join(dir, process.platform === "win32" ? "codewhale.exe" : "codewhale");
|
||||||
const assetName = process.platform === "win32" ? "deepseek-windows-x64.exe" : "deepseek-linux-x64";
|
const assetName = process.platform === "win32" ? "codewhale-windows-x64.exe" : "codewhale-linux-x64";
|
||||||
const assetPath = path.join(dir, assetName);
|
const assetPath = path.join(dir, assetName);
|
||||||
const version = "0.8.25";
|
const version = "0.8.25";
|
||||||
const content = Buffer.from("official release binary");
|
const content = Buffer.from("official release binary");
|
||||||
@@ -161,8 +161,8 @@ test("ensureBinary adopts an official release-named binary placed in downloads",
|
|||||||
|
|
||||||
test("manual binaries with mismatched checksums are not adopted", async (t) => {
|
test("manual binaries with mismatched checksums are not adopted", async (t) => {
|
||||||
const dir = await makeTempDir(t);
|
const dir = await makeTempDir(t);
|
||||||
const target = path.join(dir, process.platform === "win32" ? "deepseek.exe" : "deepseek");
|
const target = path.join(dir, process.platform === "win32" ? "codewhale.exe" : "codewhale");
|
||||||
const assetName = process.platform === "win32" ? "deepseek-windows-x64.exe" : "deepseek-linux-x64";
|
const assetName = process.platform === "win32" ? "codewhale-windows-x64.exe" : "codewhale-linux-x64";
|
||||||
const content = Buffer.from("wrong binary bytes");
|
const content = Buffer.from("wrong binary bytes");
|
||||||
|
|
||||||
await fs.promises.writeFile(target, content);
|
await fs.promises.writeFile(target, content);
|
||||||
@@ -71,7 +71,7 @@ test("optional install only swallows retryable download failures", () => {
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
const badChecksum = new Error("Checksum mismatch for deepseek-linux-x64");
|
const badChecksum = new Error("Checksum mismatch for codewhale-linux-x64");
|
||||||
badChecksum.nonRetryable = true;
|
badChecksum.nonRetryable = true;
|
||||||
assert.equal(
|
assert.equal(
|
||||||
_internal.shouldIgnoreInstallFailure("install", badChecksum, ["--optional"], {}),
|
_internal.shouldIgnoreInstallFailure("install", badChecksum, ["--optional"], {}),
|
||||||
@@ -148,7 +148,7 @@ test("withRetry prints install hint on first retryable failure", async () => {
|
|||||||
|
|
||||||
assert.equal(result, "ok");
|
assert.equal(result, "ok");
|
||||||
assert.equal(attempts, 2);
|
assert.equal(attempts, 2);
|
||||||
assert.match(stderr, /deepseek-tui install hint:/);
|
assert.match(stderr, /codewhale install hint:/);
|
||||||
assert.match(stderr, /#npm-binary-download-times-out/);
|
assert.match(stderr, /#npm-binary-download-times-out/);
|
||||||
} finally {
|
} finally {
|
||||||
process.stderr.write = previousWrite;
|
process.stderr.write = previousWrite;
|
||||||
@@ -1,89 +1,15 @@
|
|||||||
# deepseek-tui
|
# deepseek-tui (deprecated)
|
||||||
|
|
||||||
Install and run the `deepseek` and `deepseek-tui` binaries from GitHub release artifacts.
|
This package has been renamed to **codewhale**. Install that instead:
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install -g deepseek-tui
|
npm uninstall -g deepseek-tui
|
||||||
# or
|
npm install -g codewhale
|
||||||
pnpm add -g deepseek-tui
|
|
||||||
```
|
```
|
||||||
|
|
||||||
For project-local usage:
|
`codewhale` ships the same `codewhale` and `codewhale-tui` binaries plus
|
||||||
|
deprecation shims under the old `deepseek` / `deepseek-tui` names so existing
|
||||||
|
scripts keep working through one transition release.
|
||||||
|
|
||||||
```bash
|
See [docs/REBRAND.md](https://github.com/Hmbown/DeepSeek-TUI/blob/main/docs/REBRAND.md)
|
||||||
npm install deepseek-tui
|
for the full migration story.
|
||||||
npx deepseek-tui --help
|
|
||||||
```
|
|
||||||
|
|
||||||
`postinstall` tries to download platform binaries into `bin/downloads/` and
|
|
||||||
exposes `deepseek` and `deepseek-tui` commands. If GitHub release assets are
|
|
||||||
temporarily unreachable, install continues and the wrapper retries the download
|
|
||||||
on first run.
|
|
||||||
|
|
||||||
## First run
|
|
||||||
|
|
||||||
```bash
|
|
||||||
deepseek login --api-key "YOUR_DEEPSEEK_API_KEY"
|
|
||||||
deepseek doctor
|
|
||||||
deepseek
|
|
||||||
```
|
|
||||||
|
|
||||||
The `deepseek` facade and `deepseek-tui` binary share `~/.deepseek/config.toml`
|
|
||||||
for DeepSeek auth and default model settings. Common TUI commands are available
|
|
||||||
directly through the facade, including `deepseek doctor`, `deepseek models`,
|
|
||||||
`deepseek sessions`, and `deepseek resume --last`.
|
|
||||||
|
|
||||||
The app talks to DeepSeek's documented OpenAI-compatible Chat Completions API.
|
|
||||||
Set `DEEPSEEK_BASE_URL` only if you need the China endpoint or DeepSeek beta
|
|
||||||
features such as strict tool mode, chat prefix completion, or FIM completion.
|
|
||||||
|
|
||||||
NVIDIA NIM-hosted DeepSeek V4 Pro is also supported:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
deepseek auth set --provider nvidia-nim --api-key "YOUR_NVIDIA_API_KEY"
|
|
||||||
deepseek --provider nvidia-nim
|
|
||||||
```
|
|
||||||
|
|
||||||
For a single process, set `DEEPSEEK_PROVIDER=nvidia-nim` and `NVIDIA_API_KEY`
|
|
||||||
or `NVIDIA_NIM_API_KEY` (with `DEEPSEEK_API_KEY` as a compatibility fallback).
|
|
||||||
The NIM default model is `deepseek-ai/deepseek-v4-pro` and the default base URL
|
|
||||||
is `https://integrate.api.nvidia.com/v1`. With `--provider nvidia-nim`,
|
|
||||||
`--model deepseek-v4-flash` maps to `deepseek-ai/deepseek-v4-flash`.
|
|
||||||
|
|
||||||
## Supported platforms
|
|
||||||
|
|
||||||
Prebuilt binaries for the GitHub release are downloaded automatically:
|
|
||||||
|
|
||||||
- Linux x64
|
|
||||||
- Linux arm64 (v0.8.8+)
|
|
||||||
- macOS x64 / arm64
|
|
||||||
- Windows x64
|
|
||||||
|
|
||||||
Other platform/architecture combinations (musl, riscv64, FreeBSD, …) aren't
|
|
||||||
shipped as prebuilts. Unsupported platforms, checksum failures, and glibc
|
|
||||||
compatibility problems still fail with a clear error pointing you at
|
|
||||||
`cargo install deepseek-tui-cli deepseek-tui --locked` and the full
|
|
||||||
[docs/INSTALL.md](https://github.com/Hmbown/DeepSeek-TUI/blob/main/docs/INSTALL.md)
|
|
||||||
build-from-source guide.
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
- Default binary version comes from `deepseekBinaryVersion` in `package.json`.
|
|
||||||
- Set `DEEPSEEK_TUI_VERSION` or `DEEPSEEK_VERSION` to override the release version.
|
|
||||||
- Set `DEEPSEEK_TUI_GITHUB_REPO` or `DEEPSEEK_GITHUB_REPO` to override the source repo (defaults to `Hmbown/DeepSeek-TUI`).
|
|
||||||
- Set `DEEPSEEK_TUI_RELEASE_BASE_URL` to use an internal or mirrored
|
|
||||||
release-asset directory when GitHub Releases is unavailable. The directory
|
|
||||||
must contain `deepseek-artifacts-sha256.txt` and the platform binaries.
|
|
||||||
- Set `DEEPSEEK_TUI_FORCE_DOWNLOAD=1` to force download even when the cached binary is already present.
|
|
||||||
- Set `DEEPSEEK_TUI_DISABLE_INSTALL=1` to skip install-time download.
|
|
||||||
- Set `DEEPSEEK_TUI_OPTIONAL_INSTALL=1` to make install-time retryable download
|
|
||||||
failures warn and exit `0` instead of failing `npm install`.
|
|
||||||
|
|
||||||
## Release integrity
|
|
||||||
|
|
||||||
- `npm publish` runs a release-asset check to ensure all required binary assets
|
|
||||||
exist for the target GitHub release before publishing.
|
|
||||||
- Install-time downloads are verified against the release checksum manifest before
|
|
||||||
the wrapper marks them executable.
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
const { runDeepseekTui } = require("../scripts/run");
|
|
||||||
|
|
||||||
runDeepseekTui().catch((error) => {
|
|
||||||
console.error("Failed to start deepseek-tui:", error.message);
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
const { runDeepseek } = require("../scripts/run");
|
|
||||||
|
|
||||||
runDeepseek().catch((error) => {
|
|
||||||
console.error("Failed to start deepseek:", error.message);
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "deepseek-tui",
|
"name": "deepseek-tui",
|
||||||
"version": "0.8.40",
|
"version": "0.8.40",
|
||||||
"deepseekBinaryVersion": "0.8.40",
|
"description": "Deprecated. Renamed to `codewhale`. Run `npm install -g codewhale` instead.",
|
||||||
"description": "Install and run deepseek and deepseek-tui binaries from GitHub release artifacts.",
|
|
||||||
"author": "Hmbown",
|
"author": "Hmbown",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"homepage": "https://github.com/Hmbown/DeepSeek-TUI",
|
"homepage": "https://github.com/Hmbown/DeepSeek-TUI/blob/main/docs/REBRAND.md",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/Hmbown/DeepSeek-TUI.git"
|
"url": "git+https://github.com/Hmbown/DeepSeek-TUI.git"
|
||||||
@@ -14,24 +13,13 @@
|
|||||||
"url": "https://github.com/Hmbown/DeepSeek-TUI/issues"
|
"url": "https://github.com/Hmbown/DeepSeek-TUI/issues"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
"deprecated",
|
||||||
"deepseek",
|
"deepseek",
|
||||||
"cli",
|
"codewhale"
|
||||||
"tui",
|
|
||||||
"rust",
|
|
||||||
"binary",
|
|
||||||
"terminal"
|
|
||||||
],
|
],
|
||||||
"type": "commonjs",
|
"type": "commonjs",
|
||||||
"bin": {
|
|
||||||
"deepseek": "bin/deepseek.js",
|
|
||||||
"deepseek-tui": "bin/deepseek-tui.js"
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"release:check": "node scripts/verify-release-assets.js",
|
"postinstall": "node scripts/deprecation-notice.js"
|
||||||
"postinstall": "node scripts/install.js --optional",
|
|
||||||
"prepublishOnly": "node scripts/verify-release-assets.js",
|
|
||||||
"prepack": "node scripts/install.js",
|
|
||||||
"test": "node --test test/*.test.js"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
@@ -39,11 +27,8 @@
|
|||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"preferGlobal": true,
|
|
||||||
"files": [
|
"files": [
|
||||||
"bin/*.js",
|
|
||||||
"scripts/*.js",
|
"scripts/*.js",
|
||||||
"test/*.js",
|
|
||||||
"README.md",
|
"README.md",
|
||||||
"package.json"
|
"package.json"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const notice = [
|
||||||
|
"",
|
||||||
|
" ╭───────────────────────────────────────────────────────────────────╮",
|
||||||
|
" │ │",
|
||||||
|
" │ deepseek-tui has been renamed to `codewhale`. │",
|
||||||
|
" │ │",
|
||||||
|
" │ Please uninstall this package and install codewhale instead: │",
|
||||||
|
" │ │",
|
||||||
|
" │ npm uninstall -g deepseek-tui │",
|
||||||
|
" │ npm install -g codewhale │",
|
||||||
|
" │ │",
|
||||||
|
" │ codewhale ships the same `codewhale` and `codewhale-tui` │",
|
||||||
|
" │ binaries plus deprecation shims under the old names. See: │",
|
||||||
|
" │ https://github.com/Hmbown/DeepSeek-TUI/blob/main/docs/REBRAND.md │",
|
||||||
|
" │ │",
|
||||||
|
" ╰───────────────────────────────────────────────────────────────────╯",
|
||||||
|
"",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
process.stderr.write(notice);
|
||||||
@@ -56,29 +56,48 @@ fail=0
|
|||||||
|
|
||||||
echo "Checking published release ${version}..."
|
echo "Checking published release ${version}..."
|
||||||
|
|
||||||
if npm_version="$(npm view "deepseek-tui@${version}" version 2>/dev/null)"; then
|
# Canonical post-rebrand npm package.
|
||||||
echo "npm deepseek-tui@${npm_version} is published."
|
if npm_version="$(npm view "codewhale@${version}" version 2>/dev/null)"; then
|
||||||
|
echo "npm codewhale@${npm_version} is published."
|
||||||
else
|
else
|
||||||
echo "npm deepseek-tui@${version} is not published." >&2
|
echo "npm codewhale@${version} is not published." >&2
|
||||||
fail=1
|
fail=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if npm_binary_version="$(npm view "deepseek-tui@${version}" deepseekBinaryVersion 2>/dev/null)"; then
|
# `codewhaleBinaryVersion` is the new internal version-pin field. Fall back
|
||||||
|
# to the legacy `deepseekBinaryVersion` field for old/transition packages.
|
||||||
|
binary_field=""
|
||||||
|
npm_binary_version=""
|
||||||
|
if value="$(npm view "codewhale@${version}" codewhaleBinaryVersion 2>/dev/null)" && [[ -n "${value}" ]]; then
|
||||||
|
binary_field="codewhaleBinaryVersion"
|
||||||
|
npm_binary_version="${value}"
|
||||||
|
elif value="$(npm view "codewhale@${version}" deepseekBinaryVersion 2>/dev/null)" && [[ -n "${value}" ]]; then
|
||||||
|
binary_field="deepseekBinaryVersion"
|
||||||
|
npm_binary_version="${value}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${binary_field}" ]]; then
|
||||||
if [[ "${npm_binary_version}" == "${version}" ]]; then
|
if [[ "${npm_binary_version}" == "${version}" ]]; then
|
||||||
echo "npm deepseekBinaryVersion=${npm_binary_version}."
|
echo "npm ${binary_field}=${npm_binary_version}."
|
||||||
elif [[ "${allow_npm_binary_mismatch}" == "1" ]]; then
|
elif [[ "${allow_npm_binary_mismatch}" == "1" ]]; then
|
||||||
echo "npm deepseekBinaryVersion=${npm_binary_version} (allowed packaging-only mismatch)."
|
echo "npm ${binary_field}=${npm_binary_version} (allowed packaging-only mismatch)."
|
||||||
else
|
else
|
||||||
echo "npm deepseekBinaryVersion=${npm_binary_version}, expected ${version}." >&2
|
echo "npm ${binary_field}=${npm_binary_version}, expected ${version}." >&2
|
||||||
fail=1
|
fail=1
|
||||||
fi
|
fi
|
||||||
elif [[ "${allow_npm_binary_mismatch}" == "1" ]]; then
|
elif [[ "${allow_npm_binary_mismatch}" == "1" ]]; then
|
||||||
echo "npm deepseekBinaryVersion is absent (allowed packaging-only mismatch)."
|
echo "npm codewhaleBinaryVersion is absent (allowed packaging-only mismatch)."
|
||||||
else
|
else
|
||||||
echo "npm deepseekBinaryVersion is absent for deepseek-tui@${version}." >&2
|
echo "npm codewhaleBinaryVersion is absent for codewhale@${version}." >&2
|
||||||
fail=1
|
fail=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Legacy `deepseek-tui` deprecation shim package. Best-effort check —
|
||||||
|
# absence after the transition release is expected and not fatal.
|
||||||
|
if legacy_version="$(npm view "deepseek-tui@${version}" version 2>/dev/null)"; then
|
||||||
|
echo "npm deepseek-tui@${legacy_version} (deprecation shim) is published."
|
||||||
|
fi
|
||||||
|
|
||||||
for crate in "${release_crates[@]}"; do
|
for crate in "${release_crates[@]}"; do
|
||||||
if curl -fsSL "https://crates.io/api/v1/crates/${crate}/${version}" >/dev/null 2>&1; then
|
if curl -fsSL "https://crates.io/api/v1/crates/${crate}/${version}" >/dev/null 2>&1; then
|
||||||
echo "crates.io ${crate}@${version} is published."
|
echo "crates.io ${crate}@${version} is published."
|
||||||
@@ -89,7 +108,7 @@ for crate in "${release_crates[@]}"; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
if [[ "${fail}" == "0" ]]; then
|
if [[ "${fail}" == "0" ]]; then
|
||||||
echo "Published release OK: npm deepseek-tui@${version} and ${#release_crates[@]} crates are visible."
|
echo "Published release OK: npm codewhale@${version} and ${#release_crates[@]} crates are visible."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exit "${fail}"
|
exit "${fail}"
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
# Checks performed:
|
# Checks performed:
|
||||||
# 1. No `crates/*/Cargo.toml` carries a literal `version = "x.y.z"`; every
|
# 1. No `crates/*/Cargo.toml` carries a literal `version = "x.y.z"`; every
|
||||||
# crate must inherit `version.workspace = true`.
|
# crate must inherit `version.workspace = true`.
|
||||||
# 2. `npm/deepseek-tui/package.json` `version` matches the workspace
|
# 2. `npm/codewhale/package.json` `version` matches the workspace
|
||||||
# `version` in the root `Cargo.toml`. (The npm wrapper directory is
|
# `version` in the root `Cargo.toml`. (`npm/deepseek-tui/` still
|
||||||
# renamed to `npm/codewhale/` in a follow-up phase; this script will
|
# exists during the transition as a deprecation shim package; its
|
||||||
# be updated then.)
|
# version is also checked.)
|
||||||
# 3. Internal `codewhale-*` path dependency pins match the workspace version.
|
# 3. Internal `codewhale-*` path dependency pins match the workspace version.
|
||||||
# 4. The TUI crate's packaged changelog copy matches root `CHANGELOG.md`.
|
# 4. The TUI crate's packaged changelog copy matches root `CHANGELOG.md`.
|
||||||
# 5. The current release has a dated Keep a Changelog entry and compare link.
|
# 5. The current release has a dated Keep a Changelog entry and compare link.
|
||||||
@@ -32,11 +32,20 @@ fi
|
|||||||
|
|
||||||
# 2) Workspace ↔ npm package.json.
|
# 2) Workspace ↔ npm package.json.
|
||||||
workspace_version="$(grep -E '^version = "' Cargo.toml | head -n1 | sed -E 's/^version = "([^"]+)".*/\1/')"
|
workspace_version="$(grep -E '^version = "' Cargo.toml | head -n1 | sed -E 's/^version = "([^"]+)".*/\1/')"
|
||||||
npm_version="$(node -p "require('./npm/deepseek-tui/package.json').version")"
|
npm_version="$(node -p "require('./npm/codewhale/package.json').version")"
|
||||||
if [[ "${workspace_version}" != "${npm_version}" ]]; then
|
if [[ "${workspace_version}" != "${npm_version}" ]]; then
|
||||||
echo "::error::npm/deepseek-tui/package.json version (${npm_version}) does not match workspace Cargo.toml (${workspace_version})." >&2
|
echo "::error::npm/codewhale/package.json version (${npm_version}) does not match workspace Cargo.toml (${workspace_version})." >&2
|
||||||
fail=1
|
fail=1
|
||||||
fi
|
fi
|
||||||
|
# Also pin the legacy deprecation shim package to the same workspace version
|
||||||
|
# so a stale `deepseek-tui` doesn't ship pointing at a different release.
|
||||||
|
if [[ -f npm/deepseek-tui/package.json ]]; then
|
||||||
|
legacy_npm_version="$(node -p "require('./npm/deepseek-tui/package.json').version")"
|
||||||
|
if [[ "${workspace_version}" != "${legacy_npm_version}" ]]; then
|
||||||
|
echo "::error::npm/deepseek-tui/package.json version (${legacy_npm_version}) does not match workspace Cargo.toml (${workspace_version})." >&2
|
||||||
|
fail=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# 3) Internal path dependency pins.
|
# 3) Internal path dependency pins.
|
||||||
internal_dep_drift="$(
|
internal_dep_drift="$(
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const path = require("path");
|
|||||||
const { spawn } = require("child_process");
|
const { spawn } = require("child_process");
|
||||||
|
|
||||||
const repoRoot = path.resolve(__dirname, "..", "..");
|
const repoRoot = path.resolve(__dirname, "..", "..");
|
||||||
const packageDir = path.join(repoRoot, "npm", "deepseek-tui");
|
const packageDir = path.join(repoRoot, "npm", "codewhale");
|
||||||
const prepareAssetsScript = path.join(
|
const prepareAssetsScript = path.join(
|
||||||
repoRoot,
|
repoRoot,
|
||||||
"scripts",
|
"scripts",
|
||||||
@@ -133,7 +133,7 @@ function parsePackJson(stdout) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const tempRoot = await fsp.mkdtemp(path.join(os.tmpdir(), "deepseek-npm-smoke-"));
|
const tempRoot = await fsp.mkdtemp(path.join(os.tmpdir(), "codewhale-npm-smoke-"));
|
||||||
const releaseAssetsDir = path.join(tempRoot, "release-assets");
|
const releaseAssetsDir = path.join(tempRoot, "release-assets");
|
||||||
const packDir = path.join(tempRoot, "pack");
|
const packDir = path.join(tempRoot, "pack");
|
||||||
const installDir = path.join(tempRoot, "install");
|
const installDir = path.join(tempRoot, "install");
|
||||||
@@ -165,11 +165,11 @@ async function main() {
|
|||||||
|
|
||||||
await runCommand("npm", ["init", "-y"], { cwd: installDir });
|
await runCommand("npm", ["init", "-y"], { cwd: installDir });
|
||||||
await runCommand("npm", ["install", tarball], { cwd: installDir, env });
|
await runCommand("npm", ["install", tarball], { cwd: installDir, env });
|
||||||
await runCommand("npx", ["--no-install", "deepseek", "doctor", "--help"], {
|
await runCommand("npx", ["--no-install", "codewhale", "doctor", "--help"], {
|
||||||
cwd: installDir,
|
cwd: installDir,
|
||||||
env,
|
env,
|
||||||
});
|
});
|
||||||
await runCommand("npx", ["--no-install", "deepseek-tui", "--help"], {
|
await runCommand("npx", ["--no-install", "codewhale-tui", "--help"], {
|
||||||
cwd: installDir,
|
cwd: installDir,
|
||||||
env,
|
env,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const {
|
|||||||
allAssetNames,
|
allAssetNames,
|
||||||
CHECKSUM_MANIFEST,
|
CHECKSUM_MANIFEST,
|
||||||
detectBinaryNames,
|
detectBinaryNames,
|
||||||
} = require("../../npm/deepseek-tui/scripts/artifacts");
|
} = require("../../npm/codewhale/scripts/artifacts");
|
||||||
|
|
||||||
async function sha256(filePath) {
|
async function sha256(filePath) {
|
||||||
const content = await fs.readFile(filePath);
|
const content = await fs.readFile(filePath);
|
||||||
@@ -25,16 +25,16 @@ async function main() {
|
|||||||
const buildDir = path.resolve(
|
const buildDir = path.resolve(
|
||||||
process.argv[3] || path.join("target", "release"),
|
process.argv[3] || path.join("target", "release"),
|
||||||
);
|
);
|
||||||
const { deepseek, tui } = detectBinaryNames();
|
const { codewhale, tui } = detectBinaryNames();
|
||||||
const isWindows = process.platform === "win32";
|
const isWindows = process.platform === "win32";
|
||||||
|
|
||||||
const assets = [
|
const assets = [
|
||||||
{
|
{
|
||||||
source: path.join(buildDir, isWindows ? "deepseek.exe" : "deepseek"),
|
source: path.join(buildDir, isWindows ? "codewhale.exe" : "codewhale"),
|
||||||
target: deepseek,
|
target: codewhale,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: path.join(buildDir, isWindows ? "deepseek-tui.exe" : "deepseek-tui"),
|
source: path.join(buildDir, isWindows ? "codewhale-tui.exe" : "codewhale-tui"),
|
||||||
target: tui,
|
target: tui,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -45,9 +45,9 @@ async function main() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
assets.push({
|
assets.push({
|
||||||
source: assetName.startsWith("deepseek-tui")
|
source: assetName.startsWith("codewhale-tui")
|
||||||
? path.join(buildDir, isWindows ? "deepseek-tui.exe" : "deepseek-tui")
|
? path.join(buildDir, isWindows ? "codewhale-tui.exe" : "codewhale-tui")
|
||||||
: path.join(buildDir, isWindows ? "deepseek.exe" : "deepseek"),
|
: path.join(buildDir, isWindows ? "codewhale.exe" : "codewhale"),
|
||||||
target: assetName,
|
target: assetName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user