feat: add workspace-isolated toolhost runtime and capability-gap skill loop
This commit is contained in:
109
internal/runtimews/bootstrap.go
Normal file
109
internal/runtimews/bootstrap.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package runtimews
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const envWorkspaceDir = "AGENT_WORKSPACE_DIR"
|
||||
|
||||
func PrepareFromEnv() (string, error) {
|
||||
workspaceDir := strings.TrimSpace(os.Getenv(envWorkspaceDir))
|
||||
if workspaceDir == "" {
|
||||
workspaceDir = filepath.Join(".", "workspace", "agent_runtime")
|
||||
}
|
||||
absWorkspace, err := filepath.Abs(workspaceDir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.MkdirAll(absWorkspace, 0o755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := seedRuntimeWorkspace(absWorkspace); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := os.Setenv(envWorkspaceDir, absWorkspace); err != nil {
|
||||
return "", err
|
||||
}
|
||||
_ = os.Setenv("CONFIG_ENV_FILE", filepath.Join(absWorkspace, "configs", "env"))
|
||||
return absWorkspace, nil
|
||||
}
|
||||
|
||||
func seedRuntimeWorkspace(workspaceRoot string) error {
|
||||
seedDirs := []string{"configs", "data", "bot_context", "skills"}
|
||||
for _, name := range seedDirs {
|
||||
src := filepath.Join(".", name)
|
||||
dst := filepath.Join(workspaceRoot, name)
|
||||
if err := copyDirIfMissing(src, dst); err != nil {
|
||||
return fmt.Errorf("seed %s failed: %w", name, err)
|
||||
}
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Join(workspaceRoot, "workspace"), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyDirIfMissing(src, dst string) error {
|
||||
info, err := os.Stat(src)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if err := os.MkdirAll(dst, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return filepath.WalkDir(src, func(path string, d os.DirEntry, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
return walkErr
|
||||
}
|
||||
rel, err := filepath.Rel(src, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
target := filepath.Join(dst, rel)
|
||||
if d.IsDir() {
|
||||
return os.MkdirAll(target, 0o755)
|
||||
}
|
||||
if _, err := os.Stat(target); err == nil {
|
||||
return nil
|
||||
}
|
||||
if err := copyFile(path, target); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func copyFile(src, dst string) error {
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
if _, err := io.Copy(out, in); err != nil {
|
||||
return err
|
||||
}
|
||||
return out.Sync()
|
||||
}
|
||||
Reference in New Issue
Block a user