Files
tutti/packages/agent/runtimeprep/codex_user_instructions.go
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

111 lines
3.3 KiB
Go

package runtimeprep
import (
"fmt"
"os"
"path/filepath"
)
// exposeUserCodexAgentsFile seeds the run-scoped instruction file with the
// user's global Codex instructions. The session-owned file must be a copy:
// WriteManagedBlock adds Tutti's managed policy to it and must never mutate the
// user's real ~/.codex/AGENTS.md.
func exposeUserCodexAgentsFile(codexHome string, userCodexHome string) error {
source := filepath.Join(userCodexHome, "AGENTS.md")
sourceContent, sourceExists, err := readRegularCodexAgentsFile(source)
if err != nil {
return err
}
target := filepath.Join(codexHome, "AGENTS.md")
targetInfo, err := os.Lstat(target)
if os.IsNotExist(err) {
if !sourceExists {
return nil
}
if err := writeRegularCodexAgentsFile(target, sourceContent); err != nil {
return fmt.Errorf("copy user codex AGENTS.md: %w", err)
}
return nil
}
if err != nil {
return fmt.Errorf("inspect session codex AGENTS.md: %w", err)
}
// Always replace a pre-existing link with a private regular file. This
// prevents WriteManagedBlock from following a symlink or mutating a hardlink
// to the user's real ~/.codex/AGENTS.md on the next line of Prepare.
if targetInfo.Mode()&os.ModeSymlink != 0 {
if sourceExists {
if err := writeRegularCodexAgentsFile(target, sourceContent); err != nil {
return fmt.Errorf("replace session codex AGENTS.md link: %w", err)
}
return nil
}
if err := os.Remove(target); err != nil {
return fmt.Errorf("remove session codex AGENTS.md link: %w", err)
}
return nil
}
if !targetInfo.Mode().IsRegular() {
return fmt.Errorf("session codex AGENTS.md is not a regular file")
}
// Re-materialize an existing regular file too. A hardlink has regular-file
// mode, so an atomic replacement is what guarantees a new inode without
// discarding any session-local content already present in the file.
existing, err := os.ReadFile(target)
if err != nil {
return fmt.Errorf("read session codex AGENTS.md: %w", err)
}
if err := writeRegularCodexAgentsFile(target, existing); err != nil {
return fmt.Errorf("isolate session codex AGENTS.md: %w", err)
}
return nil
}
func readRegularCodexAgentsFile(path string) ([]byte, bool, error) {
info, err := os.Stat(path)
if err != nil {
if os.IsNotExist(err) {
return nil, false, nil
}
return nil, false, fmt.Errorf("inspect user codex AGENTS.md: %w", err)
}
if !info.Mode().IsRegular() {
// AGENTS.md is optional. Ignore directories, FIFOs, and other special
// files instead of blocking runtime preparation while reading them.
return nil, false, nil
}
content, err := os.ReadFile(path)
if err != nil {
return nil, false, fmt.Errorf("read user codex AGENTS.md: %w", err)
}
return content, true, nil
}
func writeRegularCodexAgentsFile(path string, content []byte) error {
temporary, err := os.CreateTemp(filepath.Dir(path), ".codex-agents-*.tmp")
if err != nil {
return err
}
temporaryPath := temporary.Name()
defer func() {
_ = temporary.Close()
_ = os.Remove(temporaryPath)
}()
if err := temporary.Chmod(0o600); err != nil {
return err
}
if _, err := temporary.Write(content); err != nil {
return err
}
if err := temporary.Sync(); err != nil {
return err
}
if err := temporary.Close(); err != nil {
return err
}
return os.Rename(temporaryPath, path)
}