You've already forked agentic-coding-workflow
ACP / MCP
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
// agent_home.go 实现 agent CLI 受管 home 目录的物化与重定向 env 注入。
|
||||
//
|
||||
// 每个 AgentKind 一个受管 home:<agent_homes_dir>/<agent_kind_id>/。spawn 前把
|
||||
// DB 中的配置文件解密落盘(全量覆写受管文件,不清空目录 —— agent 运行期写入的
|
||||
// 缓存 / 凭据得以保留),再按 client_type 注入配置目录重定向变量。受管 home 位于
|
||||
// DataDir 下,容器场景随持久卷存活。
|
||||
package acp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// materializeAgentHome 创建受管 home 并物化该 AgentKind 的全部配置文件。
|
||||
// 返回 home 绝对路径。配置文件为空时仍创建目录(agent 需要可写 home)。
|
||||
func (s *Supervisor) materializeAgentHome(ctx context.Context, kind *AgentKind) (string, error) {
|
||||
home, err := filepath.Abs(filepath.Join(s.cfg.AgentHomesDir, kind.ID.String()))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("resolve agent home: %w", err)
|
||||
}
|
||||
if err := os.MkdirAll(home, 0o700); err != nil {
|
||||
return "", fmt.Errorf("create agent home: %w", err)
|
||||
}
|
||||
// client 专属配置目录预创建:CODEX_HOME 指向的目录必须已存在(codex 硬性要求),
|
||||
// 其余预创建无害且省去 agent 首次启动的目录初始化分支。
|
||||
if sub := clientConfigSubdir(kind.ClientType); sub != "" {
|
||||
if err := os.MkdirAll(filepath.Join(home, sub), 0o700); err != nil {
|
||||
return "", fmt.Errorf("create client config dir: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
files, err := s.repo.ListConfigFiles(ctx, kind.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sep := string(filepath.Separator)
|
||||
for _, f := range files {
|
||||
plain, derr := s.crypto.Decrypt(f.EncryptedContent)
|
||||
if derr != nil {
|
||||
return "", fmt.Errorf("decrypt config file %s: %w", f.RelPath, derr)
|
||||
}
|
||||
dst := filepath.Clean(filepath.Join(home, filepath.FromSlash(f.RelPath)))
|
||||
// rel_path 写入时已校验;此处兜底防 DB 被直接篡改后逃逸受管目录。
|
||||
if !strings.HasPrefix(dst, home+sep) {
|
||||
return "", fmt.Errorf("config file %s escapes agent home", f.RelPath)
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(dst), 0o700); err != nil {
|
||||
return "", fmt.Errorf("create config file dir %s: %w", f.RelPath, err)
|
||||
}
|
||||
if err := os.WriteFile(dst, plain, 0o600); err != nil {
|
||||
return "", fmt.Errorf("write config file %s: %w", f.RelPath, err)
|
||||
}
|
||||
}
|
||||
return home, nil
|
||||
}
|
||||
|
||||
// agentHomeEnv 返回受管 home 对应的重定向 env 默认值。优先级最低:AgentKind env
|
||||
// 与系统 extraEnv 可覆盖同名 key。
|
||||
func agentHomeEnv(home string, ct ClientType) map[string]string {
|
||||
env := map[string]string{"HOME": home}
|
||||
if runtime.GOOS == "windows" {
|
||||
// Windows 上 Node/Rust 解析 home 走 USERPROFILE(开发环境)。
|
||||
env["USERPROFILE"] = home
|
||||
}
|
||||
switch ct {
|
||||
case ClientClaudeCode:
|
||||
env["CLAUDE_CONFIG_DIR"] = filepath.Join(home, ".claude")
|
||||
case ClientCodex:
|
||||
env["CODEX_HOME"] = filepath.Join(home, ".codex")
|
||||
case ClientGemini:
|
||||
// GEMINI_CLI_HOME 替换的是 home 本身,.gemini 段由 CLI 拼接。
|
||||
env["GEMINI_CLI_HOME"] = home
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
// clientConfigSubdir 返回 client 专属配置子目录(相对受管 home),无则空串。
|
||||
func clientConfigSubdir(ct ClientType) string {
|
||||
switch ct {
|
||||
case ClientClaudeCode:
|
||||
return ".claude"
|
||||
case ClientCodex:
|
||||
return ".codex"
|
||||
case ClientGemini:
|
||||
return ".gemini"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
Reference in New Issue
Block a user