mirror of
https://github.com/Twigpine/zero.git
synced 2026-11-10 05:01:28 -10:00
* fix(windows): prefer PowerShell for command execution Detect PowerShell 7 or Windows PowerShell before falling back to cmd.exe, and share the selected host shell across agent tools and the interactive shell escape. Configure UTF-8 output, preserve restricted-sandbox MSYS safeguards, and add Windows coverage for pipelines, workspace writes, and interactive stdin sessions. * test(windows): align shell coverage with PowerShell Make Windows command fixtures and guidance assertions follow the selected host shell, preserve cmd fallback coverage, and use a genuine MSYS executable when PowerShell aliases are available. Remove the unsupported non-TTY PowerShell stdin assertion; Windows interactive stdin requires a separate PTY transport. * fix(sandbox): classify Windows executable suffixes Normalize .exe, .cmd, .bat, and .com command names before safety and network lookup so PowerShell invocations such as curl.cmd receive the same network permission handling as curl. Add analyzer and engine-level regressions for the Windows PowerShell command shape observed in CI. * fix(sandbox): normalize absolute Windows command paths Extract drive-letter and UNC basenames before removing shell escape characters so fully qualified Windows executables continue matching canonical safety and network classifications. Cover both drive and UNC curl paths with analyzer regressions. * fix(sandbox): normalize relative Windows paths Recognize drive-relative, explicit relative, and suffixed backslash executable paths before generic shell escape removal so Windows commands continue to match canonical sandbox risk entries. * fix(windows): harden PowerShell execution * fix(sandbox): classify PowerShell removal aliases
65 lines
1.8 KiB
Go
65 lines
1.8 KiB
Go
package tui
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
tea "charm.land/bubbletea/v2"
|
|
"github.com/Gitlawb/zero/internal/tools"
|
|
)
|
|
|
|
// bashEscapeTimeout bounds a "!cmd" shell escape so a hung command can't freeze
|
|
// the agent indefinitely.
|
|
const bashEscapeTimeout = 30 * time.Second
|
|
|
|
// bashResultMsg carries the combined output of a "!cmd" shell escape back to the
|
|
// model for display in the transcript.
|
|
type bashResultMsg struct {
|
|
command string
|
|
output string
|
|
}
|
|
|
|
// runBashEscape runs a user-typed "!cmd" in the workspace and returns its
|
|
// combined output as a message. The user typed it explicitly, so it runs
|
|
// directly (a deliberate shell escape, outside the agent sandbox), bounded by a
|
|
// timeout and executed asynchronously so the UI stays responsive.
|
|
func runBashEscape(cwd, command string) tea.Cmd {
|
|
return func() tea.Msg {
|
|
ctx, cancel := context.WithTimeout(context.Background(), bashEscapeTimeout)
|
|
defer cancel()
|
|
|
|
cmd := tools.NewHostShellCommandContext(ctx, command)
|
|
if strings.TrimSpace(cwd) != "" {
|
|
cmd.Dir = cwd
|
|
}
|
|
out, err := cmd.CombinedOutput()
|
|
text := strings.TrimRight(string(out), "\n")
|
|
|
|
switch {
|
|
case ctx.Err() == context.DeadlineExceeded:
|
|
text = appendNote(text, "[timed out after "+bashEscapeTimeout.String()+"]")
|
|
case err != nil:
|
|
text = appendNote(text, "[exit error: "+err.Error()+"]")
|
|
}
|
|
if strings.TrimSpace(text) == "" {
|
|
text = "(no output)"
|
|
}
|
|
return bashResultMsg{command: command, output: text}
|
|
}
|
|
}
|
|
|
|
// escapeShell returns the platform shell and arguments for tests and display.
|
|
// Execution itself uses tools.NewHostShellCommandContext so the cmd.exe
|
|
// fallback receives its required raw Windows command line.
|
|
func escapeShell(command string) (string, []string) {
|
|
return tools.HostShellCommand(command)
|
|
}
|
|
|
|
func appendNote(text, note string) string {
|
|
if text == "" {
|
|
return note
|
|
}
|
|
return text + "\n" + note
|
|
}
|