
1. 容器重建后 Trae 插件全没了问题到底出在哪如果你用 Trae 通过 SSH 连进 Docker 容器做 Python 开发大概率踩过这个坑docker compose down之后重新upSSH 照样连得上终端照样能敲命令但 Trae 左侧的扩展列表空了之前装的 Ruff、Python、Pylance 全都不见。这不是 Trae 的 bug也不是 SSH 配置坏了而是容器内/home/dev/.trae-server/extensions这个目录本身就在容器可写层里容器一删插件跟着一起消失。正常思路是提前把插件目录挂载到宿主机或者用脚本在容器启动后从缓存目录恢复。原文给的方案是scripts/restore_trae_extensions.sh从/app/vsix_cache把插件拷回去。听起来很顺但实际排障时你会发现脚本明明执行了echo也打印了「恢复完成」可 Trae 重连后插件还是没回来。这时候问题往往不在脚本逻辑本身而在三个地方——脚本里的SRC/DEST路径和docker-compose.yml的volumes挂载点对不上、cp因为目标目录有残留文件而中途报错退出、以及脚本执行时机不对导致 Trae 已经读完了扩展目录。这篇就按排障视角走一遍用 Codex 接入 TaoToken 通道让模型帮你逐行核对restore_trae_extensions.sh的路径映射检查cp命令的容错写法最后在容器重建后一键把 Trae 插件还原回来。适合已经在用 Trae Docker SSH 这套组合、但被插件持久化卡住的 Python 开发者。2. 前置给 Codex 配好 TaoToken 通道排障这件事靠自己一行行比对 YAML 和 shell 变量很容易漏。我习惯让 Codex 先读一遍docker-compose.yml和restore_trae_extensions.sh把两边的路径做交叉验证。但 Codex 要能稳定跑起来得先给它一个可用的模型通道。打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 注册后进控制台创建 Key。地址是 https://taotoken.net/api 注意这个 Base URL 不带/v1也不要额外拼 UTM 参数直接原样填。Codex 侧配置里把 provider 的 base_url 指向它api_key 填刚生成的 Key。创建 Key 的入口在控制台的 API Keys 页面模型对话入口可以用来先验证通道是否通。如果你后面要长期用 Codex 做编码和 Agent 任务可以看下 Coding Plan 的额度说明避免排障排到一半额度不够。配置写完后先用一句简单请求确认通道可用再让它读项目文件。这一步别省通道没通的话后面所有排查都是白费。3. 可复制配置让 Codex 核对路径映射3.1 先确认 docker-compose.yml 的挂载点排障第一步是把「宿主机目录」和「容器内目录」的对应关系列清楚。假设你的docker-compose.yml里是这样services: python-dev: build: context: . dockerfile: Dockerfile image: my-python-dev:latest container_name: python-dev-container ports: - 2222:22 volumes: - .:/app environment: SSH_PUBLIC_KEY: ${SSH_PUBLIC_KEY} DISPLAY: ${DISPLAY} restart: unless-stopped这里.:/app意味着宿主机的项目根目录也就是D:\PythonProjects\挂到了容器/app。那么vsix_cache在宿主机是D:\PythonProjects\vsix_cache在容器里就是/app/vsix_cache。这个映射关系是后面所有路径判断的基准。3.2 检查 restore 脚本的 SRC/DEST原文的恢复脚本长这样#!/usr/bin/env bash set -euo pipefail SRC/app/vsix_cache DEST/home/dev/.trae-server/extensions echo 开始恢复扩展 mkdir -p $DEST find $DEST -mindepth 1 -maxdepth 1 -exec rm -rf {} cp -av $SRC/. $DEST/ echo 扩展恢复完成 把这段和 compose 的 volumes 对照SRC/app/vsix_cache对应宿主机D:\PythonProjects\vsix_cache没问题。DEST/home/dev/.trae-server/extensions是 Trae 在容器内的扩展目录也没问题。但实际排障中常见的坑是有人把SRC写成了/app/vsix_cache/带尾斜杠或者DEST写成了/home/dev/.trae-server/extension少个 s脚本照样能跑完不报错但插件就是没恢复。让 Codex 帮你做这件事可以直接把两个文件贴进去用这样的提示读取 docker-compose.yml 的 volumes 配置以及 scripts/restore_trae_extensions.sh 里的 SRC 和 DEST 变量。 逐项核对 1. SRC 指向的容器路径是否等于某个 volume 挂载点的子目录 2. DEST 是否与 Trae 实际读取扩展的目录一致 3. cp 命令在目标目录已有残留文件时是否会中断。 输出一份对照表标出不一致的地方。3.3 cp 命令的容错写法set -euo pipefail加上cp -av一旦某个文件拷贝失败整个脚本会立刻退出后面的插件自然恢复不全。更稳的写法是先清空目标目录再拷并且对cp的返回码做判断#!/usr/bin/env bash set -uo pipefail SRC/app/vsix_cache DEST/home/dev/.trae-server/extensions if [ ! -d $SRC ]; then echo 缓存目录不存在: $SRC exit 1 fi mkdir -p $DEST find $DEST -mindepth 1 -maxdepth 1 -exec rm -rf {} 2/dev/null || true if cp -a $SRC/. $DEST/; then echo 扩展恢复完成共 $(ls -1 $DEST | wc -l) 项 else echo 拷贝过程中出现错误请检查 $SRC 权限与磁盘空间 exit 2 fi注意这里去掉了-e改成手动判断cp的返回码这样即使个别文件拷贝失败脚本也会把错误信息打出来而不是静默退出。find ... -exec rm -rf后面加了|| true避免目标目录为空时find返回非零导致脚本中断。4. 验证请求容器重建后跑通恢复流程配置改完后按这个顺序验证一遍。先在 Trae 里装一个插件比如 Ruff然后执行备份bash scripts/backup_trae_extensions.sh备份脚本的逻辑是把/home/dev/.trae-server/extensions的内容拷到/app/vsix_cache。执行完在宿主机D:\PythonProjects\vsix_cache下应该能看到插件目录。接着模拟容器重建docker compose down docker compose up -d --build等容器起来后SSH 连进去执行恢复bash scripts/restore_trae_extensions.sh如果输出是「扩展恢复完成共 N 项」并且 N 大于 0说明拷贝成功。这时候在 Trae 里断开重连或者按CtrlShiftP执行Developer: Reload Window左侧扩展列表应该能把 Ruff 找回来。如果想让恢复自动化可以在bootstrap-sshd.sh里加一段容器启动时自动调用恢复脚本if [ -f /app/scripts/restore_trae_extensions.sh ]; then bash /app/scripts/restore_trae_extensions.sh || true fi这样每次docker compose up之后插件就自动回来了不用手动再跑一遍。5. 本篇常见错排查5.1 脚本路径和 compose 挂载点不一致最常见的表现是脚本执行成功但插件没回来。原因通常是SRC写成了宿主机路径比如D:\PythonProjects\vsix_cache但脚本是在容器内执行的容器里根本没有D:这个盘符。记住脚本里所有路径都必须是容器内视角的路径宿主机路径只在 compose 的volumes里出现。5.2 cp 因目标目录残留而中断cp -a $SRC/. $DEST/在目标目录已有同名文件时默认会覆盖但如果遇到权限问题或者符号链接循环会报错退出。配合set -e就是直接终止。排查方法是先手动ls -la $DEST看有没有异常文件再用cp -av单独跑一次看具体报什么错。5.3 Trae 已缓存扩展列表有时候插件文件确实拷回去了但 Trae 界面没刷新。这是因为 Trae 的扩展宿主进程还持有旧的目录句柄。解决办法是断开 SSH 连接后重新连或者在命令面板执行Developer: Reload Window。如果还不行把 Trae 完全退出再打开。5.4 权限问题导致 dev 用户读不到恢复脚本如果用 root 执行拷进去的文件属主是 root而 Trae 是以dev用户跑的读不到这些文件。脚本末尾加一句chown -R dev:dev $DEST就能解决。这个坑在set -e下不会报错但插件就是加载不出来比较隐蔽。5.5 vsix_cache 目录为空如果备份脚本从来没成功跑过vsix_cache就是空的恢复脚本自然拷不出东西。先确认备份脚本的SRC指向的是 Trae 实际安装插件的目录不同版本的 Trae 这个路径可能是~/.trae-server/extensions或~/.trae/extensions用find /home/dev -name extensions -type d找一下真实位置。6. 把排查流程固化下来这套排障跑通之后建议把 Codex 的核对提示词存成一个片段下次改 compose 或脚本时直接复用。核心就三件事确认SRC是容器内路径且等于某个 volume 的子目录、确认DEST和 Trae 实际读取目录一致、确认cp失败时脚本不会静默退出。通道方面Codex 走 TaoToken 的 Base URL 是 https://taotoken.net/api Key 在控制台 API Keys 页面管理。如果只是偶尔排障模型对话入口够用如果要长期让 Codex 帮你审配置、跑 Agent 任务Coding Plan 的额度更合适。接入文档里有各客户端的完整配置示例照着填就行。最后提醒一句docker compose down之前一定先跑备份脚本别等容器删了才想起来插件没存。养成「改配置先备份、重建先恢复」的习惯这套环境就能一直稳定用下去。