Files
openclaw/scripts/docs-i18n/pi_command.go
2026-03-16 02:11:18 +00:00

121 lines
3.1 KiB
Go

package main
import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
"time"
)
const (
envDocsPiExecutable = "OPENCLAW_DOCS_I18N_PI_EXECUTABLE"
envDocsPiArgs = "OPENCLAW_DOCS_I18N_PI_ARGS"
envDocsPiPackageVersion = "OPENCLAW_DOCS_I18N_PI_PACKAGE_VERSION"
defaultPiPackageVersion = "0.58.3"
)
type docsPiCommand struct {
Executable string
Args []string
}
var (
materializedPiRuntimeMu sync.Mutex
materializedPiRuntimeCommand docsPiCommand
materializedPiRuntimeErr error
)
func resolveDocsPiCommand(ctx context.Context) (docsPiCommand, error) {
if executable := strings.TrimSpace(os.Getenv(envDocsPiExecutable)); executable != "" {
return docsPiCommand{
Executable: executable,
Args: strings.Fields(os.Getenv(envDocsPiArgs)),
}, nil
}
piPath, err := exec.LookPath("pi")
if err == nil && !shouldMaterializePiRuntime(piPath) {
return docsPiCommand{Executable: piPath}, nil
}
return ensureMaterializedPiRuntime(ctx)
}
func shouldMaterializePiRuntime(piPath string) bool {
realPath, err := filepath.EvalSymlinks(piPath)
if err != nil {
realPath = piPath
}
return strings.Contains(filepath.ToSlash(realPath), "/Projects/pi-mono/")
}
func ensureMaterializedPiRuntime(ctx context.Context) (docsPiCommand, error) {
materializedPiRuntimeMu.Lock()
defer materializedPiRuntimeMu.Unlock()
if materializedPiRuntimeErr == nil && materializedPiRuntimeCommand.Executable != "" {
return materializedPiRuntimeCommand, nil
}
runtimeDir, err := getMaterializedPiRuntimeDir()
if err != nil {
materializedPiRuntimeErr = err
return docsPiCommand{}, err
}
cliPath := filepath.Join(runtimeDir, "node_modules", "@mariozechner", "pi-coding-agent", "dist", "cli.js")
if _, err := os.Stat(cliPath); errors.Is(err, os.ErrNotExist) {
installCtx, cancel := context.WithTimeout(ctx, 2*time.Minute)
defer cancel()
if err := os.MkdirAll(runtimeDir, 0o755); err != nil {
materializedPiRuntimeErr = err
return docsPiCommand{}, err
}
packageVersion := getMaterializedPiPackageVersion()
install := exec.CommandContext(
installCtx,
"npm",
"install",
"--silent",
"--no-audit",
"--no-fund",
fmt.Sprintf("@mariozechner/pi-coding-agent@%s", packageVersion),
)
install.Dir = runtimeDir
install.Env = os.Environ()
output, err := install.CombinedOutput()
if err != nil {
materializedPiRuntimeErr = fmt.Errorf("materialize pi runtime: %w (%s)", err, strings.TrimSpace(string(output)))
return docsPiCommand{}, materializedPiRuntimeErr
}
}
materializedPiRuntimeCommand = docsPiCommand{
Executable: "node",
Args: []string{cliPath},
}
materializedPiRuntimeErr = nil
return materializedPiRuntimeCommand, nil
}
func getMaterializedPiRuntimeDir() (string, error) {
cacheDir, err := os.UserCacheDir()
if err != nil {
cacheDir = os.TempDir()
}
return filepath.Join(cacheDir, "openclaw", "docs-i18n", "pi-runtime", getMaterializedPiPackageVersion()), nil
}
func getMaterializedPiPackageVersion() string {
if version := strings.TrimSpace(os.Getenv(envDocsPiPackageVersion)); version != "" {
return version
}
return defaultPiPackageVersion
}