mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-12 14:19:11 -10:00
* feat(sandbox): 支持 Git-Bash 自主选择与跨平台一键自动安装 背景: 大语言模型生成的命令绝大多数为 POSIX 语法(如 grep、sed、管道、重定向等), 但 Windows 默认使用 PowerShell,语法不兼容导致命令执行频繁失败。 macOS/Linux 虽然自带 bash,但部分精简容器或特殊环境也可能缺失。 本次变更: 1. 深度自动探测 Git-Bash 安装路径 - Windows:扫描 ProgramFiles、Scoop、Chocolatey、注册表(HKLM/HKCU) - 自动识别并纠正 git-bash.exe → bin/bash.exe(headless 可执行文件) - 注入 MSYS_NO_PATHCONV=1 防止 MSYS2 路径篡改 2. 跨平台一键自动安装后端 - Windows:通过 winget install --id Git.Git 静默安装 - macOS:通过 brew install bash git 安装 - Linux:自动适配 apt-get/dnf/pacman/zypper/apk,非 root 自动 sudo 3. 桌面端设置面板集成 - 当用户选择 bash/auto 但系统未检测到时,展示安装提示卡片 - 一键安装按钮,带 Loading/成功/失败 三态反馈 - 安装成功后自动写入配置并刷新生效状态 4. 多语言支持 - 中文简体、英文、中文繁体三语本地化 * chore: format go code and update repolint baseline budget * fix(desktop): harden Windows shell support Problem: The Git for Windows helper could rebuild the active controller after install, miss an explicitly configured portable Git Bash, and cancel only the direct winget process. Root cause: The frontend reused the session reload callback for detection refresh, capability probing ignored tools.shell.path, and the installer called exec.Cmd.Run without repository process-tree tracking. Fix: Separate pure settings refresh from explicit session reload, pass the configured path through shell inventory and install preflight, track the winget process tree, and add deterministic backend/frontend race coverage. Verification: go test ./... (desktop module) go test -race ./internal/sandbox go test -race focused desktop shell support frontend shell support test, typecheck, hooks lint, and production build Windows amd64 and arm64 desktop test-binary cross-compilation * feat(desktop): add cross-platform shell repair guidance Problem: shell support offered a safe Windows path but macOS and Linux only exposed generic manual guidance. Root cause: the inventory reported only Bash on Unix and the Settings contract had no platform-specific, copy-only remediation. Fix: report Bash, zsh, and sh capabilities; expose allowlisted Homebrew and distro commands without executing them or prescribing sudo; and invalidate discovery only on explicit session reload. Verification: go test ./...; cd desktop && go test ./...; frontend full tests, performance, production build, bundle budgets, repolint, and Windows cross-compilation. * feat(desktop): separate native shell fallback from Git support Problem: macOS repair guidance treated Bash as mandatory and coupled shell availability with Git installation. Root cause: the resolver only modeled Bash and PowerShell as executable shells, while Settings had no independent Git capability. Fix: fall back from Bash to native zsh and sh on macOS, discover Git independently, and expose platform-safe copy-only Git repair guidance without changing persisted shell preferences. Verification: go test ./... cd desktop && go test ./... frontend 230-suite test run, typecheck, performance, production build Windows amd64/arm64 sandbox and desktop cross-compiles repolint and diff checks * fix(desktop): make Git Bash repair manual-only Problem: The Git.Git winget package may elevate even when invoked with --scope user, so the settings helper could unexpectedly trigger UAC or install machine-wide. Root cause: WinGet scope selection does not force the Git for Windows installer scope; the current package uses an elevating installer for both user and machine entries. Fix: Remove winget execution and cancellation state, return manual_required for Windows, and expose only the official download plus explicit re-detect flow. Keep the Wails methods as safe compatibility results/no-ops. Verification: - cd desktop && go test ./... - focused shell-support frontend test (21/21) - frontend typecheck and production build/bundle budget - repolint in a clean current-patch worktree - Windows amd64/arm64 test-binary compilation - native Windows 11 ARM64 policy test * chore(desktop): reconcile shell support with main-v2 Problem: - The rebased shell-support branch retained stale automatic-installer comments and bundle ratchets from its older base. Root cause: - Main-v2 added notification-volume UI after this branch forked, while the shell repair policy changed to manual-only. Fix: - Measure the combined latest-main bundle and apply narrow ratchets. - Align shell inventory comments with manual re-detection and the macOS zsh/sh fallback. Verification: - pnpm build - pnpm test:all (baseline remote source-contract failure documented separately) - go test -race ./internal/sandbox ./internal/control ./internal/shellrun ./internal/tool/builtin - cd desktop && go test ./... - Windows amd64/arm64 cross-compilation * fix(sandbox): harden shell capability discovery Problem: - Passive Git detection could execute Apple's inactive /usr/bin/git shim and open the Command Line Tools installer. - A retained PowerShell path could be admitted into the Windows Bash inventory after the shell preference changed. Root cause: - Git probing had no platform preflight for the Apple shim. - The shell inventory cache and configured-path filtering ignored the selected shell preference. Fix: - Gate the Apple shim with a side-effect-free xcode-select preflight before running git --version. - Key discovery by preference and path, and only admit configured Windows Bash paths compatible with the preference. - Preserve the path-only compatibility APIs and add regression coverage for both cases. Verification: - go test ./... - cd desktop && go test ./... - go test -race ./internal/sandbox - go vet ./... (root and desktop modules) - go run ./tools/repolint - Windows amd64/arm64 sandbox and desktop test-binary compilation Co-authored-by: SivanCola <[email protected]> * fix(sandbox): validate configured shell paths Problem: - Switching shell preferences preserved an explicit path that could belong to another interpreter, crossing Bash and PowerShell argv contracts. - Explicit WSL and unresolved git-bash launchers could bypass inventory safeguards, and the checked-in repolint baseline did not reflect the branch's actual findings. Root cause: - ResolveShell and the integrated terminal trusted path existence independently of the selected shell kind, while the environment label reused raw config state. - The baseline was regenerated from a stale working state. Fix: - Centralize configured-path compatibility, preserve unknown wrappers, reject known cross-kind, WSL, and MinTTY paths, align terminal and provider shell labels with resolved execution, and refresh the exact repolint snapshot. - Add deterministic cross-kind, WSL, wrapper, argv, terminal, and shell-label regressions. Verification: - go test ./... - go vet ./... - cd desktop && go test ./... - cd desktop && go vet ./... - go test -race ./internal/sandbox - Windows amd64/arm64 test compilation and Windows 11 ARM64 focused native tests - pnpm build - go run ./tools/repolint Co-authored-by: SivanCola <[email protected]> * fix(boot): preserve stable shell labels Problem: - The configured-shell correction made automatic shell resolution publish an absolute executable path in the provider-visible environment section. - That widened cache keys for users without an explicit shell path and contradicted the low cache-impact contract. Root cause: - The label helper treated every resolved executable path as user-configured state. Fix: - Keep the stable interpreter kind for automatic resolution and emit a resolved executable path only when the user configured an explicit path. - Cover both the cache-stable default and stale-path correction cases. Verification: - go test ./internal/boot ./internal/tool/builtin ./internal/sandbox - go vet ./internal/boot ./internal/tool/builtin ./internal/sandbox - go test -race ./internal/boot ./internal/sandbox - go run ./tools/repolint - git diff --check Co-authored-by: SivanCola <[email protected]> * fix(sandbox): honor configured Bash priority Problem: - Windows auto resolution and the Settings capability view selected a PATH Bash before an explicitly configured compatible Bash path. - This contradicted the documented discovery order and made portable or pinned Git Bash configurations ineffective. Root cause: - The configured path lived in the candidate list, but both resolution and capability projection checked PATH before iterating those candidates. - Capability probes also lacked an injected test seam, so a synthetic-path test was not portable to native Windows. Fix: - Resolve a compatible configured Windows Bash before PATH in auto mode and mirror the same order in capability projection. - Keep arbitrary wrappers opt-in to prefer=bash, add a cached probe seam, and cover resolver plus Settings source priority. Verification: - go test ./... && go vet ./... - go test -race ./internal/sandbox - focused priority and shell regressions with -count=20 - desktop focused shell/settings contract tests - Windows amd64/arm64 test compilation - Windows 11 ARM64 native priority tests - go run ./tools/repolint - git diff --check Co-authored-by: SivanCola <[email protected]> * test(sandbox): use WaitGroup.Go in inventory races Problem: - The current root golangci-lint job rejected two manual Add/go/defer Done patterns in the shell inventory cache test. Root cause: - Go 1.26 and golangci-lint v2.12.2 now prefer sync.WaitGroup.Go for directly owned test goroutines. Fix: - Launch both cache-test goroutines through WaitGroup.Go without changing synchronization or assertions. Verification: - golangci-lint v2.12.2 run --timeout=5m ./... - go test ./internal/sandbox - go run ./tools/repolint - git diff --check Co-authored-by: SivanCola <[email protected]> --------- Co-authored-by: SivanCola <[email protected]>
133 lines
5.1 KiB
Go
133 lines
5.1 KiB
Go
package tool
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
)
|
|
|
|
// ShellExecution is local host metadata for one shell invocation. It is never
|
|
// part of the provider-visible tool schema or request bytes; ModelMessages and
|
|
// provider serializers must strip it before a model request leaves the host.
|
|
//
|
|
// Kind is always "shell" for shell invocations so UIs can distinguish this
|
|
// optional payload from other future execution kinds without guessing.
|
|
type ShellExecution struct {
|
|
Kind string `json:"kind"`
|
|
Shell string `json:"shell,omitempty"` // bash | zsh | sh | git-bash | powershell | pwsh
|
|
ShellVersion string `json:"shellVersion,omitempty"` // 5.1 | 7+ (PowerShell only)
|
|
Platform string `json:"platform,omitempty"` // windows | darwin | linux
|
|
// SupportsAndAnd is explicit even when false so UIs can show PowerShell 5.1
|
|
// chaining limits without treating omission as "unknown".
|
|
SupportsAndAnd bool `json:"supportsAndAnd"`
|
|
State string `json:"state,omitempty"` // running | completed | failed | timed_out | cancelled | background_started | not_run
|
|
FailurePhase string `json:"failurePhase,omitempty"` // preflight | authorization | dependency | launch | execution | timeout | cancellation
|
|
// ExitCode is set only when a child process started and produced an exit
|
|
// status. Zero is a valid successful code (*int keeps 0 distinct from unset).
|
|
ExitCode *int `json:"exitCode,omitempty"`
|
|
// OutputTail is the bounded tail of combined stdout+stderr, set only for a
|
|
// run that did not succeed. Both streams share one pipe so model-visible
|
|
// interleaving stays in child-write order, which rules out a stderr-only
|
|
// tail. At most 16 KiB; never a shell executable absolute path.
|
|
OutputTail string `json:"outputTail,omitempty"`
|
|
MutationRisk string `json:"mutationRisk,omitempty"` // none | not_started | may_have_completed | may_be_partial | unknown
|
|
Verification string `json:"verification,omitempty"` // not_verification | not_run | passed | failed
|
|
DurationMs int64 `json:"durationMs,omitempty"`
|
|
}
|
|
|
|
// Shell execution state values.
|
|
const (
|
|
ShellStateRunning = "running"
|
|
ShellStateCompleted = "completed"
|
|
ShellStateFailed = "failed"
|
|
ShellStateTimedOut = "timed_out"
|
|
ShellStateCancelled = "cancelled"
|
|
ShellStateBackgroundStarted = "background_started"
|
|
ShellStateNotRun = "not_run"
|
|
)
|
|
|
|
// Shell failure phase values.
|
|
const (
|
|
ShellPhasePreflight = "preflight"
|
|
ShellPhaseAuthorization = "authorization"
|
|
ShellPhaseDependency = "dependency"
|
|
ShellPhaseLaunch = "launch"
|
|
ShellPhaseExecution = "execution"
|
|
ShellPhaseTimeout = "timeout"
|
|
ShellPhaseCancellation = "cancellation"
|
|
)
|
|
|
|
// Shell mutation risk values.
|
|
const (
|
|
ShellMutationNone = "none"
|
|
ShellMutationNotStarted = "not_started"
|
|
ShellMutationMayHaveCompleted = "may_have_completed"
|
|
ShellMutationMayBePartial = "may_be_partial"
|
|
ShellMutationUnknown = "unknown"
|
|
)
|
|
|
|
// Shell verification values.
|
|
const (
|
|
ShellVerificationNotVerification = "not_verification"
|
|
ShellVerificationNotRun = "not_run"
|
|
ShellVerificationPassed = "passed"
|
|
ShellVerificationFailed = "failed"
|
|
)
|
|
|
|
// Shell name values for ShellExecution.Shell.
|
|
const (
|
|
ShellNameBash = "bash"
|
|
ShellNameZsh = "zsh"
|
|
ShellNameSh = "sh"
|
|
ShellNameGitBash = "git-bash"
|
|
ShellNamePowerShell = "powershell"
|
|
ShellNamePwsh = "pwsh"
|
|
)
|
|
|
|
// PowerShell version labels.
|
|
const (
|
|
ShellVersionPS51 = "5.1"
|
|
ShellVersionPS7 = "7+"
|
|
)
|
|
|
|
// OutputTailMaxBytes bounds the output tail retained on ShellExecution.
|
|
const OutputTailMaxBytes = 16 << 10
|
|
|
|
// DetailedResult is the structured outcome of a DetailedExecutor call.
|
|
// Output remains the model-visible text; Execution is host/UI metadata only.
|
|
type DetailedResult struct {
|
|
Output string
|
|
Images []string
|
|
Execution *ShellExecution
|
|
}
|
|
|
|
// DetailedExecutor is an optional Tool capability that returns structured
|
|
// execution metadata alongside the model-visible result text. Tools that do
|
|
// not implement it continue to use ImageTool/Tool.Execute.
|
|
type DetailedExecutor interface {
|
|
// ExecutionDescriptor returns a descriptor for the would-be execution
|
|
// before the process starts (shell identity, platform, chaining support).
|
|
// It must not launch a process. Args may be empty or invalid — return a
|
|
// best-effort descriptor from the bound shell configuration.
|
|
ExecutionDescriptor(args json.RawMessage) *ShellExecution
|
|
// ExecuteDetailed runs the tool and returns structured metadata. On
|
|
// policy/preflight blocks, Execution must still be populated (state=not_run).
|
|
ExecuteDetailed(ctx context.Context, args json.RawMessage) (DetailedResult, error)
|
|
}
|
|
|
|
// CloneShellExecution returns a deep copy suitable for attaching to events or
|
|
// session messages without sharing mutable pointers (e.g. ExitCode).
|
|
func CloneShellExecution(in *ShellExecution) *ShellExecution {
|
|
if in == nil {
|
|
return nil
|
|
}
|
|
out := *in
|
|
if in.ExitCode != nil {
|
|
code := *in.ExitCode
|
|
out.ExitCode = &code
|
|
}
|
|
return &out
|
|
}
|
|
|
|
// IntPtr returns a pointer to v for ShellExecution.ExitCode.
|
|
func IntPtr(v int) *int { return &v }
|