Repository navigation
Conversation
通用 HTTP(S)_PROXY 覆盖不到所有 agent transport:omp 的 anthropic-messages transport(cowork-fetch)只认 PI_PROXY_<PROVIDER>,没有这个变量就直连, 在受限地区拿到 403 Request not allowed;而 usage 探针等普通 fetch 走 env proxy 正常,表现为"探针活着、模型请求全红"。proxyctl 是 7890 / 兜底 7891 的持有者,因此由它把这组变量按生命周期托管起来。 - 新增 proxyctl.agent_env:出口按存活解析(引擎端口 → agent_env.fallback_daemon 的 port → 都没有),落成可 source 的契约文件 ~/.config/proxyctl/agent-env.sh;无存活出口时删除文件,避免 agent 打到死口 - start / stop / restart / fix / daemon / engine 后自动刷新;env --write / --install / --uninstall 显式管理,三种写模式支持 --dry-run(_plan_env) - env 无参输出追加 PI_PROXY_* export 行(无存活出口时输出 unset), 新增 --json;--unset 同时清 PI_PROXY_* - status 新增 AGENT ENV 段与 envelope.data.agent_env - doctor 新增 agent_env.dead_endpoint(warn) / agent_env.missing / agent_env.not_sourced,均带 fix_command 且 auto_fixable=true - config.yaml.example 新增 agent_env: 段;man / CHANGELOG / bash+zsh+fish 补全同步 - 刻意不接管通用 HTTP(S)_PROXY:那属于用户 shell 的静态约定,引擎停时不应被 proxyctl 悄悄摘掉 Tests: uv run pytest -q → 855 passed(新增 tests/unit/test_agent_env.py 37 条)
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1daf010e6e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if mode == "unset": | ||
| for var in list(_env_generic_vars(config)) + agent_env.var_names(cfg): | ||
| print(f"unset {var};") | ||
| return |
There was a problem hiding this comment.
Emit an envelope for JSON unset requests
When --json is active—especially via PROXYCTL_AGENT=1—the unset branch still prints shell statements and returns before the JSON envelope path. Thus proxyctl env --unset --json produces non-JSON stdout even though env advertises JSON support, breaking machine consumers that parse every invocation as an envelope. Emit structured data for this mode, reserving shell statements for non-JSON output.
AGENTS.md reference: AGENTS.md:L55-L60
Useful? React with 👍 / 👎.
| tmp = f"{path}.tmp" | ||
| with open(tmp, "w", encoding="utf-8") as f: | ||
| f.write(text) | ||
| os.replace(tmp, path) |
There was a problem hiding this comment.
Preserve restrictive permissions when rewriting shell rc files
When the configured shell rc already has restrictive permissions, such as mode 0600 because it contains credentials, _write_atomic creates a new file using the process umask and replaces the original; with a typical 022 umask, env --install or --uninstall silently changes it to 0644, exposing its contents to other local users. Preserve the destination mode when replacing an existing file.
Useful? React with 👍 / 👎.
| cmd_env(ctx["config"], unset=(mode == "unset"), mode=mode, | ||
| shell_rc=shell_rc) |
There was a problem hiding this comment.
Run env write modes under a write lock
For --write, --install, and --uninstall, the handler performs the dry-run check and then invokes cmd_env directly rather than through _exec_with_lock. Concurrent invocations can therefore race on the fixed .tmp path or overwrite each other's read-modify-write edits to the shell rc, instead of returning the documented LOCKED exit code. Route these modes through an appropriate write lock.
AGENTS.md reference: AGENTS.md:L241-L242
Useful? React with 👍 / 👎.
| "stale": bool(present and port and not reachable), | ||
| "expected_endpoint": expected_endpoint, | ||
| "expected_source": expected_source, | ||
| "in_sync": bool(present and endpoint == expected_endpoint), |
There was a problem hiding this comment.
Compare the full contract when reporting in_sync
If a user changes agent_env.providers while the selected endpoint remains the same, the existing contract can still contain only the old PI_PROXY_* variables, yet this expression reports in_sync: true. The status panel and doctor therefore provide no indication that newly configured providers are absent; compare the loaded providers/variables (and relevant metadata) with the desired rendered contract rather than only the endpoint.
Useful? React with 👍 / 👎.
|
由 #4 替代,关闭。 不合并的原因:
#4 只保留核心: |
新增 proxy_env_extra 配置:列出的变量名由 `proxyctl env` 导出为引擎的 HTTP 代理地址,`proxyctl env --unset` 一并清除;给不读 HTTP(S)_PROXY、只认自家变量的工具用(如 omp 的 PI_PROXY_ANTHROPIC)。默认为空;非法变量名和 YAML 解析出的非字符串项会跳过并在 stderr 提示。 替代 #3。
背景
通用
HTTP(S)_PROXY覆盖不到所有 agent transport。以 omp(pi 系)为例:anthropic+ api 为anthropic-messages时走cowork-fetch(自建node:https+ 固定https.Agent),不读 env proxy,只有 fetch init 里带显式proxy时才让位给globalThis.fetch;proxy只来自PI_PROXY_<PROVIDER>/PI_PROXY(transport-fetch.ts里的hMe()),没有 HTTPS_PROXY 回退;403 {"error":{"type":"forbidden","message":"Request not allowed"}},cf-ray 落在与直连一致的边缘节点。实测签名(本机):
POST /v1/messages403·cf-ray …-LAX127.0.0.1:7890401(鉴权错误,即已过地区检查)·cf-ray …-SINproxyctl 是 7890(引擎)与兜底出口(如 7891 claude-proxy)的持有者,因此由它把这组变量按生命周期托管,而不是让用户手工 export。
改动
新增
proxyctl.agent_env— 契约文件~/.config/proxyctl/agent-env.sh(首行为机读元信息,下面是可 source 的export行):agent_env.fallback_daemon声明的 port;两个都没在听 → 删除文件(避免 agent 一直打到死口);generated=时间戳不参与比对),start不会每次弄脏文件;anthropic→PI_PROXY_ANTHROPIC。生命周期自动刷新:
start/stop/restart/fix/daemon(start|stop|restart) /engine之后各调一次agent_env.sync(),失败只 warning 不中断主流程。CLI(
proxyctl env):proxyctl envPI_PROXY_*的 export 行;无存活出口时改输出unset,避免eval后残留死口--unsetPI_PROXY_*)--write--install--write+ 往 shell rc(默认~/.zprofile)注入 marker 块,幂等--uninstall--jsondata.vars+data.agent_env--write/--install/--uninstall均支持--dry-run(新增_plan_env)。诊断:
status新增AGENT ENV段;status --json新增data.agent_env(路径 / 出口与来源 /reachable/in_sync/shell_rc_installed);doctor新增 3 条建议,均带fix_command且auto_fixable=true:agent_env.dead_endpoint(warn)文件指向已停的出口agent_env.missing(advisory)有存活出口但没生成文件agent_env.not_sourced(advisory)文件在但 shell rc 没 source 它配置 / 文档:
config.yaml.example新增agent_env: {enabled, providers, fallback_daemon, shell_rc};man(env条目 + ENVIRONMENT + FILES);CHANGELOG[Unreleased];bash / zsh / fish 补全;supported_features新增agent_env_contract、status_agent_env。兼容性
proxyctl env无参输出追加PI_PROXY_*行(无存活出口时是unset),原有通用变量行不变;cmd_env保留unset=关键字(集成 smoke 依赖的调用形状),新增的mode=/shell_rc=为 keyword-only;env的side_effects仍为[],写行为走conditional_side_effects;agent_env配置段有默认值,老配置不用改;HTTP(S)_PROXY—— 那属于用户 shell 的静态约定,引擎停时不应该被 proxyctl 悄悄摘掉。0.5.13仍未发布([Unreleased]继续累积,遵循 AGENTS 的 bump 约定)。验证
uv run pytest -q→ 855 passed(新增tests/unit/test_agent_env.py37 条:出口解析、幂等重写、无出口删除、rc 注入/摘除、status 字段、3 条规则、各 CLI 模式与 dry-run;另补 completion 与 suggestion-id 白名单断言)。proxyctl env --install后新开 login shell 里PI_PROXY_ANTHROPIC已生效;status显示✓ PI_PROXY_ANTHROPIC=… (engine);伪造 dead endpoint → doctor 报agent_env.dead_endpoint;PROXYCTL_SHELL_RC=/tmp/nonexistent→ 报agent_env.not_sourced。备注
PROXYCTL_AGENT_ENV_PATH/PROXYCTL_SHELL_RC覆盖路径,不碰真实用户文件。