mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-28 16:56:16 +08:00
feat(settings): unify unix installers on mktemp+bash, fix WSL install missing native installer
Extend the mktemp+bash installer pattern from hermes (commit 20d943be) to
claude and opencode, and remove the cfg gate that locked install_command_for
to non-Windows targets — Windows host + WSL tools now correctly route
through the POSIX install priority instead of falling back to bare npm.
Frontend "one-click install commands" displayed in the About section now
diverge per platform to match what the backend actually runs.
Backend (src-tauri/src/commands/misc.rs):
- New CLAUDE_INSTALL_UNIX and OPENCODE_INSTALL_UNIX constants use the same
`bash -c 'tmp=$(mktemp) && curl -fsSL .../install.sh -o $tmp && bash
$tmp; status=$?; rm -f $tmp; exit $status'` shape as HERMES_INSTALL_UNIX.
The shared comment about why we avoid `curl | bash` is now hoisted to the
top of all three constants — the constraint (WSL `sh -c` sub-shells
don't inherit outer pipefail; default shell may be dash/ash) applies to
every shell installer, not just Hermes.
- New installer_with_npm_fallback helper deduplicates the
"official installer || npm fallback" chain construction; reuses
chain_update_commands(LifecycleCommandShell::Posix) so the wiring matches
the update-chain logic.
- install_command_for split into cross-platform posix_install_command_for
+ thin #[cfg(not(target_os = "windows"))] wrapper. The cfg gate had
been hiding a real bug: on Windows hosts, the WSL branch called
wsl_tool_action_shell_command which forwarded install actions to the
Windows-target tool_action_shell_command, yielding bare `npm i -g` for
claude/opencode even though they have working native POSIX installers.
- wsl_tool_action_shell_command now branches on action: Install dispatches
to posix_install_command_for (native installer || npm chain); Update
keeps the existing behavior. Empty install commands collapse to None so
callers error on unsupported tools instead of running an empty command.
- build_tool_lifecycle_command pipefail comment updated: the original
justification (covering install's `curl | bash` path) no longer applies
to any current command, but the directive stays as defense-in-depth for
future pipe additions.
- build_tool_action_line WSL branch comment updated to reflect the
install/update split.
Frontend (src/components/settings/AboutSection.tsx):
- New posixScriptInstallCommand(url) helper builds the same mktemp+bash
string template the backend uses.
- New powershellEncodedCommand(script) mirrors the backend's UTF-16 LE +
base64 encoder (charCodeAt + String.fromCharCode(lo, hi) + btoa) so the
PowerShell EncodedCommand shown to users matches what the backend
executes.
- ONE_CLICK_INSTALL_COMMANDS split into POSIX_ONE_CLICK_INSTALL_COMMANDS
and WINDOWS_ONE_CLICK_INSTALL_COMMANDS, selected by isWindows():
POSIX shows claude/opencode/hermes with the mktemp+bash installer;
Windows shows claude/codex/gemini/opencode/openclaw with npm (matching
backend static_fallback_command on Windows) and hermes with the
PowerShell EncodedCommand. Display now mirrors backend execution
per platform, removing the show-vs-run mismatch.
- Stale comment in the probe-concurrency note mentioning "curl | bash"
updated to "官方 installer".
Tests (src-tauri/src/commands/misc.rs):
- New wsl_install_uses_posix_install_priority covers three
representatives: claude (positive: native installer with npm fallback),
opencode (positive), codex (negative: no native installer so falls back
to bare `npm i -g`). Reverse-asserts !contains("| bash") to lock out
the old pipe form.
- claude_install_chains_native_then_npm and
opencode_install_chains_native_then_npm strengthened with
!parts[0].contains('|') so the native installer portion stays
pipe-free even if future edits to the template sneak a pipe back in.
Validated:
- cargo check --tests: clean
- cargo test --lib commands::misc:: : 59 passed, 0 failed
- pnpm typecheck: 0 errors
- cargo fmt: clean
- pnpm format: clean (no files changed)
This commit is contained in:
@@ -39,6 +39,7 @@ import appIcon from "@/assets/icons/app-icon.png";
|
||||
import { APP_ICON_MAP } from "@/config/appConfig";
|
||||
import type { AppId } from "@/lib/api/types";
|
||||
import { extractErrorMessage } from "@/utils/errorUtils";
|
||||
import { isWindows } from "@/lib/platform";
|
||||
import { ToolUpgradeConfirmDialog } from "./ToolUpgradeConfirmDialog";
|
||||
import { ToolInstallRow } from "./ToolInstallRow";
|
||||
|
||||
@@ -104,7 +105,39 @@ const ENV_BADGE_CONFIG: Record<
|
||||
},
|
||||
};
|
||||
|
||||
const ONE_CLICK_INSTALL_COMMANDS = `# Claude Code
|
||||
const posixScriptInstallCommand = (url: string) =>
|
||||
`bash -c 'tmp=$(mktemp) && curl -fsSL ${url} -o $tmp && bash $tmp; status=$?; rm -f $tmp; exit $status'`;
|
||||
|
||||
const HERMES_WINDOWS_INSTALL_SCRIPT =
|
||||
"irm https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.ps1 | iex";
|
||||
|
||||
const powershellEncodedCommand = (script: string): string => {
|
||||
let binary = "";
|
||||
for (let i = 0; i < script.length; i += 1) {
|
||||
const code = script.charCodeAt(i);
|
||||
binary += String.fromCharCode(code & 0xff, code >> 8);
|
||||
}
|
||||
return btoa(binary);
|
||||
};
|
||||
|
||||
const HERMES_WINDOWS_INSTALL_COMMAND = `powershell -NoProfile -ExecutionPolicy Bypass -EncodedCommand ${powershellEncodedCommand(
|
||||
HERMES_WINDOWS_INSTALL_SCRIPT,
|
||||
)}`;
|
||||
|
||||
const POSIX_ONE_CLICK_INSTALL_COMMANDS = `# Claude Code
|
||||
${posixScriptInstallCommand("https://claude.ai/install.sh")} || npm i -g @anthropic-ai/claude-code@latest
|
||||
# Codex
|
||||
npm i -g @openai/codex@latest
|
||||
# Gemini CLI
|
||||
npm i -g @google/gemini-cli@latest
|
||||
# OpenCode
|
||||
${posixScriptInstallCommand("https://opencode.ai/install")} || npm i -g opencode-ai@latest
|
||||
# OpenClaw
|
||||
npm i -g openclaw@latest
|
||||
# Hermes
|
||||
${posixScriptInstallCommand("https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh")}`;
|
||||
|
||||
const WINDOWS_ONE_CLICK_INSTALL_COMMANDS = `# Claude Code
|
||||
npm i -g @anthropic-ai/claude-code@latest
|
||||
# Codex
|
||||
npm i -g @openai/codex@latest
|
||||
@@ -115,7 +148,11 @@ npm i -g opencode-ai@latest
|
||||
# OpenClaw
|
||||
npm i -g openclaw@latest
|
||||
# Hermes
|
||||
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash`;
|
||||
${HERMES_WINDOWS_INSTALL_COMMAND}`;
|
||||
|
||||
const ONE_CLICK_INSTALL_COMMANDS = isWindows()
|
||||
? WINDOWS_ONE_CLICK_INSTALL_COMMANDS
|
||||
: POSIX_ONE_CLICK_INSTALL_COMMANDS;
|
||||
|
||||
const TOOL_DISPLAY_NAMES: Record<ToolName, string> = {
|
||||
claude: "Claude Code",
|
||||
@@ -185,7 +222,7 @@ export function AboutSection({ isPortable }: AboutSectionProps) {
|
||||
// 升级 preflight(probe 阶段)的 in-flight 工具集合。
|
||||
// probeToolInstallations 是个 1-3 秒级别的跨进程探测(对每个工具跑 --version + canonicalize),
|
||||
// 在它返回之前 toolActions / batchAction 都还没被置位 → 按钮不会 disabled → 用户快速双击
|
||||
// 会并发开两轮 probe,各自再触发 executeRun(并发的 `npm i -g` / `curl | bash`,写冲突)。
|
||||
// 会并发开两轮 probe,各自再触发 executeRun(并发的 `npm i -g` / 官方 installer,写冲突)。
|
||||
// 把 probe 期间的工具登记在这里、纳入 isAnyBusy 派生,关掉这个并发窗口。
|
||||
// 用 Set 而非 boolean:单卡片升级 & 批量升级可能在不同工具上独立 preflight,
|
||||
// 精确反映到各自卡片按钮的 disabled。
|
||||
|
||||
Reference in New Issue
Block a user