mirror of
https://github.com/cexll/myclaude.git
synced 2026-02-14 03:31:58 +08:00
fix(codeagent-wrapper): improve CI, version handling and temp dir
- CI: fetch tags for version detection - Makefile: inject version via ldflags - Add CODEAGENT_TMPDIR support for macOS permission issues - Inject ANTHROPIC_BASE_URL/API_KEY for claude backend Generated with SWE-Agent.ai Co-Authored-By: SWE-Agent.ai <noreply@swe-agent.ai>
This commit is contained in:
8
codeagent-wrapper/.github/workflows/ci.yml
vendored
8
codeagent-wrapper/.github/workflows/ci.yml
vendored
@@ -17,6 +17,9 @@ jobs:
|
|||||||
go-version: ["1.21", "1.22"]
|
go-version: ["1.21", "1.22"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
fetch-tags: true
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go-version }}
|
go-version: ${{ matrix.go-version }}
|
||||||
@@ -25,11 +28,16 @@ jobs:
|
|||||||
run: make test
|
run: make test
|
||||||
- name: Build
|
- name: Build
|
||||||
run: make build
|
run: make build
|
||||||
|
- name: Verify version
|
||||||
|
run: ./codeagent-wrapper --version
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
fetch-tags: true
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "1.22"
|
go-version: "1.22"
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
GO ?= go
|
GO ?= go
|
||||||
|
VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
|
||||||
|
LDFLAGS := -ldflags "-X codeagent-wrapper/internal/app.version=$(VERSION)"
|
||||||
|
|
||||||
TOOLS_BIN := $(CURDIR)/bin
|
TOOLS_BIN := $(CURDIR)/bin
|
||||||
TOOLCHAIN ?= go1.22.0
|
TOOLCHAIN ?= go1.22.0
|
||||||
@@ -11,8 +13,7 @@ STATICCHECK := $(TOOLS_BIN)/staticcheck
|
|||||||
.PHONY: build test lint clean install
|
.PHONY: build test lint clean install
|
||||||
|
|
||||||
build:
|
build:
|
||||||
$(GO) build -o codeagent ./cmd/codeagent
|
$(GO) build $(LDFLAGS) -o codeagent-wrapper ./cmd/codeagent-wrapper
|
||||||
$(GO) build -o codeagent-wrapper ./cmd/codeagent-wrapper
|
|
||||||
|
|
||||||
test:
|
test:
|
||||||
$(GO) test ./...
|
$(GO) test ./...
|
||||||
@@ -33,5 +34,4 @@ clean:
|
|||||||
@python3 -c 'import glob, os; paths=["codeagent","codeagent.exe","codeagent-wrapper","codeagent-wrapper.exe","coverage.out","cover.out","coverage.html"]; paths += glob.glob("coverage*.out") + glob.glob("cover_*.out") + glob.glob("*.test"); [os.remove(p) for p in paths if os.path.exists(p)]'
|
@python3 -c 'import glob, os; paths=["codeagent","codeagent.exe","codeagent-wrapper","codeagent-wrapper.exe","coverage.out","cover.out","coverage.html"]; paths += glob.glob("coverage*.out") + glob.glob("cover_*.out") + glob.glob("*.test"); [os.remove(p) for p in paths if os.path.exists(p)]'
|
||||||
|
|
||||||
install:
|
install:
|
||||||
$(GO) install ./cmd/codeagent
|
$(GO) install $(LDFLAGS) ./cmd/codeagent-wrapper
|
||||||
$(GO) install ./cmd/codeagent-wrapper
|
|
||||||
|
|||||||
@@ -150,3 +150,8 @@ make test
|
|||||||
make lint
|
make lint
|
||||||
make clean
|
make clean
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 故障排查
|
||||||
|
|
||||||
|
- macOS 下如果看到临时目录相关的 `permission denied`(例如临时可执行文件无法在 `/var/folders/.../T` 执行),可设置一个可执行的临时目录:`CODEAGENT_TMPDIR=$HOME/.codeagent/tmp`。
|
||||||
|
- `claude` 后端的 `base_url/api_key`(来自 `~/.codeagent/models.json`)会注入到子进程环境变量:`ANTHROPIC_BASE_URL` / `ANTHROPIC_API_KEY`。若 `base_url` 指向本地代理(如 `localhost:23001`),请确认代理进程在运行。
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var version = "dev"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
version = "6.1.2"
|
|
||||||
defaultWorkdir = "."
|
defaultWorkdir = "."
|
||||||
defaultTimeout = 7200 // seconds (2 hours)
|
defaultTimeout = 7200 // seconds (2 hours)
|
||||||
defaultCoverageTarget = 90.0
|
defaultCoverageTarget = 90.0
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ func newCleanupCommand() *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runWithLoggerAndCleanup(fn func() int) (exitCode int) {
|
func runWithLoggerAndCleanup(fn func() int) (exitCode int) {
|
||||||
|
ensureExecutableTempDir()
|
||||||
logger, err := NewLogger()
|
logger, err := NewLogger()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: failed to initialize logger: %v\n", err)
|
fmt.Fprintf(os.Stderr, "ERROR: failed to initialize logger: %v\n", err)
|
||||||
|
|||||||
134
codeagent-wrapper/internal/app/tmpdir.go
Normal file
134
codeagent-wrapper/internal/app/tmpdir.go
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
package wrapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const tmpDirEnvOverrideKey = "CODEAGENT_TMPDIR"
|
||||||
|
|
||||||
|
var tmpDirExecutableCheckFn = canExecuteInDir
|
||||||
|
|
||||||
|
func ensureExecutableTempDir() {
|
||||||
|
// Windows doesn't execute scripts via shebang, and os.TempDir semantics differ.
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if override := strings.TrimSpace(os.Getenv(tmpDirEnvOverrideKey)); override != "" {
|
||||||
|
if resolved, err := resolvePathWithTilde(override); err == nil {
|
||||||
|
if err := os.MkdirAll(resolved, 0o700); err == nil {
|
||||||
|
if ok, _ := tmpDirExecutableCheckFn(resolved); ok {
|
||||||
|
setTempEnv(resolved)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Invalid override should not block execution; fall back to default behavior.
|
||||||
|
}
|
||||||
|
|
||||||
|
current := currentTempDirFromEnv()
|
||||||
|
if current == "" {
|
||||||
|
current = "/tmp"
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, _ := tmpDirExecutableCheckFn(current)
|
||||||
|
if ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fallback := defaultFallbackTempDir()
|
||||||
|
if fallback == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(fallback, 0o700); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ok, _ := tmpDirExecutableCheckFn(fallback); !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setTempEnv(fallback)
|
||||||
|
fmt.Fprintf(os.Stderr, "INFO: temp dir is not executable; set TMPDIR=%s\n", fallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setTempEnv(dir string) {
|
||||||
|
_ = os.Setenv("TMPDIR", dir)
|
||||||
|
_ = os.Setenv("TMP", dir)
|
||||||
|
_ = os.Setenv("TEMP", dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultFallbackTempDir() string {
|
||||||
|
home, err := os.UserHomeDir()
|
||||||
|
if err != nil || strings.TrimSpace(home) == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return filepath.Clean(filepath.Join(home, ".codeagent", "tmp"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func currentTempDirFromEnv() string {
|
||||||
|
for _, k := range []string{"TMPDIR", "TMP", "TEMP"} {
|
||||||
|
if v := strings.TrimSpace(os.Getenv(k)); v != "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolvePathWithTilde(p string) (string, error) {
|
||||||
|
p = strings.TrimSpace(p)
|
||||||
|
if p == "" {
|
||||||
|
return "", errors.New("empty path")
|
||||||
|
}
|
||||||
|
|
||||||
|
if p == "~" || strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") {
|
||||||
|
home, err := os.UserHomeDir()
|
||||||
|
if err != nil || strings.TrimSpace(home) == "" {
|
||||||
|
if err == nil {
|
||||||
|
err = errors.New("empty home directory")
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("resolve ~: %w", err)
|
||||||
|
}
|
||||||
|
if p == "~" {
|
||||||
|
return home, nil
|
||||||
|
}
|
||||||
|
return filepath.Clean(home + p[1:]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Clean(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func canExecuteInDir(dir string) (bool, error) {
|
||||||
|
dir = strings.TrimSpace(dir)
|
||||||
|
if dir == "" {
|
||||||
|
return false, errors.New("empty dir")
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.CreateTemp(dir, "codeagent-tmp-exec-*")
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
path := f.Name()
|
||||||
|
defer func() { _ = os.Remove(path) }()
|
||||||
|
|
||||||
|
if _, err := f.WriteString("#!/bin/sh\nexit 0\n"); err != nil {
|
||||||
|
_ = f.Close()
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if err := os.Chmod(path, 0o700); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exec.Command(path).Run(); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
96
codeagent-wrapper/internal/app/tmpdir_test.go
Normal file
96
codeagent-wrapper/internal/app/tmpdir_test.go
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package wrapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEnsureExecutableTempDir_Override(t *testing.T) {
|
||||||
|
restore := captureTempEnv()
|
||||||
|
t.Cleanup(restore)
|
||||||
|
|
||||||
|
t.Setenv("HOME", t.TempDir())
|
||||||
|
t.Setenv("USERPROFILE", os.Getenv("HOME"))
|
||||||
|
|
||||||
|
orig := tmpDirExecutableCheckFn
|
||||||
|
tmpDirExecutableCheckFn = func(string) (bool, error) { return true, nil }
|
||||||
|
t.Cleanup(func() { tmpDirExecutableCheckFn = orig })
|
||||||
|
|
||||||
|
override := filepath.Join(t.TempDir(), "mytmp")
|
||||||
|
t.Setenv(tmpDirEnvOverrideKey, override)
|
||||||
|
|
||||||
|
ensureExecutableTempDir()
|
||||||
|
|
||||||
|
if got := os.Getenv("TMPDIR"); got != override {
|
||||||
|
t.Fatalf("TMPDIR=%q, want %q", got, override)
|
||||||
|
}
|
||||||
|
if got := os.Getenv("TMP"); got != override {
|
||||||
|
t.Fatalf("TMP=%q, want %q", got, override)
|
||||||
|
}
|
||||||
|
if got := os.Getenv("TEMP"); got != override {
|
||||||
|
t.Fatalf("TEMP=%q, want %q", got, override)
|
||||||
|
}
|
||||||
|
if st, err := os.Stat(override); err != nil || !st.IsDir() {
|
||||||
|
t.Fatalf("override dir not created: stat=%v err=%v", st, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnsureExecutableTempDir_FallbackWhenCurrentNotExecutable(t *testing.T) {
|
||||||
|
restore := captureTempEnv()
|
||||||
|
t.Cleanup(restore)
|
||||||
|
|
||||||
|
home := t.TempDir()
|
||||||
|
t.Setenv("HOME", home)
|
||||||
|
t.Setenv("USERPROFILE", home)
|
||||||
|
|
||||||
|
cur := filepath.Join(t.TempDir(), "cur-tmp")
|
||||||
|
if err := os.MkdirAll(cur, 0o700); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Setenv("TMPDIR", cur)
|
||||||
|
|
||||||
|
fallback := filepath.Join(home, ".codeagent", "tmp")
|
||||||
|
|
||||||
|
orig := tmpDirExecutableCheckFn
|
||||||
|
tmpDirExecutableCheckFn = func(dir string) (bool, error) {
|
||||||
|
if filepath.Clean(dir) == filepath.Clean(cur) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if filepath.Clean(dir) == filepath.Clean(fallback) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
t.Cleanup(func() { tmpDirExecutableCheckFn = orig })
|
||||||
|
|
||||||
|
ensureExecutableTempDir()
|
||||||
|
|
||||||
|
if got := os.Getenv("TMPDIR"); filepath.Clean(got) != filepath.Clean(fallback) {
|
||||||
|
t.Fatalf("TMPDIR=%q, want %q", got, fallback)
|
||||||
|
}
|
||||||
|
if st, err := os.Stat(fallback); err != nil || !st.IsDir() {
|
||||||
|
t.Fatalf("fallback dir not created: stat=%v err=%v", st, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func captureTempEnv() func() {
|
||||||
|
type entry struct {
|
||||||
|
set bool
|
||||||
|
val string
|
||||||
|
}
|
||||||
|
snapshot := make(map[string]entry, 3)
|
||||||
|
for _, k := range []string{"TMPDIR", "TMP", "TEMP"} {
|
||||||
|
v, ok := os.LookupEnv(k)
|
||||||
|
snapshot[k] = entry{set: ok, val: v}
|
||||||
|
}
|
||||||
|
return func() {
|
||||||
|
for k, e := range snapshot {
|
||||||
|
if !e.set {
|
||||||
|
_ = os.Unsetenv(k)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
_ = os.Setenv(k, e.val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,8 @@ func (ClaudeBackend) Env(baseURL, apiKey string) map[string]string {
|
|||||||
env["ANTHROPIC_BASE_URL"] = baseURL
|
env["ANTHROPIC_BASE_URL"] = baseURL
|
||||||
}
|
}
|
||||||
if apiKey != "" {
|
if apiKey != "" {
|
||||||
env["ANTHROPIC_AUTH_TOKEN"] = apiKey
|
// Claude Code CLI uses ANTHROPIC_API_KEY for API-key based auth.
|
||||||
|
env["ANTHROPIC_API_KEY"] = apiKey
|
||||||
}
|
}
|
||||||
return env
|
return env
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,8 +72,8 @@ func TestEnvInjectionWithAgent(t *testing.T) {
|
|||||||
if env["ANTHROPIC_BASE_URL"] != baseURL {
|
if env["ANTHROPIC_BASE_URL"] != baseURL {
|
||||||
t.Errorf("expected ANTHROPIC_BASE_URL=%q, got %q", baseURL, env["ANTHROPIC_BASE_URL"])
|
t.Errorf("expected ANTHROPIC_BASE_URL=%q, got %q", baseURL, env["ANTHROPIC_BASE_URL"])
|
||||||
}
|
}
|
||||||
if env["ANTHROPIC_AUTH_TOKEN"] != apiKey {
|
if env["ANTHROPIC_API_KEY"] != apiKey {
|
||||||
t.Errorf("expected ANTHROPIC_AUTH_TOKEN=%q, got %q", apiKey, env["ANTHROPIC_AUTH_TOKEN"])
|
t.Errorf("expected ANTHROPIC_API_KEY=%q, got %q", apiKey, env["ANTHROPIC_API_KEY"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,8 +149,8 @@ func TestEnvInjectionLogic(t *testing.T) {
|
|||||||
t.Errorf("ANTHROPIC_BASE_URL: expected %q, got %q", expectedURL, injected["ANTHROPIC_BASE_URL"])
|
t.Errorf("ANTHROPIC_BASE_URL: expected %q, got %q", expectedURL, injected["ANTHROPIC_BASE_URL"])
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := injected["ANTHROPIC_AUTH_TOKEN"]; !ok {
|
if _, ok := injected["ANTHROPIC_API_KEY"]; !ok {
|
||||||
t.Error("ANTHROPIC_AUTH_TOKEN not set")
|
t.Error("ANTHROPIC_API_KEY not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 5: Test masking
|
// Step 5: Test masking
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func TestMaskSensitiveValue(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "API_KEY with long value",
|
name: "API_KEY with long value",
|
||||||
key: "ANTHROPIC_AUTH_TOKEN",
|
key: "ANTHROPIC_API_KEY",
|
||||||
value: "sk-ant-api03-xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
value: "sk-ant-api03-xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||||
expected: "sk-a****xxxx",
|
expected: "sk-a****xxxx",
|
||||||
},
|
},
|
||||||
@@ -180,7 +180,7 @@ func TestClaudeBackendEnv(t *testing.T) {
|
|||||||
name: "both base_url and api_key",
|
name: "both base_url and api_key",
|
||||||
baseURL: "https://api.custom.com",
|
baseURL: "https://api.custom.com",
|
||||||
apiKey: "sk-test-key-12345",
|
apiKey: "sk-test-key-12345",
|
||||||
expectKeys: []string{"ANTHROPIC_BASE_URL", "ANTHROPIC_AUTH_TOKEN"},
|
expectKeys: []string{"ANTHROPIC_BASE_URL", "ANTHROPIC_API_KEY"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "only base_url",
|
name: "only base_url",
|
||||||
@@ -192,7 +192,7 @@ func TestClaudeBackendEnv(t *testing.T) {
|
|||||||
name: "only api_key",
|
name: "only api_key",
|
||||||
baseURL: "",
|
baseURL: "",
|
||||||
apiKey: "sk-test-key-12345",
|
apiKey: "sk-test-key-12345",
|
||||||
expectKeys: []string{"ANTHROPIC_AUTH_TOKEN"},
|
expectKeys: []string{"ANTHROPIC_API_KEY"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "both empty",
|
name: "both empty",
|
||||||
@@ -237,8 +237,8 @@ func TestClaudeBackendEnv(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if tt.apiKey != "" && strings.TrimSpace(tt.apiKey) != "" {
|
if tt.apiKey != "" && strings.TrimSpace(tt.apiKey) != "" {
|
||||||
if env["ANTHROPIC_AUTH_TOKEN"] != strings.TrimSpace(tt.apiKey) {
|
if env["ANTHROPIC_API_KEY"] != strings.TrimSpace(tt.apiKey) {
|
||||||
t.Errorf("ANTHROPIC_AUTH_TOKEN = %q, want %q", env["ANTHROPIC_AUTH_TOKEN"], strings.TrimSpace(tt.apiKey))
|
t.Errorf("ANTHROPIC_API_KEY = %q, want %q", env["ANTHROPIC_API_KEY"], strings.TrimSpace(tt.apiKey))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -267,7 +267,7 @@ func TestEnvLoggingIntegration(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if k == "ANTHROPIC_AUTH_TOKEN" {
|
if k == "ANTHROPIC_API_KEY" {
|
||||||
// API key should be masked
|
// API key should be masked
|
||||||
if masked == v {
|
if masked == v {
|
||||||
t.Errorf("API_KEY should be masked, but got original value")
|
t.Errorf("API_KEY should be masked, but got original value")
|
||||||
|
|||||||
@@ -117,14 +117,14 @@ func TestEnvInjection_LogsToStderrAndMasksKey(t *testing.T) {
|
|||||||
if cmd.env["ANTHROPIC_BASE_URL"] != baseURL {
|
if cmd.env["ANTHROPIC_BASE_URL"] != baseURL {
|
||||||
t.Fatalf("ANTHROPIC_BASE_URL=%q, want %q", cmd.env["ANTHROPIC_BASE_URL"], baseURL)
|
t.Fatalf("ANTHROPIC_BASE_URL=%q, want %q", cmd.env["ANTHROPIC_BASE_URL"], baseURL)
|
||||||
}
|
}
|
||||||
if cmd.env["ANTHROPIC_AUTH_TOKEN"] != apiKey {
|
if cmd.env["ANTHROPIC_API_KEY"] != apiKey {
|
||||||
t.Fatalf("ANTHROPIC_AUTH_TOKEN=%q, want %q", cmd.env["ANTHROPIC_AUTH_TOKEN"], apiKey)
|
t.Fatalf("ANTHROPIC_API_KEY=%q, want %q", cmd.env["ANTHROPIC_API_KEY"], apiKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.Contains(got, "Env: ANTHROPIC_BASE_URL="+baseURL) {
|
if !strings.Contains(got, "Env: ANTHROPIC_BASE_URL="+baseURL) {
|
||||||
t.Fatalf("stderr missing base URL env log; stderr=%q", got)
|
t.Fatalf("stderr missing base URL env log; stderr=%q", got)
|
||||||
}
|
}
|
||||||
if !strings.Contains(got, "Env: ANTHROPIC_AUTH_TOKEN=eyJh****test") {
|
if !strings.Contains(got, "Env: ANTHROPIC_API_KEY=eyJh****test") {
|
||||||
t.Fatalf("stderr missing masked API key log; stderr=%q", got)
|
t.Fatalf("stderr missing masked API key log; stderr=%q", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1088,6 +1088,8 @@ func RunCodexTaskWithContext(parentCtx context.Context, taskSpec TaskSpec, backe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
injectTempEnv(cmd)
|
||||||
|
|
||||||
// For backends that don't support -C flag (claude, gemini), set working directory via cmd.Dir
|
// For backends that don't support -C flag (claude, gemini), set working directory via cmd.Dir
|
||||||
// Codex passes workdir via -C flag, so we skip setting Dir for it to avoid conflicts
|
// Codex passes workdir via -C flag, so we skip setting Dir for it to avoid conflicts
|
||||||
if cfg.Mode != "resume" && commandName != "codex" && cfg.WorkDir != "" {
|
if cfg.Mode != "resume" && commandName != "codex" && cfg.WorkDir != "" {
|
||||||
@@ -1397,6 +1399,22 @@ waitLoop:
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func injectTempEnv(cmd commandRunner) {
|
||||||
|
if cmd == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
env := make(map[string]string, 3)
|
||||||
|
for _, k := range []string{"TMPDIR", "TMP", "TEMP"} {
|
||||||
|
if v := strings.TrimSpace(os.Getenv(k)); v != "" {
|
||||||
|
env[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(env) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cmd.SetEnv(env)
|
||||||
|
}
|
||||||
|
|
||||||
func cancelReason(commandName string, ctx context.Context) string {
|
func cancelReason(commandName string, ctx context.Context) string {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
return "Context cancelled"
|
return "Context cancelled"
|
||||||
|
|||||||
Reference in New Issue
Block a user