Why: The sandbox hides the host home directory and has no network, so pip install --user cannot supply python-docx, and the service PATH resolved python3 to a linuxbrew path that does not exist inside the sandbox. Evidence: SELF go test of execenv, tools, store, app, server, runmgr, config, and cmd/agent-system on this worktree. CI has not run. Impact: Sessions inherit a read-only base layer. Extra packages are requested through python_env, auto-approved only from the allowlist, and mounted per conversation. Schema 35 adds the request and binding tables. Refs: .task/feature-layered-python-env.md Co-authored-by: Cursor <cursoragent@cursor.com>
63 lines
1.5 KiB
Go
63 lines
1.5 KiB
Go
package execenv
|
|
|
|
import (
|
|
"encoding/json"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// Lock 是一层的不可变清单。ID 由规范 JSON 决定,与写盘时的空白无关。
|
|
type Lock struct {
|
|
Schema int `json:"schema"`
|
|
Kind string `json:"kind"` // base | extension | isolated
|
|
PythonTag string `json:"pythonTag"`
|
|
Platform string `json:"platform"`
|
|
BaseID string `json:"baseId,omitempty"`
|
|
Packages []LockedPkg `json:"packages"`
|
|
}
|
|
|
|
// LockedPkg 是一个 wheel。SHA256 是文件字节的哈希,不是文件名。
|
|
type LockedPkg struct {
|
|
Name string `json:"name"`
|
|
Version string `json:"version"`
|
|
Filename string `json:"filename"`
|
|
SHA256 string `json:"sha256"`
|
|
Size int64 `json:"size,omitempty"`
|
|
}
|
|
|
|
// ID 返回 base-<hash> / x-<hash> / iso-<hash>。
|
|
func (l Lock) ID() string {
|
|
canon := l
|
|
canon.Packages = append([]LockedPkg(nil), l.Packages...)
|
|
sort.Slice(canon.Packages, func(i, j int) bool {
|
|
return normName(canon.Packages[i].Name) < normName(canon.Packages[j].Name)
|
|
})
|
|
raw, _ := json.Marshal(canon)
|
|
sum := HashBytes(raw)
|
|
prefix := "x-"
|
|
switch l.Kind {
|
|
case "base":
|
|
prefix = "base-"
|
|
case "isolated":
|
|
prefix = "iso-"
|
|
}
|
|
return prefix + sum[:16]
|
|
}
|
|
|
|
func normName(name string) string {
|
|
var b strings.Builder
|
|
prevDash := false
|
|
for _, r := range strings.ToLower(name) {
|
|
if r == '-' || r == '_' || r == '.' {
|
|
if !prevDash && b.Len() > 0 {
|
|
b.WriteByte('-')
|
|
prevDash = true
|
|
}
|
|
continue
|
|
}
|
|
prevDash = false
|
|
b.WriteRune(r)
|
|
}
|
|
return strings.Trim(b.String(), "-")
|
|
}
|