matrixcloud / internal /matrixshell /matrixshell.go
github-actions[bot]
Deploy from 4dfd54f2
857a91b
Raw
History Blame Contribute Delete
4.16 kB
// Package matrixshell installs and runs the real MatrixShell CLI
// (github.com/agent-matrix/MatrixShell) inside a dedicated Python virtual
// environment on the host, and executes commands in a sandbox working directory
// with the venv on PATH. No simulation: install, version and command output are
// all real.
package matrixshell
import (
"context"
"errors"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/agent-matrix/matrix-runtime/internal/pyenv"
)
// RepoSpec is the pip/uv install spec for MatrixShell (not on PyPI yet).
const RepoSpec = "git+https://github.com/agent-matrix/MatrixShell.git@master"
// ErrBlocked is returned when a command is refused by the safety denylist.
var ErrBlocked = errors.New("refused by safety denylist")
// hard denylist — destructive operations refused even on explicit confirm.
var denyList = []*regexp.Regexp{
regexp.MustCompile(`(?i)\bmkfs\b`), regexp.MustCompile(`(?i)\bdd\s+if=`),
regexp.MustCompile(`(?i)\bfdisk\b`), regexp.MustCompile(`(?i)\bdiskpart\b`),
regexp.MustCompile(`(?i)\bshutdown\b`), regexp.MustCompile(`(?i)\breboot\b`),
regexp.MustCompile(`(?i)\bmkswap\b`), regexp.MustCompile(`:\(\)\s*\{`),
regexp.MustCompile(`(?i)\brm\s+-rf\s+(/|~|\$HOME)(\s|$)`),
regexp.MustCompile(`>\s*/dev/sd`),
}
// Denied reports whether a command is blocked by the safety denylist.
func Denied(command string) bool {
for _, re := range denyList {
if re.MatchString(command) {
return true
}
}
return false
}
// VenvDir is the MatrixShell sandbox venv directory under the data dir.
func VenvDir(dataDir string) string { return filepath.Join(dataDir, "venvs", "matrixshell") }
// SandboxDir is the working directory commands run in.
func SandboxDir(dataDir string) string { return filepath.Join(dataDir, "sandbox", "matrixshell") }
func env(dataDir string) *pyenv.Env { return &pyenv.Env{Dir: VenvDir(dataDir)} }
// Status describes the local MatrixShell installation.
type Status struct {
Installed bool `json:"installed"`
Version string `json:"version"`
Venv string `json:"venv"`
Python string `json:"python"`
Sandbox string `json:"sandbox"`
Spec string `json:"spec"`
}
// GetStatus inspects the sandbox venv and reports whether matrixsh is installed.
func GetStatus(ctx context.Context, dataDir string) Status {
e := env(dataDir)
st := Status{Venv: e.Dir, Sandbox: SandboxDir(dataDir), Spec: RepoSpec}
if !e.Exists() {
return st
}
st.Python = e.Python()
if e.HasBin("matrixsh") {
res, _ := e.Shell(ctx, SandboxDir(dataDir), `python -c "import importlib.metadata as m; print(m.version('matrixsh'))"`)
if res.ExitCode == 0 {
st.Installed = true
st.Version = strings.TrimSpace(res.Stdout)
}
}
return st
}
// Install creates the sandbox venv (if needed) and installs MatrixShell from
// git, streaming real output to emit. Returns the resulting status.
func Install(ctx context.Context, dataDir string, emit func(string)) (Status, error) {
dir := VenvDir(dataDir)
emit("creating python sandbox venv: " + dir)
e, err := pyenv.Ensure(ctx, dir, emit)
if err != nil {
return Status{}, err
}
emit("installing MatrixShell (" + RepoSpec + ") — this is a real pip install")
if err := e.Pip(ctx, emit, RepoSpec); err != nil {
return Status{}, err
}
if err := os.MkdirAll(SandboxDir(dataDir), 0o755); err != nil {
return Status{}, err
}
st := GetStatus(ctx, dataDir)
if !st.Installed {
return st, errors.New("matrixsh installed but its version could not be resolved")
}
emit("matrixsh " + st.Version + " ready in the sandbox")
return st, nil
}
// Exec runs a command inside the sandbox venv (with matrixsh/python on PATH),
// in the sandbox working directory. Destructive commands are refused.
func Exec(ctx context.Context, dataDir, command string) (pyenv.Result, error) {
command = strings.TrimSpace(command)
if command == "" {
return pyenv.Result{}, errors.New("empty command")
}
if Denied(command) {
return pyenv.Result{}, ErrBlocked
}
e := env(dataDir)
if !e.Exists() {
return pyenv.Result{}, errors.New("MatrixShell sandbox is not installed yet")
}
return e.Shell(ctx, SandboxDir(dataDir), command)
}