From e4e5591f95589c54b96d2e66c61ae44d11af8a7e Mon Sep 17 00:00:00 2001 From: cadywang Date: Fri, 23 Oct 2026 11:15:19 +0800 Subject: [PATCH] skill: move the Codex skill to the skills repository The skill and the code it drives were maintained side by side in one repository: a change to sdb or to the troubleshooting guide had to be applied here, while the entry points it calls live in cli/ and agent/. Two copies of the same instructions drift apart that way. The skill now lives in cadywang/skills (skills/serial-debug-bridge), so this repository keeps one job -- the bridge itself. The links in README, CONTRIBUTING and REVIEW_POLICY point there, and the changelog records the move. The wrapper still runs this repository's cli/devctl.py, which the skills README now explains. Signed-off-by: cadywang --- CHANGELOG.md | 6 + CONTRIBUTING.md | 2 +- README.md | 15 +- REVIEW_POLICY.md | 2 +- skill/serial-debug-bridge/SKILL.md | 146 ------------- skill/serial-debug-bridge/agents/openai.yaml | 7 - .../references/troubleshooting.md | 192 ------------------ skill/serial-debug-bridge/scripts/sdb | 81 -------- .../serial-debug-bridge/scripts/sdb-check.sh | 79 ------- 9 files changed, 17 insertions(+), 513 deletions(-) delete mode 100644 skill/serial-debug-bridge/SKILL.md delete mode 100644 skill/serial-debug-bridge/agents/openai.yaml delete mode 100644 skill/serial-debug-bridge/references/troubleshooting.md delete mode 100755 skill/serial-debug-bridge/scripts/sdb delete mode 100755 skill/serial-debug-bridge/scripts/sdb-check.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index e9ad779..e6c36bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,12 @@ 也要过这道门禁,所以清单没法在合并时被悄悄放宽。工具 `tools/check-pr.py`, 自测 `tests/test_check_pr.py`(18 例);机器人 PR(dependabot)跳过。 +### 变更 +- Codex skill 迁移到独立仓库 [cadywang/skills](https://github.com/cadywang/skills) 的 + `skills/serial-debug-bridge/`:本仓库不再保留 `skill/` 副本,避免 skill 与 + `devctl`/`agent` 在同一仓库里重复维护(两份实现容易各改各的)。README / CONTRIBUTING / + REVIEW_POLICY 里的引用已改指向新仓库;`scripts/sdb` 仍使用本仓库的 `cli/devctl.py`。 + ### 修复 - CI 的提交规范门禁误拒 GitHub 网页端 squash 合并产生的提交:作者被改写成 `+@users.noreply.github.com`、自动追加 `Co-authored-by:`,正文还可能夹在 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3de0b1d..042e1cb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -141,7 +141,7 @@ git config core.hooksPath tools/git-hooks 3. required status checks 未通过不可合入;单人维护模式下不设强制批准数(GitHub 不允许自批),由维护者审阅后合并——加了协作者后按 [REVIEW_POLICY.md](REVIEW_POLICY.md) 提升为「至少 1 位他人批准」。 4. 由 maintainer 合入 `main`:**Rebase and merge**(线性历史,分支自动删除)。仓库只允许这一种合并方式——**不要 Squash**:它会把整个 PR 压成一个提交,提交标题变成 PR 标题、正文被拼成一坨,每个提交的 `Signed-off-by:` 也不再是原文。rebase 保留 PR 里每个提交的作者与信息,所以提交信息必须一开始就写对;PR 标题同样按内核规范 `: <祈使句摘要>`(检索入口,且万一有人改回 squash 就会写进历史)。 5. 发布:打 tag `vX.Y.Z`,同步更新 `CHANGELOG.md`。 -6. 补丁必须保持同步:`agent/serial_agent.ps1` ↔ `agent/serial_agent.py`、`cli/devctl.ps1` ↔ `cli/devctl.py`,以及 `PROTOCOL.md` / `README.md` / `CHANGELOG.md` / `skill/` 随行为变化一起改,清单见 [REVIEW_POLICY.md](REVIEW_POLICY.md)。 +6. 补丁必须保持同步:`agent/serial_agent.ps1` ↔ `agent/serial_agent.py`、`cli/devctl.ps1` ↔ `cli/devctl.py`,以及 `PROTOCOL.md` / `README.md` / `CHANGELOG.md` 随行为变化一起改,清单见 [REVIEW_POLICY.md](REVIEW_POLICY.md)。Codex skill 已迁到 [cadywang/skills](https://github.com/cadywang/skills),跨仓库的改动请两边分别提 PR。 ## 7. 许可 diff --git a/README.md b/README.md index adaeceb..623cc5b 100644 --- a/README.md +++ b/README.md @@ -443,8 +443,9 @@ journalctl -u serial-agent -f (agent 会一直持有串口,所以一旦进入 shell,后续 `exec` 都有效。) * 设备**重启后会停在 `login:`**:直接以 **root** 登录(这些开发板 root 通常无密码), **不要反复用 `exec` 试** —— 它会把命令当用户名、探针行当密码,每次白等一个 30 秒超时。 - 登录方式(`devctl ... shell` 手动登录,或 raw 透传模式发 `root`)见 skill 的 - `references/troubleshooting.md` 第 10 节。 + 登录方式(`devctl ... shell` 手动登录,或 raw 透传模式发 `root`)见 + [skills 仓库的 `troubleshooting.md`](https://github.com/cadywang/skills/blob/main/skills/serial-debug-bridge/references/troubleshooting.md) + 第 10 节。 * 波特率/串口名不对:核对 `-Port` `-Baud` / `--port` `--baud`,确认设备实际参数。 * 换行/回车不对:极少数设备只认 `\r`,可在设备侧 `stty` 调整,或先 `shell` 手动敲一次。 @@ -562,10 +563,12 @@ squash 合并,它就会变成 `main` 上的提交标题)与 **模板里「 * 安全问题**不要**开公开 Issue,见 [`SECURITY.md`](SECURITY.md)。 * 提问前先看 [`SUPPORT.md`](SUPPORT.md),Issue/PR 模板在 `.github/`。 * 依赖更新由 `.github/dependabot.yml` 发起 PR(同样要过 CI 五个 job;bot 生成的提交按 `--skip-bots` 豁免 commit 风格检查,见 [`REVIEW_POLICY.md`](REVIEW_POLICY.md))。 -* 面向 server 的 Codex skill 在 `skill/serial-debug-bridge/`,安装到 server 的 - `~/.codex/skills/` 后即可让 server 侧的 Codex 直接调用(`scripts/sdb` 为统一入口, - `scripts/sdb-check.sh` 做链路与设备身份自检): - `scp -r skill/serial-debug-bridge :~/.codex/skills/` +* 面向 server 的 Codex skill 已迁到 [cadywang/skills](https://github.com/cadywang/skills) 的 + `skills/serial-debug-bridge/`(本仓库不再保留副本);装到 server 的 `~/.codex/skills/` + 后即可让 server 侧的 Codex 直接调用(`scripts/sdb` 为统一入口,`scripts/sdb-check.sh` + 做链路与设备身份自检):在 skills 仓库里执行 + `scp -r skills/serial-debug-bridge :~/.codex/skills/`。 + `scripts/sdb` 仍使用本仓库的 `cli/devctl.py`(安装方式见 skills 仓库 README)。 * 重新施加/校验保护规则: `pwsh -File tools/create-github-repo.ps1 -Owner `(幂等,支持 `-DryRun`)。 diff --git a/REVIEW_POLICY.md b/REVIEW_POLICY.md index 974bc91..f9e2a1a 100644 --- a/REVIEW_POLICY.md +++ b/REVIEW_POLICY.md @@ -36,7 +36,7 @@ git push -u origin agent/hex-retry | `cli/devctl.ps1` | `cli/devctl.py` | | 协议行为(帧格式、编码、超时语义) | `PROTOCOL.md` + 两套实现 + 两套 mock(`tests/*`) | | 面向用户的行为 | `README.md`、`CHANGELOG.md` | -| skill 用到的入口/参数(`scripts/sdb`、环境变量) | `skill/serial-debug-bridge/SKILL.md` 与 `references/` | +| skill 用到的入口/参数(`scripts/sdb`、环境变量) | [cadywang/skills](https://github.com/cadywang/skills) 的 `skills/serial-debug-bridge/SKILL.md` 与 `references/`(不在本仓库;跨仓库改动两边分别提 PR) | CI 的 `Cross implementation check` 只能验证「ps 的 CLI 连 py 的 agent」这类组合, **覆盖不了语义是否一致**——所以人工审查时要专门盯这一项。 diff --git a/skill/serial-debug-bridge/SKILL.md b/skill/serial-debug-bridge/SKILL.md deleted file mode 100644 index 3f9dc78..0000000 --- a/skill/serial-debug-bridge/SKILL.md +++ /dev/null @@ -1,146 +0,0 @@ ---- -name: serial-debug-bridge -description: 在 server 上通过 serial-debug-bridge 操作接在 client 串口上的嵌入式设备:执行命令、传文件、复位、交互控制台,以及链路排障。适用于串口不在本机(devctl/agent/TCP/SSH/反向隧道)的场景。 -metadata: - short-description: 通过远端串口调试嵌入式设备 ---- - -# serial-debug-bridge(server 侧操作) - -## 心智模型 - -``` -server: devctl --TCP 或 SSH--> client: serial_agent --UART--> 嵌入式设备 -``` - -串口插在哪台机器上,`serial_agent` 就在哪台机器上跑(下称 **client**); -你在 **server** 上敲 `sdb`,`sdb` 调用 `devctl.py` 把命令/文件转给 agent,agent 再读写串口。 - -**设备控制台通常就是 root shell**:能连上 agent 等于能完全控制设备。 - -## 怎么调用:用 `scripts/sdb` - -`scripts/sdb` 是唯一的推荐入口,它替你处理端点解析、鉴权参数和外层超时: - -```bash -S="$HOME/.codex/skills/serial-debug-bridge/scripts/sdb" - -$S info # 代理/设备信息(不碰设备) -$S exec 'uname -a; id -un' # 在设备上执行;退出码 = 设备命令退出码 -$S push ./fw.bin /tmp/fw.bin # server -> 设备(设备侧 md5 校验) -$S pull /etc/hostname ./h # 设备 -> server(落盘后 md5 校验) -$S md5 /tmp/fw.bin # 设备侧 md5 -$S reset # 通过 DTR 复位(会打断设备,先确认) -$S shutdown # 让 client 上的 agent 退出,释放串口 -$S shell # 交互式控制台(仅 TCP),Ctrl+] 退出 -``` - -等价的直接调用(需要自己拼参数): - -```bash -python3 ~/serial-debug-bridge/devctl.py --tcp 127.0.0.1:17801 exec "uname -a" -``` - -### 端点解析(环境变量 > 配置文件 > 默认) - -| 变量 | 默认 | 说明 | -| --- | --- | --- | -| `SDB_TCP` | `127.0.0.1:17801` | 连已监听的 agent;直连时写 `client-ip:7801` | -| `SDB_SSH` | 空 | `user@host`,让 devctl 通过 ssh 在 client 上拉起 agent | -| `SDB_PORT` / `SDB_BAUD` | `/dev/ttyUSB0` / `115200` | 仅 `SDB_SSH` 时用 | -| `SDB_REMOTE_AGENT` | 空 | client 上的 agent 路径(仅 `SDB_SSH`) | -| `SDB_TOKEN` | 空 | agent 用 `--auth` 启动时的口令 | -| `SDB_DEVCTL` | 见脚本 | `devctl.py` 路径 | -| `SDB_CONFIG` | `~/.config/serial-debug-bridge/target` | 配置文件(会被 source) | -| `SDB_TIMEOUT` | `180` | 外层硬超时秒数,`0` 关闭 | -| `SDB_EXTRA` | 空 | 附加 devctl 参数,按空格切分,如 `-q --timeout 300000` | - -> **大文件务必放宽超时**:`SDB_EXTRA="-q" SDB_TIMEOUT=0 $S push big.bin /tmp/big.bin`。 -> 60 KB 上传就要约 50 秒(见下)。 - -## 开工前的两步 - -```bash -# 1) 链路与设备身份:确认「我连上的确实是这台设备」 -bash "$HOME/.codex/skills/serial-debug-bridge/scripts/sdb-check.sh" -bash "$HOME/.codex/skills/serial-debug-bridge/scripts/sdb-check.sh" --ports # 顺带看端口占用 - -# 2) 设备有哪些传输工具 -$S exec 'command -v busybox base64 openssl awk od md5sum' -``` - -`sdb-check.sh` 会打印端点、`info`、设备身份(`uname -a; hostname`)和传输能力; -`--ports` 会列出本机该端口的监听者,用来发现「端口被别的进程抢占」。 - -## 传输可靠性与速度(必须知道) - -串口在高波特率下**收发两个方向都会偶发丢字节**,所以不要硬怼: - -* **编码自适应**:`base64` → `busybox base64` → `openssl base64` → `hex+awk`。 - 设备没有 base64 时(例如 Yocto 精简镜像)自动走 hex+awk。 -* **上传逐块校验 + 重传**:按 1 KB 分块,设备侧自算 md5,追加后再核对文件大小, - 不一致就重发;失败重发前会自动发 Ctrl-C 复位可能被卡住的 shell。 -* **发送方向用回显校验**:agent 会确认设备回显的命令与我们发送的一致,不一致自动重发 - (曾观察到 `uname -a` 被丢成 `uname`,从而执行了错误的命令)。 -* **速度约 1~1.2 KB/s**。大文件先压缩:`$S exec 'tar czf /tmp/a.tgz '` 再 `pull`。 -* 单会话:agent 是单线程的,别并发发起多个 `sdb`。 - -## 排障(详见 references/troubleshooting.md) - -1. **`agent closed the connection` / 莫名超时** - 端点被别的进程抢了。**VS Code 会自动转发它检测到的端口并绑定 `127.0.0.1`**, - 比反向隧道(通常连 `127.0.0.1`)更具体从而抢走连接。 - 用 `sdb-check.sh --ports` 看监听者,改连 `127.0.0.2:port` 或换端口。 -2. **设备 shell 停在 `>`**(上次传输被中断留下的未闭合引号/here-document) - agent 超时会自动发 Ctrl-C;必要时 `$S shell` 里手动 Ctrl-C,或兜底 `$S reset`(先确认)。 -3. **`push` 报 `verified=False`** 重试;持续失败就降波特率,或先压缩文件。 -4. **传输中途失败且控制台出现启动横幅 / `login:`** - 设备自己重启了,不是桥的问题。agent 超时会把控制台最后 200 字节打进日志 - (`console said: ...`),先看那条;重新登录后再传。 - -5. **agent 停不下来 / 串口被它占住** - 还连得上就 `$S shutdown`(agent 自己退出并释放串口);连不上(孤儿进程)就在 client 上跑 - `agent/stop-agent.ps1 -Port COM3`(Windows)或 `agent/stop-agent.sh --port /dev/ttyUSB0`(Linux), - 先加 `-List` / `--list` 看会停谁。详见 `references/troubleshooting.md` §12。 - -6. **设备停在 `login:`(重启后最常见)——先以 root 登录,别硬试** - 登录提示符要的是「用户名 + 密码」两行,而 `exec` 发的是「命令 + 探针」, - 于是命令被当成用户名、探针行被当成密码:**必然失败,还白等一个 30 秒超时**(试几次就是几分钟)。 - 这些开发板 root 通常**无密码**,直接登录即可: - - * 人在终端:`$S shell` → 看到 `login:` 输入 `root` 回车 → `Ctrl+]` 退出; - * 脚本化 / 无人值守:client 上以 **raw 透传**启动 agent(串口独占,先停掉协议模式 agent: - `$S shutdown`,或在 client 上跑 `stop-agent.ps1` / `stop-agent.sh`): - `powershell -File \agent\serial_agent.ps1 -Port COM3 -Baud 460800 -TcpPort 17801 -Raw`, - 再从 server 侧发两行:`printf '\nroot\n' | nc 127.0.0.1 17801`,看到 `root@:~#` 即成功; - 然后停掉 raw agent(raw 模式没有协议通道,只能用 client 上的 `stop-agent` 脚本)、 - 改回协议模式重启 agent。 - - 登录状态会一直保持(agent 持有同一条串口会话),所以**一次会话只需登录一次**。 - 详见 `references/troubleshooting.md` §10。 - -## 发现 bug:请提交 patch,不要只绕过去 - -这套桥(`agent/*`、`cli/*`、`tests/*`、`skill/*`)由维护者在 GitHub 上持续修正。 -**使用中踩到 bug,除了当下绕过,还要把修复交回上游**: - -1. **先给最小复现**:触发命令、期望结果、实际输出(含退出码)、agent 日志(`-LogFile`)。 - 先跑一遍 `sdb-check.sh` 并把输出附上——不少「桥的 bug」其实是设备重启或端口被占。 -2. **改代码要成对改**:`agent/serial_agent.ps1` ↔ `agent/serial_agent.py`、 - `cli/devctl.ps1` ↔ `cli/devctl.py` 必须同步(本工具支持 ps↔py 交叉使用);涉及协议时同步 `PROTOCOL.md`。 -3. **跑自测再提**:`powershell -File tests/selftest.ps1`(含 `-Codec hex`)、 - `python3 tests/test_e2e.py`(含 `--agent-args "--codec hex"`);有条件再跑 `bash tests/verify-live.sh :`。 -4. **按仓库规范提交**:内核风格 commit(`: <祈使句摘要>`,正文 ≤72 列,`git commit -s` 带 - `Signed-off-by:`),从 `main` 切分支(`agent/...`、`cli/...`、`skill/...`)后提 PR,并在 PR 里贴自测输出。 -5. **没有写权限时**:把 `git diff` 存成 `fix.patch`,连同最小复现与自测输出一起交给维护者。 -6. **确实只能绕过时**(换编码、降波特率、改端口等):在结果里写清**绕过了什么、为什么、风险是什么**, - 并给出建议的修复方向——别让同一个坑下次再踩。 - -> 仓库约定:补丁必须走 PR、CI 全绿后由维护者合并(`main` 不接受直接推送),且**两套实现与文档必须同步修改**——详见仓库根目录的 `REVIEW_POLICY.md` 与 `CONTRIBUTING.md`。 - -## 安全 - -* 破坏性操作(`rm`、`dd`、写 flash、`reset`、刷固件)**必须**先与用户确认, - 并先 `$S exec 'uname -a; hostname'` 确认目标是预期设备——端口/隧道可能指向另一台设备。 -* `push` 会覆盖目标路径,先 `$S exec 'ls -l '` 看看。 -* 不要把 agent 端口暴露到公网;TCP 方式配合 `SDB_TOKEN`(agent 用 `--auth` 启动)或 SSH。 diff --git a/skill/serial-debug-bridge/agents/openai.yaml b/skill/serial-debug-bridge/agents/openai.yaml deleted file mode 100644 index 66a77f4..0000000 --- a/skill/serial-debug-bridge/agents/openai.yaml +++ /dev/null @@ -1,7 +0,0 @@ -interface: - display_name: "Serial Debug Bridge" - short_description: "从 server 通过远端串口操作嵌入式设备" - brand_color: "#0B7285" - default_prompt: "Use $serial-debug-bridge to check the link and run the command I need on the embedded device." -policy: - allow_implicit_invocation: true diff --git a/skill/serial-debug-bridge/references/troubleshooting.md b/skill/serial-debug-bridge/references/troubleshooting.md deleted file mode 100644 index 58f6217..0000000 --- a/skill/serial-debug-bridge/references/troubleshooting.md +++ /dev/null @@ -1,192 +0,0 @@ -# 排障手册 - -## 1. 连接建立了但立刻关闭 / 一直卡住 - -症状:`devctl: agent closed the connection`、或连上后长时间无输出、或 `info` 都超时。 - -排查顺序: - -1. `scripts/sdb-check.sh` —— 它会打印端点解析结果、`info`、设备身份。 -2. 端点被占用:一个端口上可能同时存在**两个监听**,例如 client 上 - `0.0.0.0:17801`(我们的 agent)与 `127.0.0.1:17801`(VS Code 的自动端口转发)。 - Windows 会优先匹配更具体的 `127.0.0.1`,于是反向隧道(`-R ...:127.0.0.1:17801`) - 连到的是 VS Code 的转发器,协议不匹配 → 立刻关闭。 - 处理:隧道/客户端改连 `127.0.0.2:17801`(Windows 回环 /8 都可用),或换端口, - 或在 VS Code 的 Ports 面板里取消该端口的转发。 -3. agent 是单线程的:某个会话卡住(比如设备不回话)会让后续连接排队、看起来像超时。 - 等它超时退出,或在 client 上重启 agent。 - -## 2. 设备 shell 被卡住(提示符变成 `>`) - -原因:上一次操作被中断,留下未闭合的引号或 here-document,设备 shell 在等收尾。 - -处理: - -* agent 在命令超时后会自动发送 Ctrl-C(`\x03` + 换行),多数情况能自己恢复; -* 若仍未恢复:`python3 devctl.py --tcp shell` 后按 Ctrl-C, - 或让 client 侧用任意串口工具发 Ctrl-C; -* 兜底:`devctl ... reset`(DTR 复位),**执行前确认不会影响设备上的重要状态**。 - -## 3. 上传/下载不一致 - -* `push` 的 `verified=True` 才算成功;`verified=False` 直接重试。 -* 连续失败:降低波特率(例如 460800 → 115200)、换短一点/屏蔽更好的线、 - 或先把文件 `tar cz` 压缩后传(体积小,链路上的出错窗口就小)。 -* `pull` 后 devctl 会比对 md5;不一致时不要使用该文件。 - -## 4. 设备缺少工具 - -`push`/`pull` 依赖设备自带工具,探测顺序: - -| 能力 | 需要的设备工具 | -| --- | --- | -| 上传/下载(首选) | `base64`(或 `busybox base64`、`openssl base64`) | -| 上传/下载(回退) | `awk` + `od` + `md5sum` | -| 校验 | `md5sum`(必需) | - -用 `exec "command -v base64 awk od md5sum"` 确认。若 `md5sum` 都没有, -校验会退化,此时不要用串口搬重要文件。 - -## 5. 交互式 shell - -* 只支持 TCP 传输(SSH 的 stdio 是纯协议通道,会返回 `shell-requires-tcp`)。 -* `Ctrl+]` 退出(devctl.ps1 与 devctl.py 同)。 -* 需要登录的设备:先在 `shell` 里登录,agent 会保持同一条串口会话,之后的 `exec` 就可以用。 - -## 6. 复位 - -`reset` 拉低再拉高 DTR。仅在设备的 DTR 接到了复位脚时有效;复位会中断一切正在进行的操作, -执行前必须与用户确认。 - -## 7. 超时(`SDB_TIMEOUT`) - -`sdb` 默认给 devctl 套一层外层硬超时(默认 180 秒),因为 devctl 自己不会主动放弃一个卡死的 agent。 -串口传输很慢:**60 KB ≈ 50 秒**,200 KB ≈ 3 分钟。所以: - -```bash -SDB_TIMEOUT=0 sdb push big.bin /tmp/big.bin # 关闭外层超时 -SDB_EXTRA="-q" SDB_TIMEOUT=600 sdb push big.bin /tmp/big.bin -``` - -被外层 `timeout` 杀掉的表现是进程返回 124、设备上留下**半个文件**——重传前先删掉目标文件, -或者让它保持最后覆盖的语义(`push` 开头会 `: >` 截断目标)。 - -## 8. 从别的机器/脚本里调用时的引号 - -`sdb exec 'uname -a; id -un'` 里的参数会被逐层解析:本地 shell → ssh → 远端 shell → sdb → devctl。 -少一层引号就会让 `;`、`|`、`$` 在**错误的机器上**执行(典型症状:`id -un` 打印出了 server 的用户名, -而不是设备的 `root`)。 - -可靠做法: - -* 在 server 上把命令写进脚本文件再执行(`scp` 上去后 `bash script.sh`); -* 或远端外层用单引号、内部避免再嵌套引号; -* 单条命令优先用 `sdb exec '...'` 且不要在 ssh 的同一层再包双引号。 -## 9. 传输中途失败,控制台出现启动横幅 / `login:` - -如果 `push`/`pull` 中途报 `upload failed (lock-step ack timeout)`,而随后 `sdb-check.sh` -或 `sdb exec` 的输出里出现内核启动横幅、`login:` 或 `Poky ... ttyS0`, -那**不是桥的故障——是设备自己重启了**(看门狗、掉电、被其它工具打断等)。 - -agent 在超时时会把控制台最后约 200 字节写进日志(`-LogFile`), -先看那条 `console said: ...` / `timeout waiting for the marker; console said: ...` 再判断。 - -处理: - -* 在控制台重新登录(需要登录的镜像;多数开发板 root 无密码),提示符回来后重传; -* 中途重启会留下半个文件:重传前删掉目标文件,或让 `push` 直接覆盖; -* 若频繁重启,先排查设备侧(供电、看门狗、内存)——串口传输本身不会导致重启。 - -> 实测:一次 200 KB 上传失败正是因为设备在传输中重启;重新登录后同一操作 -> 90 秒完成且 md5 一致。 - -## 10. 设备停在 `login:` 提示符(重启后最常见) - -**症状**:`exec` 一直超时(rc=124),或 `sdb-check.sh` 的输出里出现 -`Poky (Yocto Project Reference Distro) ... ttyS0` 紧跟着 `login:`。 - -**为什么不能反复用 `exec` 试**:登录提示符期待的是「用户名 + 密码」两行,而 `exec` 发的是 -「命令文本 + 探针行」——于是命令被当成用户名、探针行被当成密码,**必然登录失败**, -而且每次都要白等一个 30 秒超时(试三五次就是好几分钟)。 - -**正确做法:直接以 root 登录(这些开发板 root 通常无密码)** - -1. 人在终端前 —— 用交互控制台(仅 TCP): - - ```bash - $S shell # 看到 login: 后输入 root 回车,Ctrl+] 退出 - ``` - -2. 脚本化 / 无人值守 —— 切到 raw 透传模式,直接发两行: - - ```bash - # ① client(串口所在机器)上:先停掉协议模式的 agent(串口独占),改用 raw - powershell -NoProfile -ExecutionPolicy Bypass -File \agent\serial_agent.ps1 ` - -Port COM3 -Baud 460800 -TcpPort 17801 -Raw - - # ② server 侧:先发一个空行清掉残留输入,再发 root - printf '\nroot\n' | nc 127.0.0.1 17801 - # 期望:myboard login: root 然后 root@myboard:~# - ``` - -3. 登录成功后:停掉 raw agent,重新以**协议模式**启动 agent,`info` / `exec` / `push` 全部恢复。 - -**要点** - -* 登录状态由设备侧持有,agent 只负责搬字节:**一次会话只需登录一次**,之后 `exec` 一直可用。 -* 不要在登录提示符下"多试几次"——那是纯粹浪费时间;不确定卡在哪,先跑一次 `sdb-check.sh`。 -* 少数板子 root 要密码:raw 模式下把密码也发一行即可(`printf '\nroot\n\n'`)。 - -## 11. 确认是桥的 bug 之后 - -本文列的多半是使用姿势与环境问题。若确认是桥自身的缺陷(稳定复现、与设备无关), -按 `SKILL.md` 的「发现 bug:请提交 patch」把最小复现 + `fix.patch` + 自测输出交回上游, -不要只在上层脚本里绕过去——绕过会让下一个人再踩一次。 - -## 12. 串口被 agent 占住 / agent 停不下来 - -症状:`info` / `exec` 超时或 `agent closed the connection`,client 上的串口工具报 -「访问被拒绝 / busy」,而你以为 agent 早就不在了。 - -原因:串口是**独占**的,agent 只要还活着就占着它。最常见的两种情况: - -* 启动 agent 的窗口 / SSH 会话没了,进程却留了下来(孤儿进程); -* 想换一个 agent(换波特率、换串口、切到 `-Raw` 去登录控制台),旧的那个还在跑。 - -处理: - -1. **还连得上 agent 时**(TCP 或 SSH)——让 agent 自己退出: - - ```bash - $S shutdown # 回应里带 pid,便于确认停的是哪一个 - ``` - - 注意这会打断别人正在用的链路(agent 单线程,一次只服务一个会话)。 - -2. **连不上时**(人在 client 机器上): - - ```powershell - # Windows:先 -List 只看不杀 - powershell -ExecutionPolicy Bypass -File agent\stop-agent.ps1 -List - powershell -ExecutionPolicy Bypass -File agent\stop-agent.ps1 -Port COM3 - ``` - - ```bash - # Linux / macOS - ./agent/stop-agent.sh --list - ./agent/stop-agent.sh --port /dev/ttyUSB0 - ``` - - 脚本按**启动方式**匹配 agent(`-File <脚本>`,或脚本作为参数),过滤条件支持串口名与 - TCP 端口,停完会复核进程是否真的没了。装了 systemd 单元的直接 `sudo systemctl stop serial-agent`。 - -3. **还是打不开**:那就不是 agent。串口终端(PuTTY / MobaXterm / IDE 的串口监视器)也会 - 一直占着它,先关掉;Windows 上要确认谁持有句柄,可用 Sysinternals `handle64.exe -a <设备名>`, - 设备名是驱动对象名而不是 `COM3`(本次实测的 FTDI 串口是 `VCP0`),可以用注册表映射查到: - - ```powershell - Get-ItemProperty 'HKLM:\HARDWARE\DEVICEMAP\SERIALCOMM' # -> \Device\VCP0 : COM3 - ``` - -> `shutdown` 与 `stop-agent*` 只结束 **agent 进程**(串口随之释放),不会动设备;别和 `reset` -> 搞混——`reset` 拉 DTR 会重启设备。 diff --git a/skill/serial-debug-bridge/scripts/sdb b/skill/serial-debug-bridge/scripts/sdb deleted file mode 100755 index e6d6ab9..0000000 --- a/skill/serial-debug-bridge/scripts/sdb +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env bash -# sdb -- talk to the embedded board through the client-side serial agent. -# -# sdb info agent + device information -# sdb exec 'uname -a' run a shell command on the board -# sdb push ./fw.bin /tmp/fw.bin upload to the board (device-side md5 verified) -# sdb pull /etc/hostname ./h download from the board -# sdb md5 /tmp/fw.bin device-side md5 of a file -# sdb reset reboot the board via DTR -# -# Target resolution: environment > config file > built-in default. -# SDB_TCP host[:port] connect to an already-listening agent (default 127.0.0.1:17801) -# SDB_SSH user@host let devctl launch the agent over ssh (needs server->client ssh) -# SDB_PORT, SDB_BAUD serial device/baud on the client (only used with SDB_SSH) -# SDB_REMOTE_AGENT agent path on the client (only used with SDB_SSH) -# SDB_TOKEN auth token, if the agent was started with --auth -# SDB_DEVCTL path to devctl.py -# SDB_CONFIG config file (default ~/.config/serial-debug-bridge/target) -# SDB_TIMEOUT outer hard timeout in seconds, 0 disables (default 180; -# a 60 KB upload alone takes ~50 s at 460800 baud) -# SDB_EXTRA extra devctl flags, word-split (e.g. "-q --timeout 120000") -set -euo pipefail - -DEVCTL="${SDB_DEVCTL:-/SSD4T2/home/yi_wang/serial-debug-bridge/devctl.py}" -# Fall back to the usual locations so the wrapper is not tied to one host. -if [ ! -f "$DEVCTL" ]; then - for c in "$HOME/serial-debug-bridge/devctl.py" "$HOME/.local/share/serial-debug-bridge/devctl.py" "./cli/devctl.py"; do - [ -f "$c" ] && DEVCTL="$c" && break - done -fi -CFG="${SDB_CONFIG:-$HOME/.config/serial-debug-bridge/target}" - -tcp="127.0.0.1:17801" -ssh_target="" -port="/dev/ttyUSB0" -baud="115200" -remote_agent="" -token="" - -if [ -f "$CFG" ]; then - # shellcheck disable=SC1090 - . "$CFG" -fi - -tcp="${SDB_TCP:-$tcp}" -ssh_target="${SDB_SSH:-$ssh_target}" -port="${SDB_PORT:-$port}" -baud="${SDB_BAUD:-$baud}" -remote_agent="${SDB_REMOTE_AGENT:-$remote_agent}" -token="${SDB_TOKEN:-$token}" - -if [ ! -f "$DEVCTL" ]; then - echo "sdb: devctl.py not found: $DEVCTL (set SDB_DEVCTL)" >&2 - exit 2 -fi - -args=() -if [ -n "$ssh_target" ]; then - args+=(--ssh "$ssh_target" --port "$port" --baud "$baud") - if [ -n "$remote_agent" ]; then - args+=(--remote-agent "$remote_agent") - fi -else - args+=(--tcp "$tcp") -fi -if [ -n "$token" ]; then - args+=(--token "$token") -fi - -extra=() -if [ -n "${SDB_EXTRA:-}" ]; then - # shellcheck disable=SC2206 - extra=(${SDB_EXTRA}) -fi - -# devctl itself never gives up on a wedged agent, so keep an outer hard timeout. -to="${SDB_TIMEOUT:-180}" -if [ "$to" = "0" ]; then - exec python3 "$DEVCTL" "${args[@]}" "${extra[@]}" "$@" -fi -exec timeout -k 5 "$to" python3 "$DEVCTL" "${args[@]}" "${extra[@]}" "$@" diff --git a/skill/serial-debug-bridge/scripts/sdb-check.sh b/skill/serial-debug-bridge/scripts/sdb-check.sh deleted file mode 100755 index 80c3854..0000000 --- a/skill/serial-debug-bridge/scripts/sdb-check.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env bash -# --------------------------------------------------------------------------- -# sdb-check.sh -- verify the serial-debug-bridge link and identify the device. -# -# Run this on the SERVER before doing anything else. It answers three -# questions that are otherwise guessed at: -# 1. which devctl and which endpoint am I about to talk to? -# 2. is the agent answering, and which device is actually on the other end? -# 3. can that device decode/verify file transfers at all? -# -# Usage: -# scripts/sdb-check.sh # uses SDB_DEVCTL / SDB_TARGET -# scripts/sdb-check.sh --ports # also list local listeners on the port -# SDB_TARGET=172.21.5.10:7801 scripts/sdb-check.sh -# --------------------------------------------------------------------------- -set -uo pipefail - -DEVCTL="${SDB_DEVCTL:-}" -TARGET="${SDB_TARGET:-127.0.0.1:17801}" -SHOW_PORTS=0 -[ "${1:-}" = "--ports" ] && SHOW_PORTS=1 - -if [ -z "$DEVCTL" ]; then - for c in "$HOME/serial-debug-bridge/devctl.py" "$HOME/.local/share/serial-debug-bridge/devctl.py" "./cli/devctl.py"; do - [ -f "$c" ] && DEVCTL="$c" && break - done -fi -[ -n "$DEVCTL" ] && [ -f "$DEVCTL" ] || { echo "sdb-check: devctl.py not found; set SDB_DEVCTL" >&2; exit 2; } - -PY="$(command -v python3 || command -v python || true)" -[ -n "$PY" ] || { echo "sdb-check: python3 not found" >&2; exit 2; } - -HOST="${TARGET%:*}" -PORT="${TARGET##*:}" - -echo "== endpoint ==" -echo " devctl : $DEVCTL" -echo " target : $TARGET" - -if [ "$SHOW_PORTS" = 1 ]; then - echo "== listeners for port $PORT on this host ==" - (command -v ss >/dev/null 2>&1 && ss -tlnp 2>/dev/null | grep -E "[:.]$PORT\b") \ - || (command -v netstat >/dev/null 2>&1 && netstat -tln 2>/dev/null | grep -E "[:.]$PORT\b") \ - || echo " (no ss/netstat)" - echo " note: if one address is wider than the other (0.0.0.0 vs a specific" - echo " address) the wider one may never receive the connection: a more" - echo " specific bind wins. This is what happens when an editor auto-forwards" - echo " the port on 127.0.0.1 and shadows a reverse tunnel to 127.0.0.1." -fi - -echo "== agent ==" -if ! "$PY" "$DEVCTL" --tcp "$TARGET" info; then - echo "sdb-check: the agent did not answer. See references/troubleshooting.md" >&2 - exit 1 -fi - -echo "== device identity ==" -IDENT="$("$PY" "$DEVCTL" --tcp "$TARGET" exec "uname -a; hostname 2>/dev/null" 2>&1)" || true -printf '%s\n' "$IDENT" - -# After a reboot the console usually sits at a login prompt. Retrying exec there -# only burns one 30 s timeout per attempt, so say it once and point at the fix. -if printf '%s' "$IDENT" | grep -qi 'login:'; then - echo - echo "!! the device is sitting at a login prompt (login:)." - echo "!! do NOT retry exec: each attempt just burns a 30 s timeout." - echo "!! log in as root (usually password-less), then continue:" - echo "!! interactive: sdb shell -> type: root -> Ctrl+]" - echo "!! scripted : start the agent with -Raw / --raw and send:" - echo "!! printf '\nroot\n' | nc :" - echo "!! details: references/troubleshooting.md section 10" -fi - -echo "== transfer capability ==" -"$PY" "$DEVCTL" --tcp "$TARGET" exec "command -v busybox base64 openssl awk od md5sum 2>/dev/null" || true - -echo "== hint ==" -echo " expected: md5sum present, and either base64 (any variant) or awk+od." -echo " speed is roughly 1-1.2 KB/s; compress large payloads first."