Files
agent-radar 2ed3415fb7
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
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

619 lines
25 KiB
Go

package agentstatus
import (
"context"
"errors"
"os"
"path/filepath"
"runtime"
"slices"
"strings"
"testing"
managedruntime "github.com/tutti-os/tutti/services/tuttid/service/managedruntime"
)
func TestDisplayNPMRegistryStripsCredentials(t *testing.T) {
t.Parallel()
cases := map[string]string{
// Plain registries (and the test override) pass through unchanged.
"https://registry.npmjs.org": "https://registry.npmjs.org",
"https://registry.example.test": "https://registry.example.test",
"registry.example.test": "registry.example.test",
// Embedded credentials are stripped before status/log exposure.
"https://user:token@registry.foo/path": "https://registry.foo/path",
"https://token@registry.foo": "https://registry.foo",
}
for in, want := range cases {
if got := displayNPMRegistry(in); got != want {
t.Errorf("displayNPMRegistry(%q) = %q, want %q", in, got, want)
}
}
}
func TestCodexNPMPrefixFromPackageDir(t *testing.T) {
t.Parallel()
cases := map[string]string{
// Unix npm global layout: <prefix>/lib/node_modules/@openai/codex
filepath.FromSlash("/Users/x/.nvm/versions/node/v24.12.0/lib/node_modules/@openai/codex"): filepath.FromSlash("/Users/x/.nvm/versions/node/v24.12.0"),
filepath.FromSlash("/Users/x/.local/lib/node_modules/@openai/codex"): filepath.FromSlash("/Users/x/.local"),
filepath.FromSlash("/usr/local/lib/node_modules/@openai/codex"): filepath.FromSlash("/usr/local"),
// Windows npm global layout: <prefix>/node_modules/@openai/codex (no lib)
filepath.FromSlash("C:/Users/x/AppData/Roaming/npm/node_modules/@openai/codex"): filepath.FromSlash("C:/Users/x/AppData/Roaming/npm"),
// Not npm's global layout -> no prefix derivable.
filepath.Join("/tmp/standalone/codex"): "",
filepath.FromSlash("/node_modules/@openai/codex"): "",
}
for in, want := range cases {
if got := npmGlobalPrefixFromPackageDir(in); got != want {
t.Errorf("npmGlobalPrefixFromPackageDir(%q) = %q, want %q", in, got, want)
}
}
}
func TestManagedNPMGlobalPackageDirRejectsUnsafePackageNames(t *testing.T) {
t.Parallel()
for _, packageName := range []string{
"",
".",
"..",
"../tutti-agent",
"@tutti-os",
"@tutti-os/../tutti-agent",
"@tutti-os/tutti-agent/extra",
`@tutti-os\tutti-agent`,
} {
if path, ok := managedNPMGlobalPackageDir("/safe/prefix", packageName); ok {
t.Errorf("managedNPMGlobalPackageDir(%q) = %q, want rejected", packageName, path)
}
}
}
// TestRunCodexCLILatestInstallerRepairsInPlace verifies that when an existing
// @openai/codex install is resolved but incomplete, the installer reinstalls
// into the npm global prefix that already owns it (repair-in-place) rather than
// duplicating the package in ~/.local.
func TestRunCodexCLILatestInstallerRepairsInPlace(t *testing.T) {
home := t.TempDir()
// Mimic an nvm-style global install with a missing platform subpackage.
nvmPrefix := filepath.Join(home, ".nvm", "versions", "node", "v24.12.0")
pkgDir := filepath.Join(nvmPrefix, "lib", "node_modules", "@openai", "codex")
writePackageManifest(t, pkgDir, "@openai/codex", MinSupportedCodexVersion)
codexBin := filepath.Join(pkgDir, "bin", "codex")
writeExecutable(t, codexBin, "#!/bin/sh\nexit 0\n")
binDir := filepath.Join(nvmPrefix, "bin")
if err := os.MkdirAll(binDir, 0o755); err != nil {
t.Fatalf("mkdir bin dir: %v", err)
}
if err := os.Symlink(codexBin, filepath.Join(binDir, "codex")); err != nil {
t.Fatalf("symlink codex: %v", err)
}
// Fake npm/node on PATH so the resolver finds them.
writeExecutable(t, filepath.Join(binDir, npmBinaryNameForTest()), "#!/bin/sh\nexit 0\n")
writeExecutable(t, filepath.Join(binDir, nodeBinaryNameForTest()), "#!/bin/sh\nexit 0\n")
service := probeTestService(home)
service.HTTPClient = agentNPMRegistryProbeHTTPClient(nil)
service.Environ = func() []string { return []string{"PATH=" + binDir} }
service.IsExecutableFile = isTestExecutableUnderHome(home)
existingCLIPath := filepath.Join(binDir, "codex")
wantPrefix, wantPrefixOK := managedNPMRepairInstallPrefix(existingCLIPath, "@openai/codex")
if !wantPrefixOK {
t.Fatalf("expected repair prefix to be derivable for %s", existingCLIPath)
}
var command InstallCommandInput
service.InstallCommand = func(_ context.Context, input InstallCommandInput) (InstallCommandResult, error) {
command = input
return InstallCommandResult{ExitCode: 0, Stdout: "repaired"}, nil
}
if _, err := service.runCodexCLILatestInstaller(context.Background(), "codex", InstallerSpec{
Kind: InstallerKindCodexCLILatest,
CodexCLI: codexCLIInstallerSpec().CodexCLI,
}, existingCLIPath); err != nil {
t.Fatalf("runCodexCLILatestInstaller() error = %v", err)
}
if !strings.Contains(command.Command, "--prefix "+wantPrefix+" ") {
t.Fatalf("Command = %q, want repair-in-place at --prefix %s", command.Command, wantPrefix)
}
if strings.Contains(command.Command, filepath.Join(home, ".local")) {
t.Fatalf("Command = %q, repair-in-place must not duplicate the package in ~/.local", command.Command)
}
}
func TestManagedNPMResultHasReplacementLock(t *testing.T) {
if !managedNPMResultHasReplacementLock(InstallCommandResult{
ExitCode: 0,
Stderr: "npm warn cleanup EPERM: operation not permitted, unlink 'tutti-agent.exe'",
}) {
t.Fatal("EPERM executable replacement warning was not classified as a partial install")
}
if managedNPMResultHasReplacementLock(InstallCommandResult{
ExitCode: 0,
Stderr: "npm WARN deprecated package@1.0.0: this package is deprecated",
}) {
t.Fatal("unrelated npm warning was classified as a replacement lock")
}
}
func TestRunManagedNPMPackageInstallerRejectsPersistentLockedExecutableReplacement(t *testing.T) {
home := t.TempDir()
runtimeRoot := fakeManagedRuntimeRoot(t)
managedNPM := filepath.Join(runtimeRoot, "node", "bin", npmBinaryNameForTest())
managedNode := filepath.Join(runtimeRoot, "node", "bin", nodeBinaryNameForTest())
managedNodeBinDir := filepath.Dir(managedNode)
service := probeTestService(home)
service.HTTPClient = agentNPMRegistryProbeHTTPClient(nil)
service.Environ = func() []string {
return []string{"PATH=/usr/bin:/bin", agentNPMRegistryEnv + "=https://registry.example.test"}
}
service.ManagedRuntime = staticManagedRuntimeResolver{
runtime: managedruntime.ResolvedRuntime{
Root: runtimeRoot,
Node: managedNode,
NPM: managedNPM,
BinDirs: []string{managedNodeBinDir},
EnvOverrides: []string{
"TUTTI_APP_RUNTIME_ROOT=" + runtimeRoot,
"TUTTI_APP_NODE=" + managedNode,
"TUTTI_APP_NPM=" + managedNPM,
"PATH=" + managedNodeBinDir + string(os.PathListSeparator) + "/usr/bin" + string(os.PathListSeparator) + "/bin",
},
},
}
service.IsExecutableFile = isTestExecutableUnderHome(home)
installCalls := 0
service.InstallCommand = func(_ context.Context, _ InstallCommandInput) (InstallCommandResult, error) {
installCalls++
return InstallCommandResult{
ExitCode: 0,
Stderr: "npm warn cleanup EPERM: operation not permitted, unlink 'tutti-agent.exe'",
}, nil
}
result, err := service.runManagedNPMPackageInstaller(context.Background(), "tutti-agent", ManagedNPMPackageInstallerSpec{
PackageName: "@tutti-os/tutti-agent",
BinaryName: "tutti-agent",
IncludeOptional: true,
}, "")
if err != nil {
t.Fatalf("runManagedNPMPackageInstaller() error = %v", err)
}
if installCalls != 2 {
t.Fatalf("install command calls = %d, want one retry after locked replacement", installCalls)
}
if result.ExitCode == 0 {
t.Fatalf("result = %#v, want persistent locked replacement to fail", result)
}
}
// TestRunCodexCLILatestInstallerFallsBackToLocalBin verifies that when the
// existing codex binary is not from an npm global install (no package layout to
// derive a prefix from), the installer falls back to a fresh install in ~/.local.
func TestRunCodexCLILatestInstallerFallsBackToLocalBin(t *testing.T) {
home := t.TempDir()
binDir := filepath.Join(home, "bin")
// Standalone codex binary with no @openai/codex package.json above it.
standalone := filepath.Join(binDir, "codex")
writeExecutable(t, standalone, "#!/bin/sh\nexit 0\n")
writeExecutable(t, filepath.Join(binDir, npmBinaryNameForTest()), "#!/bin/sh\nexit 0\n")
writeExecutable(t, filepath.Join(binDir, nodeBinaryNameForTest()), "#!/bin/sh\nexit 0\n")
service := probeTestService(home)
service.HTTPClient = agentNPMRegistryProbeHTTPClient(nil)
service.Environ = func() []string { return []string{"PATH=" + binDir} }
service.IsExecutableFile = isTestExecutableUnderHome(home)
if _, ok := managedNPMRepairInstallPrefix(standalone, "@openai/codex"); ok {
t.Fatalf("standalone codex should not yield a repair prefix")
}
var command InstallCommandInput
service.InstallCommand = func(_ context.Context, input InstallCommandInput) (InstallCommandResult, error) {
command = input
return InstallCommandResult{ExitCode: 0, Stdout: "installed"}, nil
}
if _, err := service.runCodexCLILatestInstaller(context.Background(), "codex", InstallerSpec{
Kind: InstallerKindCodexCLILatest,
CodexCLI: codexCLIInstallerSpec().CodexCLI,
}, standalone); err != nil {
t.Fatalf("runCodexCLILatestInstaller() error = %v", err)
}
wantPrefix := managedNPMInstallPrefixForTest(home)
if !strings.Contains(command.Command, wantPrefix) {
t.Fatalf("Command = %q, want fresh install at --prefix %s", command.Command, wantPrefix)
}
}
func TestRunCodexCLILatestInstallerUsesManagedRuntimeNPMWhenUserNPMMissing(t *testing.T) {
const provider = "descriptor-codex"
home := t.TempDir()
runtimeRoot := fakeManagedRuntimeRoot(t)
managedNPM := filepath.Join(runtimeRoot, "node", "bin", npmBinaryNameForTest())
managedNode := filepath.Join(runtimeRoot, "node", "bin", nodeBinaryNameForTest())
managedNodeBinDir := filepath.Dir(managedNode)
service := probeTestService(home)
service.HTTPClient = agentNPMRegistryProbeHTTPClient(nil)
service.Environ = func() []string {
return []string{"PATH=/usr/bin:/bin", agentNPMRegistryEnv + "=https://registry.example.test"}
}
service.ManagedRuntime = staticManagedRuntimeResolver{
runtime: managedruntime.ResolvedRuntime{
Root: runtimeRoot,
Node: managedNode,
NPM: managedNPM,
BinDirs: []string{managedNodeBinDir},
EnvOverrides: []string{
"TUTTI_APP_RUNTIME_ROOT=" + runtimeRoot,
"TUTTI_APP_NODE=" + managedNode,
"TUTTI_APP_NPM=" + managedNPM,
"PATH=" + managedNodeBinDir + string(os.PathListSeparator) + "/usr/bin" + string(os.PathListSeparator) + "/bin",
},
},
}
service.IsExecutableFile = isTestExecutableUnderHome(home)
var command InstallCommandInput
service.InstallCommand = func(_ context.Context, input InstallCommandInput) (InstallCommandResult, error) {
command = input
return InstallCommandResult{ExitCode: 0, Stdout: "installed"}, nil
}
ctx := withActiveActionToken(context.Background(), nextActiveActionToken())
claimActiveAction(ctx, provider, ActiveAction{ID: ActionInstall, Status: "running"})
defer clearActiveAction(ctx, provider)
if _, err := service.runCodexCLILatestInstaller(ctx, provider, InstallerSpec{
Kind: InstallerKindCodexCLILatest,
CodexCLI: codexCLIInstallerSpec().CodexCLI,
}, ""); err != nil {
t.Fatalf("runCodexCLILatestInstaller() error = %v", err)
}
if !strings.Contains(command.Command, managedNPM) ||
!strings.Contains(command.Command, "install") ||
!strings.Contains(command.Command, "@openai/codex") ||
!strings.Contains(command.Command, "--include=optional") ||
!strings.Contains(command.Command, "--prefix") {
t.Fatalf("Command = %q, want managed runtime npm install", command.Command)
}
if !slices.Contains(command.Env, "TUTTI_APP_NPM="+managedNPM) {
t.Fatalf("Env = %#v, want managed runtime npm marker", command.Env)
}
if !slices.Contains(command.Env, "TUTTI_APP_NODE="+managedNode) {
t.Fatalf("Env = %#v, want managed runtime node marker", command.Env)
}
if !slices.Contains(command.Env, "npm_config_registry=https://registry.example.test") {
t.Fatalf("Env = %#v, want selected npm registry", command.Env)
}
action := activeActionForProvider(provider)
if action == nil {
t.Fatal("custom provider active action missing")
}
if action.Registry != "https://registry.example.test" || action.NodeTarget != managedNode {
t.Fatalf("custom provider active action = %#v", action)
}
}
func TestRunManagedNPMPackageInstallerInstallsTuttiAgentWithManagedRuntime(t *testing.T) {
home := t.TempDir()
runtimeRoot := fakeManagedRuntimeRoot(t)
managedNPM := filepath.Join(runtimeRoot, "node", "bin", npmBinaryNameForTest())
managedNode := filepath.Join(runtimeRoot, "node", "bin", nodeBinaryNameForTest())
managedNodeBinDir := filepath.Dir(managedNode)
service := probeTestService(home)
service.HTTPClient = agentNPMRegistryProbeHTTPClient(nil)
service.Environ = func() []string {
return []string{"PATH=/usr/bin:/bin", agentNPMRegistryEnv + "=https://registry.example.test"}
}
service.ManagedRuntime = staticManagedRuntimeResolver{
runtime: managedruntime.ResolvedRuntime{
Root: runtimeRoot,
Node: managedNode,
NPM: managedNPM,
BinDirs: []string{managedNodeBinDir},
EnvOverrides: []string{
"TUTTI_APP_RUNTIME_ROOT=" + runtimeRoot,
"TUTTI_APP_NODE=" + managedNode,
"TUTTI_APP_NPM=" + managedNPM,
"PATH=" + managedNodeBinDir + string(os.PathListSeparator) + "/usr/bin" + string(os.PathListSeparator) + "/bin",
},
},
}
service.IsExecutableFile = isTestExecutableUnderHome(home)
var command InstallCommandInput
service.InstallCommand = func(_ context.Context, input InstallCommandInput) (InstallCommandResult, error) {
command = input
return InstallCommandResult{ExitCode: 0, Stdout: "installed"}, nil
}
if _, err := service.runManagedNPMPackageInstaller(context.Background(), "tutti-agent", ManagedNPMPackageInstallerSpec{
PackageName: "@tutti-os/tutti-agent",
BinaryName: "tutti-agent",
IncludeOptional: true,
}, ""); err != nil {
t.Fatalf("runManagedNPMPackageInstaller() error = %v", err)
}
if !strings.Contains(command.Command, managedNPM) ||
!strings.Contains(command.Command, "install") ||
!strings.Contains(command.Command, "@tutti-os/tutti-agent") ||
!strings.Contains(command.Command, "--include=optional") ||
!strings.Contains(command.Command, "--prefix") {
t.Fatalf("Command = %q, want managed runtime npm install", command.Command)
}
if !slices.Contains(command.Env, "TUTTI_APP_NPM="+managedNPM) {
t.Fatalf("Env = %#v, want managed runtime npm marker", command.Env)
}
if !slices.Contains(command.Env, "TUTTI_APP_NODE="+managedNode) {
t.Fatalf("Env = %#v, want managed runtime node marker", command.Env)
}
if !slices.Contains(command.Env, "npm_config_registry=https://registry.example.test") {
t.Fatalf("Env = %#v, want selected npm registry", command.Env)
}
}
func TestRunManagedNPMPackageInstallerCleansOnlyOwnStaleStagingDirectories(t *testing.T) {
home := t.TempDir()
runtimeRoot := fakeManagedRuntimeRoot(t)
managedNPM := filepath.Join(runtimeRoot, "node", "bin", npmBinaryNameForTest())
managedNode := filepath.Join(runtimeRoot, "node", "bin", nodeBinaryNameForTest())
managedNodeBinDir := filepath.Dir(managedNode)
installPrefix := managedNPMInstallPrefixForTest(home)
packageDir, ok := managedNPMGlobalPackageDir(installPrefix, "@tutti-os/tutti-agent")
if !ok {
t.Fatal("managed npm package directory was not resolved")
}
staleDir := filepath.Join(filepath.Dir(packageDir), ".tutti-agent-8IDARXyS")
unrelatedStagingDir := filepath.Join(filepath.Dir(packageDir), ".other-package-12345678")
for _, dir := range []string{packageDir, staleDir, unrelatedStagingDir} {
if err := os.MkdirAll(dir, 0o755); err != nil {
t.Fatalf("mkdir %s: %v", dir, err)
}
}
service := probeTestService(home)
service.HTTPClient = agentNPMRegistryProbeHTTPClient(nil)
service.Environ = func() []string {
return []string{"PATH=/usr/bin:/bin", agentNPMRegistryEnv + "=https://registry.example.test"}
}
service.ManagedRuntime = staticManagedRuntimeResolver{
runtime: managedruntime.ResolvedRuntime{
Root: runtimeRoot,
Node: managedNode,
NPM: managedNPM,
BinDirs: []string{managedNodeBinDir},
EnvOverrides: []string{
"TUTTI_APP_RUNTIME_ROOT=" + runtimeRoot,
"TUTTI_APP_NODE=" + managedNode,
"TUTTI_APP_NPM=" + managedNPM,
"PATH=" + managedNodeBinDir + string(os.PathListSeparator) + "/usr/bin" + string(os.PathListSeparator) + "/bin",
},
},
}
service.IsExecutableFile = isTestExecutableUnderHome(home)
service.InstallCommand = func(_ context.Context, _ InstallCommandInput) (InstallCommandResult, error) {
if _, err := os.Stat(staleDir); !os.IsNotExist(err) {
t.Fatalf("stale package staging directory still exists before install: %v", err)
}
for _, path := range []string{packageDir, unrelatedStagingDir} {
if _, err := os.Stat(path); err != nil {
t.Fatalf("unrelated path %s was removed: %v", path, err)
}
}
return InstallCommandResult{ExitCode: 0, Stdout: "installed"}, nil
}
if _, err := service.runManagedNPMPackageInstaller(context.Background(), "tutti-agent", ManagedNPMPackageInstallerSpec{
PackageName: "@tutti-os/tutti-agent",
BinaryName: "tutti-agent",
IncludeOptional: true,
}, ""); err != nil {
t.Fatalf("runManagedNPMPackageInstaller() error = %v", err)
}
}
func TestRunManagedNPMPackageInstallerCleansStagingAfterInterruptedAttempt(t *testing.T) {
home := t.TempDir()
runtimeRoot := fakeManagedRuntimeRoot(t)
managedNPM := filepath.Join(runtimeRoot, "node", "bin", npmBinaryNameForTest())
managedNode := filepath.Join(runtimeRoot, "node", "bin", nodeBinaryNameForTest())
managedNodeBinDir := filepath.Dir(managedNode)
installPrefix := managedNPMInstallPrefixForTest(home)
packageDir, ok := managedNPMGlobalPackageDir(installPrefix, "@tutti-os/tutti-agent")
if !ok {
t.Fatal("managed npm package directory was not resolved")
}
staleDir := filepath.Join(filepath.Dir(packageDir), ".tutti-agent-canceled")
service := probeTestService(home)
service.HTTPClient = agentNPMRegistryProbeHTTPClient(nil)
service.Environ = func() []string {
return []string{"PATH=/usr/bin:/bin", agentNPMRegistryEnv + "=https://registry.example.test"}
}
service.ManagedRuntime = staticManagedRuntimeResolver{
runtime: managedruntime.ResolvedRuntime{
Root: runtimeRoot,
Node: managedNode,
NPM: managedNPM,
BinDirs: []string{managedNodeBinDir},
EnvOverrides: []string{
"TUTTI_APP_RUNTIME_ROOT=" + runtimeRoot,
"TUTTI_APP_NODE=" + managedNode,
"TUTTI_APP_NPM=" + managedNPM,
"PATH=" + managedNodeBinDir + string(os.PathListSeparator) + "/usr/bin" + string(os.PathListSeparator) + "/bin",
},
},
}
service.IsExecutableFile = isTestExecutableUnderHome(home)
service.InstallCommand = func(_ context.Context, _ InstallCommandInput) (InstallCommandResult, error) {
if err := os.MkdirAll(staleDir, 0o755); err != nil {
t.Fatalf("mkdir stale staging directory: %v", err)
}
return InstallCommandResult{ExitCode: -1}, context.Canceled
}
if _, err := service.runManagedNPMPackageInstaller(context.Background(), "tutti-agent", ManagedNPMPackageInstallerSpec{
PackageName: "@tutti-os/tutti-agent",
BinaryName: "tutti-agent",
IncludeOptional: true,
}, ""); !errors.Is(err, context.Canceled) {
t.Fatalf("runManagedNPMPackageInstaller() error = %v, want context canceled", err)
}
if _, err := os.Stat(staleDir); !os.IsNotExist(err) {
t.Fatalf("stale staging directory remains after interrupted install: %v", err)
}
}
func TestRunManagedNPMPackageInstallerRepairsManagedBinEEXIST(t *testing.T) {
home := t.TempDir()
runtimeRoot := fakeManagedRuntimeRoot(t)
managedNPM := filepath.Join(runtimeRoot, "node", "bin", npmBinaryNameForTest())
managedNode := filepath.Join(runtimeRoot, "node", "bin", nodeBinaryNameForTest())
managedNodeBinDir := filepath.Dir(managedNode)
conflictPath := filepath.Join(home, ".local", "bin", "tutti-agent")
writeExecutable(t, conflictPath, "#!/bin/sh\nif [ \"$1\" = \"--version\" ]; then echo 'tutti-agent 0.0.5.1'; exit 0; fi\nexit 0\n")
service := probeTestService(home)
service.HTTPClient = agentNPMRegistryProbeHTTPClient(nil)
service.Environ = func() []string {
return []string{"PATH=/usr/bin:/bin", agentNPMRegistryEnv + "=https://registry.example.test"}
}
service.ManagedRuntime = staticManagedRuntimeResolver{
runtime: managedruntime.ResolvedRuntime{
Root: runtimeRoot,
Node: managedNode,
NPM: managedNPM,
BinDirs: []string{managedNodeBinDir},
EnvOverrides: []string{
"TUTTI_APP_RUNTIME_ROOT=" + runtimeRoot,
"TUTTI_APP_NODE=" + managedNode,
"TUTTI_APP_NPM=" + managedNPM,
"PATH=" + managedNodeBinDir + string(os.PathListSeparator) + "/usr/bin" + string(os.PathListSeparator) + "/bin",
},
},
}
service.IsExecutableFile = isTestExecutableUnderHome(home)
var commands []InstallCommandInput
service.InstallCommand = func(_ context.Context, input InstallCommandInput) (InstallCommandResult, error) {
commands = append(commands, input)
if len(commands) == 1 {
return InstallCommandResult{
ExitCode: 1,
Stderr: "npm error code EEXIST\nnpm error path " + conflictPath + "\nnpm error File exists: " + conflictPath,
}, nil
}
if _, err := os.Stat(conflictPath); !os.IsNotExist(err) {
t.Fatalf("conflict path still exists before retry: %v", err)
}
return InstallCommandResult{ExitCode: 0, Stdout: "installed"}, nil
}
if _, err := service.runManagedNPMPackageInstaller(context.Background(), "tutti-agent", ManagedNPMPackageInstallerSpec{
PackageName: "@tutti-os/tutti-agent",
PackageVersion: "0.0.5",
BinaryName: "tutti-agent",
IncludeOptional: true,
}, conflictPath); err != nil {
t.Fatalf("runManagedNPMPackageInstaller() error = %v", err)
}
if len(commands) != 2 {
t.Fatalf("install command calls = %d, want EEXIST retry", len(commands))
}
}
type staticManagedRuntimeResolver struct {
runtime managedruntime.ResolvedRuntime
}
func TestManagedNPMRepairInstallPrefixFindsWindowsNPMShim(t *testing.T) {
if runtime.GOOS != "windows" {
t.Skip("Windows npm shims use a different global layout")
}
home := t.TempDir()
prefix := filepath.Join(home, ".local", "bin")
packageDir := filepath.Join(prefix, "node_modules", "@openai", "codex")
writePackageManifest(t, packageDir, "@openai/codex", MinSupportedCodexVersion)
shim := filepath.Join(prefix, "codex.cmd")
if err := os.WriteFile(shim, []byte("@echo off\r\n"), 0o755); err != nil {
t.Fatal(err)
}
if got, ok := managedNPMRepairInstallPrefix(shim, "@openai/codex"); !ok || got != prefix {
t.Fatalf("managedNPMRepairInstallPrefix() = %q, %t; want %q, true", got, ok, prefix)
}
}
func TestRunCodexCLILatestInstallerRepairsLegacyWindowsPrefixInPlace(t *testing.T) {
if runtime.GOOS != "windows" {
t.Skip("Windows legacy npm prefix behavior")
}
home := t.TempDir()
legacyPrefix := filepath.Join(home, ".local")
packageDir := filepath.Join(legacyPrefix, "node_modules", "@openai", "codex")
writePackageManifest(t, packageDir, "@openai/codex", MinSupportedCodexVersion)
legacyShim := filepath.Join(legacyPrefix, "codex.cmd")
writeExecutable(t, legacyShim, "@echo off\r\n")
runtimeRoot := fakeManagedRuntimeRoot(t)
managedNPM := filepath.Join(runtimeRoot, "node", "bin", npmBinaryNameForTest())
managedNode := filepath.Join(runtimeRoot, "node", "bin", nodeBinaryNameForTest())
service := probeTestService(home)
service.HTTPClient = agentNPMRegistryProbeHTTPClient(nil)
service.ManagedRuntime = staticManagedRuntimeResolver{runtime: managedruntime.ResolvedRuntime{
Root: runtimeRoot,
Node: managedNode,
NPM: managedNPM,
BinDirs: []string{filepath.Dir(managedNode)},
EnvOverrides: []string{"PATH=" + filepath.Dir(managedNode)},
}}
var command InstallCommandInput
service.InstallCommand = func(_ context.Context, input InstallCommandInput) (InstallCommandResult, error) {
command = input
return InstallCommandResult{ExitCode: 0, Stdout: "installed"}, nil
}
if _, err := service.runCodexCLILatestInstaller(context.Background(), "codex", InstallerSpec{
Kind: InstallerKindCodexCLILatest,
CodexCLI: codexCLIInstallerSpec().CodexCLI,
}, legacyShim); err != nil {
t.Fatalf("runCodexCLILatestInstaller() error = %v", err)
}
wantPrefix := legacyPrefix
if len(command.Args) < 2 || command.Args[0] == "" {
t.Fatalf("Command args = %#v, want npm install arguments", command.Args)
}
gotPrefix := ""
for index, arg := range command.Args {
if arg == "--prefix" && index+1 < len(command.Args) {
gotPrefix = command.Args[index+1]
break
}
}
if gotPrefix != wantPrefix {
t.Fatalf("Command args = %#v, want legacy Windows prefix repaired in place at %s", command.Args, wantPrefix)
}
}
func managedNPMInstallPrefixForTest(home string) string {
if runtime.GOOS == "windows" {
return filepath.Join(home, ".local", "bin")
}
return filepath.Join(home, ".local")
}
func (r staticManagedRuntimeResolver) Resolve(context.Context) (managedruntime.ResolvedRuntime, error) {
return r.runtime, nil
}
func (r staticManagedRuntimeResolver) ResolveProfile(context.Context, string) (managedruntime.ResolvedRuntime, error) {
return r.runtime, nil
}