【Bug已解决】/resume picker does not show CWD filter when Codex is launched with --remote ... --cd 解决方案

原始报错线索:/resume picker does not show CWD filter when Codex is launched with --remote ... --cd(当用 --remote ... --cd 参数远程启动时,「恢复会话」的选择器没有显示按当前工作目录过滤的选项)。


一、现象长什么样

用户这样启动程序:

codex --remote myhost --cd /projects/foo

然后想恢复一个会话,打开 /resume 选择器,期望能「按当前工作目录 /projects/foo 过滤」:

  1. 选择器里没有 CWD 过滤选项;
  2. 选择器把所有目录的会话混在一起,没法按 --cd 指定的目录筛;
  3. 但如果不加 --remote,本地启动时 CWD 过滤又是有的;
  4. 说明:--cd 的值在某个环节丢失了,没传到选择器。 根因是启动参数解析后,部分上下文(--cd)没有被保留并传递给后续的会话恢复功能

二、背景:启动上下文应贯穿全程

程序启动时的关键信息(远程主机、工作目录、用户、环境)构成「启动上下文」,它应当在整个进程生命周期内可被各子功能读取:

  • 主对话要用 --cd 决定初始目录;
  • /resume 选择器要用 --cd 提供过滤;
  • 文件操作要用 --cd 作为默认根。 若启动参数只被「主对话」消费、--cd 用完即弃,后续子功能就缺上下文。

三、为什么 CWD 过滤不显示:根因

3.1 启动参数局部消费后丢弃

--cd 在 main 里读出来设了工作目录,但没存进全局「启动上下文」对象,选择器读不到。

3.2 远程模式跳过了上下文传递

--remote 分支里,CWD 被当成「远端的事情」没在本地上下文登记,选择器认为「没有 CWD」。

3.3 选择器状态未初始化

选择器组件初始化时没从上下文读 CWD,默认不显示过滤 UI。

3.4 参数解析顺序问题

--cd 紧跟 --remote,解析器把 --cd 当成 --remote 的参数而非独立 flag,值被吞。

四、最小可运行复现(参数未传递到选择器)

下面演示「--cd 被主流程消费但没存上下文」:

def parse_args_bad(argv):
    ctx = {"remote": None, "cwd": None}
    i = 0
    while i < len(argv):
        if argv[i] == "--remote":
            ctx["remote"] = argv[i+1]; i += 2
        elif argv[i] == "--cd":
            # 错误:只打印/设了工作目录,没存进 ctx
            print("设置工作目录:", argv[i+1])
            i += 2
        else:
            i += 1
    return ctx     # cwd 始终是 None
if __name__ == "__main__":
    ctx = parse_args_bad(["--remote", "h1", "--cd", "/projects/foo"])
    print("选择器可读到的 cwd:", ctx["cwd"])   # None -> 过滤不显示

--cd 没存进上下文,选择器读到 None,过滤 UI 不显示(根因 3.1)。

五、解决方案一:启动上下文对象贯穿全程

把启动参数解析进一个「上下文对象」,全程共享,绝不局部丢弃:

from dataclasses import dataclass, field
from typing import Optional
@dataclass
class LaunchContext:
    remote: Optional[str] = None
    cwd: Optional[str] = None
    user: Optional[str] = None
    def has_cwd(self) -> bool:
        return bool(self.cwd)
def parse_args(argv) -> LaunchContext:
    ctx = LaunchContext()
    i = 0
    while i < len(argv):
        if argv[i] == "--remote" and i + 1 < len(argv):
            ctx.remote = argv[i+1]; i += 2
        elif argv[i] == "--cd" and i + 1 < len(argv):
            ctx.cwd = argv[i+1]; i += 2       # 关键:存进上下文
        else:
            i += 1
    return ctx
if __name__ == "__main__":
    ctx = parse_args(["--remote", "h1", "--cd", "/projects/foo"])
    print("选择器可读到的 cwd:", ctx.cwd)   # /projects/foo ✅ 过滤可显示

LaunchContext 作为单一载体,--cd 存进去,选择器随时可读(解决 3.1/3.2)。

六、解决方案二:远程模式下也登记本地 CWD 视图

即便 --remote--cd 也应作为「远端的工作目录」登记进上下文,选择器据此过滤远端的会话:

def build_resume_filter(ctx: LaunchContext):
    """根据启动上下文,决定 /resume 选择器显示哪些过滤。"""
    filters = []
    if ctx.has_cwd():
        # 无论本地还是远程,cwd 都作为过滤维度
        scope = f"remote:{ctx.remote}" if ctx.remote else "local"
        filters.append({"type": "cwd", "value": ctx.cwd, "scope": scope})
    if ctx.remote:
        filters.append({"type": "remote", "value": ctx.remote})
    return filters
if __name__ == "__main__":
    ctx = parse_args(["--remote", "h1", "--cd", "/projects/foo"])
    print("选择器过滤:", build_resume_filter(ctx))
    # [{'type':'cwd','value':'/projects/foo','scope':'remote:h1'}, ...]

远程模式下 CWD 过滤依然登记(带 remote 作用域),选择器不缺选项(解决 3.2)。

七、解决方案三:选择器初始化时从上下文读取

选择器组件构造时主动从上下文取 CWD,有则渲染过滤 UI:

class ResumePicker:
    def __init__(self, ctx: LaunchContext, sessions):
        self.ctx = ctx
        self.sessions = sessions
    def visible_filters(self):
        if self.ctx.has_cwd():
            return ["按工作目录过滤", self.ctx.cwd]
        return ["无 CWD 过滤"]
    def filter_sessions(self):
        if not self.ctx.has_cwd():
            return self.sessions
        return [s for s in self.sessions if s.get("cwd") == self.ctx.cwd]
if __name__ == "__main__":
    ctx = parse_args(["--remote", "h1", "--cd", "/projects/foo"])
    picker = ResumePicker(ctx, [
        {"id": 1, "cwd": "/projects/foo"},
        {"id": 2, "cwd": "/projects/bar"},
    ])
    print("过滤 UI:", picker.visible_filters())
    print("过滤后会话:", picker.filter_sessions())   # 只剩 cwd 匹配的

选择器从上下文读 CWD,有则显示过滤、按 CWD 筛(解决 3.3)。

八、参数解析健壮性

  • 顺序无关--remote A --cd B--cd B --remote A 等价;
  • 不吞值:每个 flag 独立消费自己的下一个参数(第五节已处理);
  • 默认值--cd 缺省时取进程 cwd(如 os.getcwd()),选择器仍有过滤;
  • 可观测:解析后打印上下文。

九、排查清单

「远程启动时恢复选择器不显示 CWD 过滤」,按下面排查:

  1. --cd 是否存进启动上下文?还是局部消费即弃(第五节,根因3.1);
  2. 远程模式是否登记 CWD(第六节);
  3. 选择器是否从上下文读 CWD(第七节);
  4. 参数解析是否吞值--cd 是否被当 --remote 的参数(第四节);
  5. --cd 缺省是否有默认(第八节);
  6. 多子功能是否共享同一上下文(呼应第133/105篇);
  7. 日志是否打印解析后的上下文
  8. 本地 vs 远程行为是否一致(第六节 scope 区分)。

十、小结

「远程启动时 /resume 选择器不显示 CWD 过滤」的根因是**--cd 等启动上下文在解析后被局部消费、未存入可共享的上下文对象,后续子功能读不到**。通用修复:

  1. 上下文对象贯穿:解析进 LaunchContext,全程共享,绝不局部丢弃(第五节);
  2. 远程也登记 CWD:远程模式下 CWD 带 remote 作用域登记,过滤不缺(第六节);
  3. 选择器读上下文:构造时取 CWD,有则渲染过滤并按其筛选(第七节);
  4. 解析健壮:顺序无关、不吞值、缺省有默认(第八节)。 一句话:启动参数不是「被某个功能一次性消费完就扔」的输入,而是「定义整个进程上下文状态」的种子,必须存进可共享对象供所有子功能读取。把启动上下文当成全局状态的第一公民,远程启动后 /resume 也能正确按 --cd 过滤——这与第 133 篇配置继承、第 105 篇数据源聚合、第 112 篇跨设备同步,共同体现「上下文必须被完整传递与共享」。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐