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

212 lines
7.1 KiB
Go

package runtimeprep
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"sync"
)
const codexModelsCacheAuthorityFile = ".models_cache.authority.sha256"
var codexModelsCacheAuthorityMu sync.Mutex
// exposeUserCodexModelsCache gives every run-scoped CODEX_HOME one shared,
// process-default model cache. Codex refreshes models_cache.json before it emits
// thread.started; keeping that writable cache behind a symlink lets a refresh
// from one AgentGUI session remove the cold catalog request from later sessions.
//
// Cache reuse is fenced by the current global config, auth and referenced model
// catalog. Codex's own cache file does not record provider identity, so carrying
// it across a global input change can expose another provider's model list. The
// link is installed even before the source exists; Codex treats that as a cache
// miss and writes the new authority-scoped cache through the link.
func exposeUserCodexModelsCache(codexHome, userCodexHome string) error {
codexModelsCacheAuthorityMu.Lock()
defer codexModelsCacheAuthorityMu.Unlock()
target := filepath.Join(codexHome, "models_cache.json")
if err := reconcileUserCodexModelsCacheAuthority(target, userCodexHome); err != nil {
return err
}
if _, err := os.Lstat(target); err == nil {
return nil
} else if !os.IsNotExist(err) {
return fmt.Errorf("inspect codex models cache: %w", err)
}
if err := os.MkdirAll(userCodexHome, 0o700); err != nil {
return fmt.Errorf("create shared codex home for models cache: %w", err)
}
source := filepath.Join(userCodexHome, "models_cache.json")
linkErr := exposeCodexFile(source, target, 0o600)
if linkErr == nil {
return nil
}
info, statErr := os.Stat(source)
if os.IsNotExist(statErr) {
if err := initializeCodexModelsCacheForWindows(source, target); err != nil {
return fmt.Errorf("initialize Windows codex models cache: %w", err)
}
// Platforms that cannot create the link have no cache to copy yet. The
// run remains usable and Codex will create its ordinary local cache.
return nil
}
if statErr != nil {
return fmt.Errorf("inspect shared codex models cache after link failure: %w", statErr)
}
if info.IsDir() {
return fmt.Errorf("shared codex models cache is a directory: %s", source)
}
if copyErr := copyFile(source, target, 0o600); copyErr != nil {
return fmt.Errorf("expose codex models cache: link failed: %v; copy failed: %w", linkErr, copyErr)
}
return nil
}
func reconcileUserCodexModelsCacheAuthority(target, userCodexHome string) error {
authority, err := userCodexModelsCacheAuthority(userCodexHome)
if err != nil {
return err
}
fencePath := filepath.Join(userCodexHome, codexModelsCacheAuthorityFile)
if existing, readErr := os.ReadFile(fencePath); readErr == nil && strings.TrimSpace(string(existing)) == authority {
return nil
} else if readErr != nil && !os.IsNotExist(readErr) {
return fmt.Errorf("read codex models cache authority: %w", readErr)
}
sharedCache := filepath.Join(userCodexHome, "models_cache.json")
if err := removeCodexModelsCacheFile(sharedCache); err != nil {
return err
}
if info, statErr := os.Lstat(target); statErr == nil {
if info.Mode()&os.ModeSymlink == 0 {
if err := removeCodexModelsCacheFile(target); err != nil {
return err
}
}
} else if !os.IsNotExist(statErr) {
return fmt.Errorf("inspect run-scoped codex models cache: %w", statErr)
}
return writeCodexModelsCacheAuthority(fencePath, authority)
}
func userCodexModelsCacheAuthority(userCodexHome string) (string, error) {
digest := sha256.New()
configPath := filepath.Join(userCodexHome, "config.toml")
config, err := hashCodexModelsCacheAuthorityFile(digest, "config", configPath)
if err != nil {
return "", err
}
if _, err := hashCodexModelsCacheAuthorityFile(digest, "auth", filepath.Join(userCodexHome, "auth.json")); err != nil {
return "", err
}
if catalogPath := codexConfigRelativeFilePath(config, userCodexHome, "model_catalog_json"); catalogPath != "" {
if _, err := hashCodexModelsCacheAuthorityFile(digest, "model_catalog", catalogPath); err != nil {
return "", err
}
}
if instructionsPath := codexConfigRelativeFilePath(config, userCodexHome, "model_instructions_file"); instructionsPath != "" {
if _, err := hashCodexModelsCacheAuthorityFile(digest, "model_instructions", instructionsPath); err != nil {
return "", err
}
}
return hex.EncodeToString(digest.Sum(nil)), nil
}
func hashCodexModelsCacheAuthorityFile(digest io.Writer, label, path string) ([]byte, error) {
content, err := os.ReadFile(path)
switch {
case err == nil:
_, _ = io.WriteString(digest, label+"\x00present\x00")
_, _ = digest.Write(content)
_, _ = io.WriteString(digest, "\x00")
return content, nil
case os.IsNotExist(err):
_, _ = io.WriteString(digest, label+"\x00missing\x00")
return nil, nil
default:
return nil, fmt.Errorf("read codex models cache authority %s: %w", label, err)
}
}
func codexConfigRelativeFilePath(config []byte, userCodexHome string, key string) string {
lines := strings.Split(strings.ReplaceAll(string(config), "\r\n", "\n"), "\n")
for _, line := range lines {
trimmed := strings.TrimSpace(line)
if trimmed == "" || strings.HasPrefix(trimmed, "#") {
continue
}
if strings.HasPrefix(trimmed, "[") {
return ""
}
value, ok := codexConfigStringAssignmentValue(trimmed, key)
if !ok {
continue
}
value = strings.TrimSpace(value)
if value == "" {
return ""
}
if filepath.IsAbs(value) {
return filepath.Clean(value)
}
cleanRel := filepath.Clean(value)
if cleanRel == "." || cleanRel == ".." || strings.HasPrefix(cleanRel, ".."+string(filepath.Separator)) {
return ""
}
return filepath.Join(userCodexHome, cleanRel)
}
return ""
}
func removeCodexModelsCacheFile(path string) error {
info, err := os.Lstat(path)
if os.IsNotExist(err) {
return nil
}
if err != nil {
return fmt.Errorf("inspect codex models cache %s: %w", path, err)
}
if info.IsDir() {
return fmt.Errorf("codex models cache is a directory: %s", path)
}
if err := os.Remove(path); err != nil {
return fmt.Errorf("remove stale codex models cache %s: %w", path, err)
}
return nil
}
func writeCodexModelsCacheAuthority(path, authority string) error {
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
return fmt.Errorf("create codex models cache authority directory: %w", err)
}
temporary, err := os.CreateTemp(filepath.Dir(path), ".models-cache-authority-*.tmp")
if err != nil {
return fmt.Errorf("create codex models cache authority: %w", err)
}
temporaryPath := temporary.Name()
defer func() {
_ = temporary.Close()
_ = os.Remove(temporaryPath)
}()
if err := temporary.Chmod(0o600); err != nil {
return fmt.Errorf("chmod codex models cache authority: %w", err)
}
if _, err := temporary.WriteString(authority + "\n"); err != nil {
return fmt.Errorf("write codex models cache authority: %w", err)
}
if err := temporary.Close(); err != nil {
return fmt.Errorf("close codex models cache authority: %w", err)
}
if err := os.Rename(temporaryPath, path); err != nil {
return fmt.Errorf("commit codex models cache authority: %w", err)
}
return nil
}