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

478 lines
16 KiB
Go

package agentstatus
import (
"archive/tar"
"bytes"
"compress/gzip"
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"runtime"
"strings"
"sync/atomic"
"testing"
"github.com/klauspost/compress/zstd"
"github.com/tutti-os/tutti/services/tuttid/service/usercommand"
)
const testClaudeVersion = "2.1.201"
const testClaudeNPMVersion = "0.3.201"
func TestActivateClaudeCodeBinaryPublishesStableUserCommand(t *testing.T) {
runtimeRoot := filepath.Join(t.TempDir(), "agent-runtimes", "claude-code")
userBinDir := filepath.Join(t.TempDir(), ".local", "bin")
executable := filepath.Join(runtimeRoot, "versions", testClaudeVersion, testClaudeBinaryName())
if err := os.MkdirAll(filepath.Dir(executable), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(executable, []byte("test claude binary"), 0o700); err != nil {
t.Fatal(err)
}
adapter := &recordingUserPathAdapter{}
service := Service{
ClaudeCodeRuntimeDir: runtimeRoot,
UserCommandBinDir: userBinDir,
UserPathAdapter: adapter,
}
descriptor := claudeSDKRuntimeDescriptor{ClaudeVersion: testClaudeVersion}
if err := service.activateClaudeCodeBinary(context.Background(), t.TempDir(), descriptor, executable); err != nil {
t.Fatal(err)
}
if adapter.directory != userBinDir {
t.Fatalf("published PATH directory = %q, want %q", adapter.directory, userBinDir)
}
entry, err := usercommand.NewEntry(runtimeRoot, userBinDir, "claude", executable)
if err != nil {
t.Fatal(err)
}
if err := entry.Verify(); err != nil {
t.Fatal(err)
}
}
func testClaudeBinaryName() string {
if runtime.GOOS == "windows" {
return "claude.exe"
}
return "claude"
}
type claudeBinaryFixture struct {
service Service
stateRoot string
runtimeRoot string
payload []byte
}
func newClaudeBinaryFixture(t *testing.T, extraEnv ...string) claudeBinaryFixture {
t.Helper()
payload := []byte("fake-claude-binary payload for " + t.Name())
sum := sha256.Sum256(payload)
bundleDir := filepath.Join(t.TempDir(), "claude-sdk-sidecar")
entry := filepath.Join(bundleDir, "src", "main.ts")
if err := os.MkdirAll(filepath.Dir(entry), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(entry, []byte("// sidecar entry\n"), 0o644); err != nil {
t.Fatal(err)
}
sdkDir := filepath.Join(bundleDir, "node_modules", claudeSDKPackageScopeDir, claudeSDKPackageName)
if err := os.MkdirAll(sdkDir, 0o755); err != nil {
t.Fatal(err)
}
packageJSON := fmt.Sprintf(`{"name":"@anthropic-ai/claude-agent-sdk","version":%q}`, testClaudeNPMVersion)
if err := os.WriteFile(filepath.Join(sdkDir, "package.json"), []byte(packageJSON), 0o644); err != nil {
t.Fatal(err)
}
manifest := claudeSDKManifest{
Version: testClaudeVersion,
Platforms: map[string]claudeSDKManifestPlatform{
claudeCodePlatformKey(runtime.GOOS, runtime.GOARCH): {
Binary: testClaudeBinaryName(),
Checksum: hex.EncodeToString(sum[:]),
Size: int64(len(payload)),
},
},
}
manifestContent, err := json.Marshal(manifest)
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(sdkDir, "manifest.json"), manifestContent, 0o644); err != nil {
t.Fatal(err)
}
stateDir := t.TempDir()
runtimeRoot := filepath.Join(t.TempDir(), ".local", "share", "tutti", "agent-runtimes", "claude-code")
env := append([]string{
claudeSDKSidecarEntryPathEnv + "=" + entry,
}, extraEnv...)
service := Service{
Environ: func() []string { return env },
HomeDir: func() (string, error) { return stateDir, nil },
LookPath: func(string) (string, error) { return "", os.ErrNotExist },
ClaudeCodeStateDir: stateDir,
ClaudeCodeRuntimeDir: runtimeRoot,
}
return claudeBinaryFixture{
service: service,
stateRoot: filepath.Join(stateDir, filepath.FromSlash(claudeCodeStateRelDir)),
runtimeRoot: runtimeRoot,
payload: payload,
}
}
func TestClaudeCodeRuntimeRootRequiresInjectedDirectory(t *testing.T) {
if _, err := (Service{}).claudeCodeRuntimeRoot(); err == nil {
t.Fatal("claudeCodeRuntimeRoot() error = nil")
}
}
func TestManagedClaudeCodeInstallerUsesProvisionedRuntime(t *testing.T) {
fixture := newClaudeBinaryFixture(t)
installedPath := fixture.installedBinaryPath()
if err := os.MkdirAll(filepath.Dir(installedPath), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(installedPath, fixture.payload, 0o755); err != nil {
t.Fatal(err)
}
if err := writeClaudeCodePointer(fixture.stateRoot, claudeSDKRuntimeDescriptor{
ClaudeVersion: testClaudeVersion,
}, installedPath); err != nil {
t.Fatal(err)
}
result, err := fixture.service.runManagedClaudeCodeInstaller(context.Background())
if err != nil {
t.Fatalf("runManagedClaudeCodeInstaller() error = %v", err)
}
if result.ExitCode != 0 || !strings.Contains(result.Stdout, installedPath) {
t.Fatalf("result = %#v, want successful managed runtime install", result)
}
}
func TestResolveProviderRuntimeUsesManagedClaudeCodePointer(t *testing.T) {
fixture := newClaudeBinaryFixture(t)
installedPath := fixture.installedBinaryPath()
if err := os.MkdirAll(filepath.Dir(installedPath), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(installedPath, fixture.payload, 0o755); err != nil {
t.Fatal(err)
}
if err := writeClaudeCodePointer(fixture.stateRoot, claudeSDKRuntimeDescriptor{
ClaudeVersion: testClaudeVersion,
}, installedPath); err != nil {
t.Fatal(err)
}
specs, err := fixture.service.selectProviderSpecs(context.Background(), []string{"claude-code"}, false)
if err != nil {
t.Fatalf("selectProviderSpecs() error = %v", err)
}
runtimeResolution := fixture.service.resolveProviderRuntime(context.Background(), specs[0])
if runtimeResolution.CLIPath != installedPath {
t.Fatalf("CLIPath = %q, want managed Claude binary %q", runtimeResolution.CLIPath, installedPath)
}
}
func zstdCompress(t *testing.T, payload []byte) []byte {
t.Helper()
var buffer bytes.Buffer
writer, err := zstd.NewWriter(&buffer)
if err != nil {
t.Fatal(err)
}
if _, err := writer.Write(payload); err != nil {
t.Fatal(err)
}
if err := writer.Close(); err != nil {
t.Fatal(err)
}
return buffer.Bytes()
}
func npmTarballWithBinary(t *testing.T, binaryName string, payload []byte) []byte {
t.Helper()
var buffer bytes.Buffer
gzipWriter := gzip.NewWriter(&buffer)
tarWriter := tar.NewWriter(gzipWriter)
if err := tarWriter.WriteHeader(&tar.Header{
Name: "package/package.json",
Mode: 0o644,
Size: int64(len("{}")),
}); err != nil {
t.Fatal(err)
}
if _, err := tarWriter.Write([]byte("{}")); err != nil {
t.Fatal(err)
}
if err := tarWriter.WriteHeader(&tar.Header{
Name: "package/" + binaryName,
Mode: 0o755,
Size: int64(len(payload)),
}); err != nil {
t.Fatal(err)
}
if _, err := tarWriter.Write(payload); err != nil {
t.Fatal(err)
}
if err := tarWriter.Close(); err != nil {
t.Fatal(err)
}
if err := gzipWriter.Close(); err != nil {
t.Fatal(err)
}
return buffer.Bytes()
}
func (f claudeBinaryFixture) installedBinaryPath() string {
return filepath.Join(f.runtimeRoot, "versions", testClaudeVersion, testClaudeBinaryName())
}
func TestEnsureClaudeCodeBinaryDownloadsFromCDN(t *testing.T) {
var requests atomic.Int64
var fixture claudeBinaryFixture
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requests.Add(1)
want := "/claude-code/" + testClaudeVersion + "/claude-" + claudeCodePlatformKey(runtime.GOOS, runtime.GOARCH) + ".zst"
if r.URL.Path != want {
t.Errorf("unexpected CDN path %q, want %q", r.URL.Path, want)
http.NotFound(w, r)
return
}
_, _ = w.Write(zstdCompress(t, fixture.payload))
}))
defer server.Close()
fixture = newClaudeBinaryFixture(t, claudeCodeBinaryBaseURLEnv+"="+server.URL+"/claude-code")
status, err := fixture.service.EnsureClaudeCodeBinary(context.Background())
if err != nil {
t.Fatalf("EnsureClaudeCodeBinary: %v", err)
}
if status.Source != "cdn" {
t.Fatalf("source = %q, want cdn", status.Source)
}
if status.Version != testClaudeVersion {
t.Fatalf("version = %q, want %q", status.Version, testClaudeVersion)
}
installed, err := os.ReadFile(fixture.installedBinaryPath())
if err != nil {
t.Fatalf("read installed binary: %v", err)
}
if !bytes.Equal(installed, fixture.payload) {
t.Fatal("installed binary content mismatch")
}
info, err := os.Stat(fixture.installedBinaryPath())
if err != nil {
t.Fatal(err)
}
if runtime.GOOS != "windows" && info.Mode().Perm()&0o111 == 0 {
t.Fatal("installed binary is not executable")
}
pointer, err := readClaudeCodePointer(fixture.stateRoot)
if err != nil {
t.Fatalf("read pointer: %v", err)
}
if pointer.Version != testClaudeVersion || pointer.Executable != fixture.installedBinaryPath() {
t.Fatalf("pointer = %+v", pointer)
}
// A second ensure resolves from disk without re-downloading.
downloaded := requests.Load()
status, err = fixture.service.EnsureClaudeCodeBinary(context.Background())
if err != nil {
t.Fatalf("EnsureClaudeCodeBinary (second): %v", err)
}
if status.Source != "installed" {
t.Fatalf("second source = %q, want installed", status.Source)
}
if requests.Load() != downloaded {
t.Fatal("second ensure re-downloaded the binary")
}
}
func TestEnsureClaudeCodeBinaryFallsBackToNPM(t *testing.T) {
var fixture claudeBinaryFixture
tarballPath := "/@anthropic-ai/claude-agent-sdk-" + claudeCodePlatformKey(runtime.GOOS, runtime.GOARCH) +
"/-/claude-agent-sdk-" + claudeCodePlatformKey(runtime.GOOS, runtime.GOARCH) + "-" + testClaudeNPMVersion + ".tgz"
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == tarballPath {
_, _ = w.Write(npmTarballWithBinary(t, testClaudeBinaryName(), fixture.payload))
return
}
http.NotFound(w, r)
}))
defer server.Close()
fixture = newClaudeBinaryFixture(t,
claudeCodeBinaryBaseURLEnv+"="+server.URL+"/missing-cdn",
agentNPMRegistryEnv+"="+server.URL,
)
status, err := fixture.service.EnsureClaudeCodeBinary(context.Background())
if err != nil {
t.Fatalf("EnsureClaudeCodeBinary: %v", err)
}
if status.Source != "npm" {
t.Fatalf("source = %q, want npm", status.Source)
}
installed, err := os.ReadFile(fixture.installedBinaryPath())
if err != nil {
t.Fatalf("read installed binary: %v", err)
}
if !bytes.Equal(installed, fixture.payload) {
t.Fatal("installed binary content mismatch")
}
}
func TestEnsureClaudeCodeBinaryRejectsChecksumMismatch(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
payload := []byte("tampered payload of matching length!!!!!!")
_, _ = w.Write(zstdCompress(t, payload))
}))
defer server.Close()
fixture := newClaudeBinaryFixture(t,
claudeCodeBinaryBaseURLEnv+"="+server.URL,
agentNPMRegistryEnv+"="+server.URL+"/missing-registry",
)
// Match the manifest size so only the checksum gate can reject it.
tampered := make([]byte, len(fixture.payload))
copy(tampered, "tampered payload")
server.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write(zstdCompress(t, tampered))
})
if _, err := fixture.service.EnsureClaudeCodeBinary(context.Background()); err == nil {
t.Fatal("expected checksum mismatch error")
}
if _, err := os.Stat(fixture.installedBinaryPath()); !os.IsNotExist(err) {
t.Fatalf("tampered binary must not stay installed: %v", err)
}
if _, err := readClaudeCodePointer(fixture.stateRoot); !os.IsNotExist(err) {
t.Fatalf("pointer must not be written on failure: %v", err)
}
}
func TestEnsureClaudeCodeBinarySkipsWhenNativePackagePresent(t *testing.T) {
fixture := newClaudeBinaryFixture(t)
entry := ""
for _, kv := range fixture.service.Environ() {
if len(kv) > len(claudeSDKSidecarEntryPathEnv)+1 && kv[:len(claudeSDKSidecarEntryPathEnv)] == claudeSDKSidecarEntryPathEnv {
entry = kv[len(claudeSDKSidecarEntryPathEnv)+1:]
}
}
bundleDir := filepath.Dir(filepath.Dir(entry))
nativeDir := filepath.Join(bundleDir, "node_modules", claudeSDKPackageScopeDir,
claudeSDKPackageName+"-"+claudeCodePlatformKey(runtime.GOOS, runtime.GOARCH))
if err := os.MkdirAll(nativeDir, 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(nativeDir, testClaudeBinaryName()), fixture.payload, 0o755); err != nil {
t.Fatal(err)
}
status, err := fixture.service.EnsureClaudeCodeBinary(context.Background())
if err != nil {
t.Fatalf("EnsureClaudeCodeBinary: %v", err)
}
if status.Source != "native_package_present" {
t.Fatalf("source = %q, want native_package_present", status.Source)
}
if status.Path != "" {
t.Fatalf("path = %q, want empty", status.Path)
}
}
func TestClaudeCodePlatformKey(t *testing.T) {
cases := []struct {
goos, goarch, want string
}{
{"darwin", "arm64", "darwin-arm64"},
{"darwin", "amd64", "darwin-x64"},
{"linux", "amd64", "linux-x64"},
{"linux", "arm64", "linux-arm64"},
{"windows", "amd64", "win32-x64"},
{"windows", "arm64", "win32-arm64"},
{"plan9", "amd64", ""},
{"linux", "riscv64", ""},
}
for _, tc := range cases {
if got := claudeCodePlatformKey(tc.goos, tc.goarch); got != tc.want {
t.Errorf("claudeCodePlatformKey(%s,%s) = %q, want %q", tc.goos, tc.goarch, got, tc.want)
}
}
}
func TestClaudeBinaryLockIsRecognized(t *testing.T) {
if !requiresInstallCommandLock(claudeCodeBinaryLockCommand) {
t.Fatal("claude binary provisioning must acquire an install lock")
}
lockPath := installCommandLockPath(claudeCodeBinaryLockCommand)
if filepath.Base(lockPath) != "claude-code-runtime-binary.lock" {
t.Fatalf("lock path = %q, want dedicated claude lock file", lockPath)
}
if lockPath == installCommandLockPath("npm install -g something") {
t.Fatal("claude lock must not share the npm global install lock")
}
}
func TestPromoteClaudeBinaryRejectsMismatchedStaging(t *testing.T) {
dir := t.TempDir()
staging := filepath.Join(dir, ".claude.staging")
final := filepath.Join(dir, "claude")
if err := os.WriteFile(staging, []byte("not the pinned bytes"), 0o644); err != nil {
t.Fatal(err)
}
descriptor := claudeSDKRuntimeDescriptor{
PlatformKey: "darwin-arm64",
SHA256: "0000000000000000000000000000000000000000000000000000000000000000",
}
if err := promoteClaudeBinary(staging, final, descriptor); err == nil {
t.Fatal("expected sha256 mismatch error")
}
if _, err := os.Stat(final); !os.IsNotExist(err) {
t.Fatalf("final path must never receive unverified bytes: %v", err)
}
if _, err := os.Stat(staging); !os.IsNotExist(err) {
t.Fatalf("rejected staging file must be removed: %v", err)
}
}
func TestExtractClaudeBinaryFromTarballBoundsSize(t *testing.T) {
dir := t.TempDir()
oversized := bytes.Repeat([]byte("A"), 4096)
archive := filepath.Join(dir, "package.tgz")
if err := os.WriteFile(archive, npmTarballWithBinary(t, "claude", oversized), 0o644); err != nil {
t.Fatal(err)
}
destination := filepath.Join(dir, ".claude.staging")
err := extractClaudeBinaryFromTarball(archive, "claude", destination, 128)
if err == nil {
t.Fatal("expected size mismatch error for oversized member")
}
if _, statErr := os.Stat(destination); !os.IsNotExist(statErr) {
t.Fatalf("oversized extraction must not leave a file: %v", statErr)
}
}
func TestNPMPackageTarballURL(t *testing.T) {
got := npmPackageTarballURL("https://registry.npmjs.org/", "@anthropic-ai/claude-agent-sdk-darwin-arm64", "0.3.201")
want := "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.201.tgz"
if got != want {
t.Fatalf("npmPackageTarballURL = %q, want %q", got, want)
}
if npmPackageTarballURL("", "pkg", "1.0.0") != "" {
t.Fatal("empty registry must produce empty URL")
}
}