源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,447 @@
|
||||
package runtimecmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Resolver struct {
|
||||
Environ func() []string
|
||||
HomeDir func() (string, error)
|
||||
IsExecutableFile func(string) bool
|
||||
LookPath func(string) (string, error)
|
||||
// ScutilProxy returns the raw output of `scutil --proxy` (and whether it is
|
||||
// available). It is injectable for legacy macOS parser tests; production
|
||||
// reads the platform system proxy through the build-tagged adapter.
|
||||
ScutilProxy func() (string, bool)
|
||||
}
|
||||
|
||||
// nestingGuardEnvKeys are the environment variables a parent Claude Code
|
||||
// session exports to detect (and refuse) nested launches. When tuttid itself
|
||||
// runs inside a Claude Code session these leak into spawned agent subprocesses, causing
|
||||
// a child `claude` to abort with "cannot be launched inside another Claude Code
|
||||
// session". They are stripped from the base environment so each spawned agent
|
||||
// starts as a fresh session; they are CLAUDE-specific and harmless to other runtimes.
|
||||
var nestingGuardEnvKeys = []string{
|
||||
"CLAUDECODE",
|
||||
"CLAUDE_CODE_ENTRYPOINT",
|
||||
"CLAUDE_CODE_SESSION_ID",
|
||||
"CLAUDE_CODE_CHILD_SESSION",
|
||||
}
|
||||
|
||||
func (r Resolver) Env(overrides []string) []string {
|
||||
baseEnv := stripEnvKeys(r.environ(), nestingGuardEnvKeys)
|
||||
env := append(baseEnv, overrides...)
|
||||
pathKey := pathEnvKey(env)
|
||||
pathGroups := [][]string{}
|
||||
if overridePathGroups := pathGroupsFromEnv(overrides, pathKey, envValue(baseEnv, pathKey)); len(overridePathGroups) > 0 {
|
||||
pathGroups = append(pathGroups, overridePathGroups...)
|
||||
pathGroups = append(pathGroups, preferredExecutableDirs(env))
|
||||
pathGroups = append(pathGroups, r.fallbackExecutableDirs())
|
||||
} else {
|
||||
pathGroups = append(pathGroups, preferredExecutableDirs(env))
|
||||
pathGroups = append(pathGroups, splitPathList(envValue(baseEnv, pathKey)))
|
||||
pathGroups = append(pathGroups, r.fallbackExecutableDirs())
|
||||
}
|
||||
pathDirs := mergePathDirs(pathGroups...)
|
||||
if len(pathDirs) > 0 {
|
||||
env = setEnvValue(env, pathKey, strings.Join(pathDirs, string(os.PathListSeparator)))
|
||||
}
|
||||
return r.injectSystemProxyEnv(env)
|
||||
}
|
||||
|
||||
func (r Resolver) Resolve(command string, env []string) string {
|
||||
command = strings.TrimSpace(command)
|
||||
if command == "" || strings.ContainsAny(command, `/\`) {
|
||||
return command
|
||||
}
|
||||
if candidates := r.ResolveAll(command, env); len(candidates) > 0 {
|
||||
return candidates[0]
|
||||
}
|
||||
return command
|
||||
}
|
||||
|
||||
// ResolveAll returns every executable match for command in the effective PATH,
|
||||
// preserving directory order and removing duplicate paths. It deliberately
|
||||
// does not call LookPath: callers that need an OS-specific fallback can append
|
||||
// that result explicitly, while discovery callers get a complete, stable view
|
||||
// of the resolver's own search plan.
|
||||
func (r Resolver) ResolveAll(command string, env []string) []string {
|
||||
command = strings.TrimSpace(command)
|
||||
if command == "" || strings.ContainsAny(command, "/\\") {
|
||||
return nil
|
||||
}
|
||||
return r.ResolveAllNames([]string{command}, env)
|
||||
}
|
||||
|
||||
// ResolveAllNames returns every executable match for a set of command names.
|
||||
// Directory order is primary and name order is secondary, which lets callers
|
||||
// enumerate platform launcher variants without changing PATH precedence.
|
||||
func (r Resolver) ResolveAllNames(commandNames []string, env []string) []string {
|
||||
result := []string{}
|
||||
seen := map[string]struct{}{}
|
||||
for _, dir := range splitPathList(envValue(env, pathEnvKey(env))) {
|
||||
for _, command := range commandNames {
|
||||
command = strings.TrimSpace(command)
|
||||
if command == "" || strings.ContainsAny(command, `/\\`) {
|
||||
continue
|
||||
}
|
||||
for _, executableName := range executableNameCandidates(command, env) {
|
||||
candidate := filepath.Join(dir, executableName)
|
||||
if !r.isExecutableFile(candidate) {
|
||||
continue
|
||||
}
|
||||
key := executablePathKey(candidate)
|
||||
if _, ok := seen[key]; ok {
|
||||
continue
|
||||
}
|
||||
seen[key] = struct{}{}
|
||||
result = append(result, candidate)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (r Resolver) ResolveBinary(binaryNames []string, overrides []string) string {
|
||||
env := r.Env(overrides)
|
||||
for _, binaryName := range binaryNames {
|
||||
binaryName = strings.TrimSpace(binaryName)
|
||||
if binaryName == "" {
|
||||
continue
|
||||
}
|
||||
path := r.Resolve(binaryName, env)
|
||||
if path != binaryName {
|
||||
return path
|
||||
}
|
||||
if path := r.lookPath(binaryName); path != "" {
|
||||
return path
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r Resolver) UserBinInstallDirs(overrides []string) []string {
|
||||
baseEnv := r.environ()
|
||||
env := append([]string{}, baseEnv...)
|
||||
env = append(env, overrides...)
|
||||
pathValue := envValue(baseEnv, pathEnvKey(baseEnv))
|
||||
if overridePath, ok := envValueFrom(overrides, pathEnvKey(env)); ok {
|
||||
pathValue = overridePath
|
||||
}
|
||||
candidates := [][]string{
|
||||
splitPathList(pathValue),
|
||||
}
|
||||
home, err := r.homeDir()
|
||||
if err == nil && strings.TrimSpace(home) != "" {
|
||||
managedNPMDirs := UserManagedNPMExecutableDirs(home)
|
||||
candidates = append(candidates, managedNPMDirs, []string{filepath.Join(home, "bin")})
|
||||
}
|
||||
return mergePathDirs(candidates...)
|
||||
}
|
||||
|
||||
func preferredExecutableDirs(env []string) []string {
|
||||
dirs := []string{}
|
||||
if nPrefix := strings.TrimSpace(envValue(env, "N_PREFIX")); nPrefix != "" {
|
||||
dirs = append([]string{filepath.Join(nPrefix, "bin")}, dirs...)
|
||||
}
|
||||
if pnpmHome := strings.TrimSpace(envValue(env, "PNPM_HOME")); pnpmHome != "" {
|
||||
dirs = append([]string{pnpmHome}, dirs...)
|
||||
}
|
||||
if voltaHome := strings.TrimSpace(envValue(env, "VOLTA_HOME")); voltaHome != "" {
|
||||
dirs = append([]string{filepath.Join(voltaHome, "bin")}, dirs...)
|
||||
}
|
||||
if asdfDataDir := strings.TrimSpace(envValue(env, "ASDF_DATA_DIR")); asdfDataDir != "" {
|
||||
dirs = append([]string{filepath.Join(asdfDataDir, "shims")}, dirs...)
|
||||
}
|
||||
if miseDataDir := strings.TrimSpace(envValue(env, "MISE_DATA_DIR")); miseDataDir != "" {
|
||||
dirs = append([]string{filepath.Join(miseDataDir, "shims")}, dirs...)
|
||||
}
|
||||
if fnmDir := strings.TrimSpace(envValue(env, "FNM_DIR")); fnmDir != "" {
|
||||
dirs = append(fnmNodeBinDirs(fnmDir), dirs...)
|
||||
}
|
||||
return dirs
|
||||
}
|
||||
|
||||
func (r Resolver) fallbackExecutableDirs() []string {
|
||||
dirs := []string{
|
||||
"/opt/homebrew/bin",
|
||||
"/usr/local/bin",
|
||||
"/usr/bin",
|
||||
"/bin",
|
||||
}
|
||||
homeDirs := []string{}
|
||||
home, err := r.homeDir()
|
||||
if err == nil && strings.TrimSpace(home) != "" {
|
||||
homeDirs = []string{
|
||||
filepath.Join(home, ".tutti", "bin"),
|
||||
filepath.Join(home, ".opencode", "bin"),
|
||||
}
|
||||
homeDirs = append(homeDirs, UserManagedNPMExecutableDirs(home)...)
|
||||
homeDirs = append(homeDirs,
|
||||
filepath.Join(home, "bin"),
|
||||
filepath.Join(home, ".npm-global", "bin"),
|
||||
filepath.Join(home, ".n", "bin"),
|
||||
filepath.Join(home, "n", "bin"),
|
||||
filepath.Join(home, ".volta", "bin"),
|
||||
filepath.Join(home, ".asdf", "shims"),
|
||||
filepath.Join(home, ".mise", "shims"),
|
||||
filepath.Join(home, ".bun", "bin"),
|
||||
filepath.Join(home, "Library", "pnpm"),
|
||||
)
|
||||
homeDirs = append(homeDirs, nvmNodeBinDirs(home)...)
|
||||
homeDirs = append(homeDirs, fnmNodeBinDirs(filepath.Join(home, ".fnm"))...)
|
||||
// Cursor's native Windows installer places agent.ps1 and its versioned
|
||||
// payload under %LOCALAPPDATA%\\cursor-agent rather than on PATH.
|
||||
homeDirs = append(homeDirs, filepath.Join(home, "AppData", "Local", "cursor-agent"))
|
||||
}
|
||||
return append(homeDirs, dirs...)
|
||||
}
|
||||
|
||||
func (r Resolver) environ() []string {
|
||||
if r.Environ != nil {
|
||||
return r.Environ()
|
||||
}
|
||||
return os.Environ()
|
||||
}
|
||||
|
||||
func (r Resolver) homeDir() (string, error) {
|
||||
if r.HomeDir != nil {
|
||||
return r.HomeDir()
|
||||
}
|
||||
return os.UserHomeDir()
|
||||
}
|
||||
|
||||
func (r Resolver) isExecutableFile(path string) bool {
|
||||
if r.IsExecutableFile != nil {
|
||||
return r.IsExecutableFile(path)
|
||||
}
|
||||
return isExecutableFile(path)
|
||||
}
|
||||
|
||||
func (r Resolver) lookPath(binaryName string) string {
|
||||
lookPath := r.LookPath
|
||||
if lookPath == nil {
|
||||
lookPath = exec.LookPath
|
||||
}
|
||||
path, err := lookPath(binaryName)
|
||||
if err == nil && strings.TrimSpace(path) != "" {
|
||||
return strings.TrimSpace(path)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func nvmNodeBinDirs(home string) []string {
|
||||
matches, err := filepath.Glob(filepath.Join(home, ".nvm", "versions", "node", "*", "bin"))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
sort.SliceStable(matches, func(i, j int) bool {
|
||||
return compareNVMNodeBinDirs(matches[i], matches[j]) > 0
|
||||
})
|
||||
return matches
|
||||
}
|
||||
|
||||
func compareNVMNodeBinDirs(left string, right string) int {
|
||||
leftVersion := nvmNodeVersion(left)
|
||||
rightVersion := nvmNodeVersion(right)
|
||||
for i := 0; i < max(len(leftVersion), len(rightVersion)); i++ {
|
||||
leftPart := versionPart(leftVersion, i)
|
||||
rightPart := versionPart(rightVersion, i)
|
||||
if leftPart > rightPart {
|
||||
return 1
|
||||
}
|
||||
if leftPart < rightPart {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
return strings.Compare(left, right)
|
||||
}
|
||||
|
||||
func nvmNodeVersion(binDir string) []int {
|
||||
versionDir := filepath.Base(filepath.Dir(binDir))
|
||||
version := strings.TrimPrefix(versionDir, "v")
|
||||
parts := strings.Split(version, ".")
|
||||
result := make([]int, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
value, err := strconv.Atoi(part)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
result = append(result, value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func versionPart(version []int, index int) int {
|
||||
if index < 0 || index >= len(version) {
|
||||
return 0
|
||||
}
|
||||
return version[index]
|
||||
}
|
||||
|
||||
func fnmNodeBinDirs(fnmDir string) []string {
|
||||
matches, err := filepath.Glob(filepath.Join(fnmDir, "node-versions", "*", "installation", "bin"))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
func mergePathDirs(groups ...[]string) []string {
|
||||
result := []string{}
|
||||
seen := map[string]bool{}
|
||||
for _, group := range groups {
|
||||
for _, dir := range group {
|
||||
normalized := strings.TrimSpace(dir)
|
||||
if normalized == "" {
|
||||
continue
|
||||
}
|
||||
key := pathDirKey(normalized)
|
||||
if seen[key] {
|
||||
continue
|
||||
}
|
||||
seen[key] = true
|
||||
result = append(result, normalized)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func pathDirKey(dir string) string {
|
||||
key := filepath.Clean(strings.TrimSpace(dir))
|
||||
if runtime.GOOS == "windows" {
|
||||
key = strings.ToLower(key)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func executablePathKey(path string) string {
|
||||
return pathDirKey(path)
|
||||
}
|
||||
|
||||
func pathEnvKey(env []string) string {
|
||||
for i := len(env) - 1; i >= 0; i-- {
|
||||
key, _, ok := strings.Cut(env[i], "=")
|
||||
if ok && strings.EqualFold(key, "PATH") {
|
||||
return key
|
||||
}
|
||||
}
|
||||
return "PATH"
|
||||
}
|
||||
|
||||
func envValue(env []string, key string) string {
|
||||
value, _ := envValueFrom(env, key)
|
||||
return value
|
||||
}
|
||||
|
||||
func envValueFrom(env []string, key string) (string, bool) {
|
||||
for i := len(env) - 1; i >= 0; i-- {
|
||||
candidateKey, value, ok := strings.Cut(env[i], "=")
|
||||
if ok && strings.EqualFold(candidateKey, key) {
|
||||
return value, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func pathGroupsFromEnv(env []string, key string, basePath string) [][]string {
|
||||
groups := [][]string{}
|
||||
baseDirs := splitPathList(basePath)
|
||||
inheritedBaseDirs := []string{}
|
||||
for i := len(env) - 1; i >= 0; i-- {
|
||||
candidateKey, value, ok := strings.Cut(env[i], "=")
|
||||
if ok && strings.EqualFold(candidateKey, key) {
|
||||
dirs := splitPathList(value)
|
||||
if prefix, inherited, ok := splitInheritedPath(dirs, baseDirs); ok {
|
||||
groups = append(groups, prefix)
|
||||
if len(inheritedBaseDirs) == 0 {
|
||||
inheritedBaseDirs = inherited
|
||||
}
|
||||
continue
|
||||
}
|
||||
groups = append(groups, dirs)
|
||||
}
|
||||
}
|
||||
if len(inheritedBaseDirs) > 0 {
|
||||
groups = append(groups, inheritedBaseDirs)
|
||||
}
|
||||
return groups
|
||||
}
|
||||
|
||||
// splitPathList follows the host separator but also accepts the colon form
|
||||
// emitted by MSYS/Unix-like test and launcher environments on Windows. A
|
||||
// native Windows path containing a drive letter or backslashes remains on the
|
||||
// normal semicolon path-list path.
|
||||
func splitPathList(value string) []string {
|
||||
if runtime.GOOS == "windows" && !strings.Contains(value, ";") &&
|
||||
strings.Contains(value, ":") && !strings.Contains(value, `\`) {
|
||||
return strings.Split(value, ":")
|
||||
}
|
||||
return filepath.SplitList(value)
|
||||
}
|
||||
|
||||
func splitInheritedPath(dirs []string, baseDirs []string) ([]string, []string, bool) {
|
||||
if len(baseDirs) == 0 || len(dirs) < len(baseDirs) {
|
||||
return nil, nil, false
|
||||
}
|
||||
for i := 0; i+len(baseDirs) <= len(dirs); i++ {
|
||||
if pathDirSlicesEqual(dirs[i:i+len(baseDirs)], baseDirs) {
|
||||
return dirs[:i], dirs[i:], true
|
||||
}
|
||||
}
|
||||
return nil, nil, false
|
||||
}
|
||||
|
||||
func pathDirSlicesEqual(a []string, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if pathDirKey(a[i]) != pathDirKey(b[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func stripEnvKeys(env []string, keys []string) []string {
|
||||
if len(keys) == 0 {
|
||||
return env
|
||||
}
|
||||
next := make([]string, 0, len(env))
|
||||
for _, item := range env {
|
||||
candidateKey, _, ok := strings.Cut(item, "=")
|
||||
if ok {
|
||||
drop := false
|
||||
for _, key := range keys {
|
||||
if strings.EqualFold(candidateKey, key) {
|
||||
drop = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if drop {
|
||||
continue
|
||||
}
|
||||
}
|
||||
next = append(next, item)
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
func setEnvValue(env []string, key string, value string) []string {
|
||||
next := make([]string, 0, len(env)+1)
|
||||
for _, item := range env {
|
||||
candidateKey, _, ok := strings.Cut(item, "=")
|
||||
if ok && strings.EqualFold(candidateKey, key) {
|
||||
continue
|
||||
}
|
||||
next = append(next, item)
|
||||
}
|
||||
return append(next, key+"="+value)
|
||||
}
|
||||
Reference in New Issue
Block a user