在使用 Claude Code、Codex 或 DeepSeek Harness 等 AI 编程助手执行长时间任务时,我们往往需要离开电脑处理其他事务。本教程为这些 Agent 一键配置桌面弹窗提醒与手机远程交互:任务完成时在电脑上弹窗提醒,并把 AI 最后输出的一段话推送到手机;需要用户确认或选择时通过手机 ntfy 推送完成交互确认;还可以在手机端直接发送文字继续对话(远程续聊),让你无需一直盯着终端。整个过程只需安装一个包、运行一次安装命令。

前置教程

资源下载

相关文档

1. 方案简介

a4phone 是一个通过 ntfy.sh 实现 Claude Code / Codex / DSH(DeepSeek Harness)远程手机交互的 npm 包,安装后自动处理三类事件,并额外提供远程续聊能力:

事件 行为
Stop(任务完成) 电脑弹窗 + 手机推送完成信息(含目录、会话、AI 最后输出
AskUserQuestion(AI 提问,Codex 为 request_user_input 电脑弹窗 + 手机显示选项按钮可点选
PermissionRequest(权限请求) 电脑弹窗 + 手机 Approve/Deny/Always Approve
DSH 交互(1.1.6 起支持) DSH 任务完成(turn/end)/ 提问(ask_user_question)/ 审批(approval/request)→ 手机交互
DSH 远程续聊(1.2.0 起支持) 手机发文字 → 直接注入 DSH 正在运行的会话 → 回复推回手机(桌面会话同步可见)
远程续聊(新增能力) 手机向主话题发文字,自动恢复最近会话继续对话
自动更新提醒(1.3.0 起支持) 守护进程周期检查 npm 新版本并推送手机提醒;其他命令发现新版本时终端提示 + 手机推送(1.4.0 起)
开机自启(1.4.0 起支持) a4p setup 默认启动守护进程并注册 Windows 开机自启,登录自动运行

1.1 工作流程

graph LR A[AI 助手
触发事件] --> B{a4phone Hook
拦截并分类事件} B -->|Stop 任务完成| C[系统通知
+ 手机推送] C --> D[推送 AI
最后输出] B -->|提问 / 权限请求| E{交互模式} E -->|外出模式| F[ntfy.sh
推送手机] F --> G[手机点选按钮] G --> H[决策注入
AI 助手] E -->|终端优先| I[终端提问
交互确认] style A fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style B fill:#ebdef0 style C fill:#d5f5e3 style D fill:#d6eaf8 style E fill:#fdebd0 style F fill:#fadbd8 style G fill:#d5f5e3 style H fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style I fill:#d6eaf8

2. 安装

2.1 前置条件

  1. AI 编程助手已安装并可用:Claude Code、Codex 或 DeepSeek Harness(DSH)能在终端正常启动会话。
  2. Node.js 环境:版本 18 或以上。
  3. 手机设备:运行 iOS 或 Android 的智能手机,用于接收通知。
  4. Claude Code / Codex 已登录可用:远程续聊按最近会话的 agent 自动选择续聊方式(claude --resumecodex exec resume 或 DSH 进程内注入),需要对应 CLI 已登录且正常配置(如接入国内大模型也可)。
  5. (可选)DSH 已安装运行:如需 DSH 手机交互,先安装并运行 DeepSeek Harness(~/.dsh/profiles/web 目录存在),a4p setup 检测到后会自动挂载插件;未安装则自动跳过该步骤。

2.2 安装 ntfy App

2.3 安装 a4phone

npm install -g a4phone

2.4 运行安装引导

a4p setup

安装引导自动完成:

  1. 生成独一无二的话题名称(如 a4p-xxxx),写入配置 ~/.a4phone/config.json,无需手动准备。
  2. 注册 Hook:在 ~/.claude/settings.json 自动写入三个 Hook(Stop / AskUserQuestion / PermissionRequest),并同时在 ~/.codex/config.toml 末尾追加 Codex Hook(见第 7 章)。
  3. 挂载 DSH 插件:检测到 DSH 环境(~/.dsh/profiles/web 存在)时,把 a4phone 内置的 dsh-hook 插件挂载到 ~/.dsh/profiles/web/cordis.patch.yml(详见第 8 章)。
  4. 默认启动续聊守护进程:自动运行 a4p listen(后台无窗口),手机续聊开箱即用(1.4.0 起)。
  5. 默认注册开机自启(Windows):登录时自动运行续聊守护进程,无需手动配置(1.4.0 起,详见 5.2 节)。
  6. 显示二维码:终端打印订阅二维码,或粘贴话题内容完成订阅。

运行安装引导示意图

2.5 手机订阅

用手机 ntfy App 扫描终端二维码,或手动添加订阅并输入话题名称(如 a4p-xxxx)完成订阅。

手机订阅示意图

重要:订阅完成后,务必在订阅设置中开启 "即时交付"(详见 9.2 节),否则消息需手动下拉刷新才能收到,提问/权限请求的交互也无法实时弹出。

手机订阅示意图

手机订阅示意图

3. 核心逻辑

  • 拦截:Claude Code 触发事件时,Hook 调用 a4p hook(Codex 为 a4p hook codex)。
  • DSH 拦截:DeepSeek Harness 由 a4phone 内置的 dsh-hook 插件直接监听会话事件(session/eventtools/executeapproval/request),复用同一 a4phone 话题与模式,无需单独的 hook 命令。
  • 推送a4phone 把通知推送到你的专属话题(ntfy.sh/话题名)。
  • 点选:手机 ntfy App 收到带按钮的通知,你点击按钮。
  • 回传:决策通过响应话题(话题名-response)回传。
  • 注入:Hook 收到决策后注入回 AI 助手(提问答案 / 权限 approve-deny)。
  • 续聊:手机向主话题(话题名)发送文字,守护进程 a4p listen 恢复最近会话继续对话,结果推回手机。

a4phone 只使用两个话题分工协作:

话题 用途
话题名 主话题,推送任务完成、提问、权限请求等通知;同时是远程续聊的对话通道
话题名-response 响应话题,手机点按钮决策回传;也可自由文本/编号回复

提问回传方式

  • 按钮点选(选项 ≤ 3 个):手机通知上直接点按钮,ntfy App 自动 POST 回传,无需订阅响应话题
  • 编号回复 / 自由作答(选项 > 3 个自动降级为编号列表,或想自由回答):向响应话题 话题名-response 发送编号(如「3」)或文字;若未订阅响应话题,可浏览器打开 https://ntfy.sh/话题名-response 直接回复(1.4.2 起提问推送自带该浏览器链接提示)。
  • 注意:回复纯数字(如「3」)需要 a4phone ≥ 1.4.1——1.4.1 修复了纯数字被 JSON.parse 误判为 JSON 对象而导致编号回复丢失的问题;按钮模式回按钮 label 即可。

格式说明:提问(PreToolUse 事件)Claude Code 返回 hookEventName: "PreToolUse" + permissionDecision: "allow" + updatedInput.answers(改写工具输入注入答案);Codex 因提问工具 request_user_input 忽略输入里的 answers,改用 permissionDecision: "deny" + permissionDecisionReason 把答案写进阻断原因,让模型直接采用答案继续。权限请求返回 hookEventName: "PermissionRequest" + decision.behavior。两者是不同的事件 schema,a4phone 已正确处理。

3. 核心逻辑示意图

4. 手机查看 AI 最后输出

任务完成后,推送的内容不再只有"任务已完成 + 目录 + AI 最后输出的一段话,一起推送过来,你离开电脑也能看到实际结果。

原理:Claude Code 的 Stop Hook 输入中带有 transcript_path,指向本次会话的 JSONL 会话记录。a4phone 解析该记录,取出最后一条 AI 回复文本(Claude Code 取 assistant 消息的 text 块;Codex 取 response_item 中的 assistant 消息,task_complete 事件仅作文件稳定时的兜底),截断到 1000 字符后随推送发出。

手机收到的任务完成通知形如:

任务已完成
目录: C:\项目目录
会话: 296e77be

AI 最后输出:
已经补充了 .gitignore 中缺失的缓存目录条目,并验证了所有 ?? 标记的未跟踪文件……

会话记录文件可能很大,a4phone 只截取最后一段文本的前 1000 字符,兼顾推送长度限制与可读性。

5. 手机远程续聊

5.1 续聊闭环

远程续聊是整套方案的进阶能力:任务结束后,你不需要回到电脑前,直接在手机向主话题发一条文字,即可恢复最近一次会话(Claude Code / Codex / DSH)继续对话,新的回复会自动推回手机。如果续聊过程中 AI 再次提问或请求权限,仍会推送手机交互,形成完整的远程对话闭环。

graph LR A[手机向主话题
发送文字] --> B[ntfy.sh
消息服务器] B --> C[守护进程
a4p listen] C --> D[恢复最近会话
headless 续聊] D --> E[AI 处理
新请求] E --> F[Hook
拦截结果] F --> G[结果推送
回手机] G --> A style A fill:#fadbd8 style B fill:#fadbd8 style C fill:#ebdef0 style D fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style E fill:#ffecd6 style F fill:#ebdef0 style G fill:#d5f5e3

续聊闭环示意图

原理Stop 事件触发时,a4phone 会把最近会话的 session_id、项目目录和来源 agent 记录到 ~/.a4phone/last.json。续聊时守护进程按最近会话的 agent 自动选择续聊方式:Claude Code 执行 claude --resume <会话ID> --continue -p,Codex 执行 codex exec resume <会话ID> -o <临时文件> -,并把手机发来的文字通过 stdin 管道作为下一条用户消息注入;headless 运行完成后,守护进程把回复推回手机主话题(Codex 用 -o 捕获最后一条回复)。DSH 的续聊方式完全不同——不另起进程,而是通过文件队列把消息交给 dsh web 进程内的插件,直接注入桌面上正在运行的同一个会话(详见 8.3 节),手机消息与 AI 回复都会实时出现在桌面会话里。续聊子进程的 Stop 事件带 A4P_RESUME 标记(DSH 用插件内标记),不会再重复推送"任务已完成"通知,结果统一由守护进程去重推送。

5.2 启动续聊守护进程

a4p listen 会启动一个后台守护进程(无窗口、不占用终端),实时监听主话题,收到手机文字即触发续聊:

a4p listen               # 后台启动守护进程(无窗口)
a4p listen --status      # 查看守护进程运行状态
a4p listen --stop        # 停止守护进程

守护进程启动后终端提示:

续聊守护进程已后台启动(PID: 12345)。
日志: C:\Users\你的用户名\.a4phone\daemon.log

守护进程运行日志写入 ~/.a4phone/daemon.log,手机发文字后日志大致如下:

续聊守护进程已启动,监听: https://ntfy.sh/a4p-xxxx/json
提示:手机向话题 a4p-xxxx 发送文字即可与当前会话交流。
收到续聊请求:帮我总结一下刚才的改动
消息已处理(resume 续聊,退出码 0),结果已推送手机。

守护进程断线会自动重连;续聊请求串行处理,避免并发占用同一会话。

开机常驻方式

  • Windows(推荐,1.4.0 起自动)a4p setup 默认注册开机自启(在启动文件夹写入隐藏脚本,登录时自动运行守护进程,无需管理员权限);可用 a4p autostart 查看状态、a4p autostart --on / --off 手动开关,a4p uninstall 自动移除。
  • WSL / Linuxa4p setup 不会自动注册,请用 tmuxsystemd 常驻。

5.3 手机向主话题发送续聊消息

续聊不需要单独的续聊话题——你通过二维码订阅的主话题 话题名 就是对话通道

  1. 手机 ntfy App 已订阅主话题 话题名a4p setup 生成,扫描二维码即可,无需额外订阅)。
  2. 在该话题页面输入你要追加的内容并发送(话题名称可用 a4p last 或查看 ~/.a4phone/config.json 确认)。
  3. 守护进程收到后自动恢复最近会话继续对话,结果推送回主话题。

5.4 手动续聊

未启动守护进程时,也可以在电脑上手动续聊:

a4p resume 帮我总结一下刚才的改动

手动续聊与手机续聊走同一套逻辑,适合临时调试或不便常驻守护进程的场景。

5.5 查看最近会话

a4p last

输出最近一次任务完成时记录的会话 ID、项目目录、来源与时间,用于确认续聊的目标会话。

5.6 续聊注意事项

  • 支持 Claude Code / Codex / DSH 会话:守护进程按最近会话的 agent 自动选择续聊方式(Claude Code 用 claude --resume,Codex 用 codex exec resume,DSH 用进程内插件注入,见 8.3 节)。Codex 续聊需 Codex CLI 已登录、Hook 已信任;DSH 续聊需 dsh web 正在运行且已挂载新版插件。
  • Claude Code 会话独占:一个会话同一时间只能被一个进程占用,续聊前请先结束终端里仍在运行的原会话,否则会冲突。Codex 会话被窗口占用时(thread-store conflict),a4phone 会自动把它 fork 成新线程续聊——不需要关闭原窗口,原会话原样保留,手机对话在 fork 上继续。
  • 自动切换外出模式:续聊期间守护进程会自动临时切换为外出模式(手机优先),结束后恢复原模式,保证续聊回合中的提问/权限请求走手机通道。
  • 积压合并(1.2.1 起):一轮续聊最长可达 resumeTimeout(默认 30 分钟),期间手机连续发来的多条消息会自动合并为一个批次一次性续聊(不再逐条排队、每条一个独立轮次),保证手机内容一定能送达 AI;积压批次持久化到 ~/.a4phone/pending-batch.json,守护进程重启/崩溃后自动恢复,不丢消息。
  • 续聊耗时:默认超时 30 分钟,可通过配置 ~/.a4phone/config.jsonresumeTimeout(秒)调整。
  • 话题安全:ntfy.sh 公共话题可被知晓话题名的人读写,续聊文字会作为提示词喂给 AI,重要场景建议改用自建 ntfy 服务或访问令牌。

6. 模式切换

a4phone 内置两种交互模式,切换命令简短、即时生效:

a4p out        # 外出模式:提问/权限请求优先推送手机,超时终端兜底
a4p home       # 终端优先模式(默认):提问/权限请求直接走终端,手机不参与
a4p status     # 查看当前模式

机制说明:Claude Code 的提问/权限请求只能接受一个答案来源——Hook 注入了手机答案,终端就不会弹出;反之终端弹出。两者无法同时生效,因此用模式切换。切换即时生效,无需重启会话。

7. Codex 配置

a4p setup同时自动配置 Claude Code 和 Codex: - Claude Code:写入 ~/.claude/settings.json - Codex:在 ~/.codex/config.toml 末尾追加 Hook 配置(自动保留你已有的模型/提供商等设置)

若需手动配置或核对,a4p setup 写入的 Codex 配置结构如下(启用项在 [features] 表内,Hook 定义为根级别数组):

[features]
hooks = true

# 任务完成
[[hooks.Stop]]
[[hooks.Stop.hooks]]
type = "command"
command = "a4p hook codex"

# AI 提问
[[hooks.PreToolUse]]
matcher = "request_user_input"
[[hooks.PreToolUse.hooks]]
type = "command"
command = "a4p hook codex"

# 权限请求
[[hooks.PermissionRequest]]
[[hooks.PermissionRequest.hooks]]
type = "command"
command = "a4p hook codex"

注意:Codex 的 Hook 启用项必须放在 [features] 表内([features] hooks = true),不能写成根级别的裸 hooks = true,否则会与 [[hooks.*]] 冲突导致 TOML 解析错误。

提问工具名:Codex 的提问工具叫 request_user_input(不是 AskUserQuestion),PreToolUse 的 matcher 必须匹配该名称 Hook 才会触发。a4p setup 会把旧配置中的错误 matcher 就地修正为 request_user_input

答案注入方式不同:Codex 端无法像 Claude Code 那样用 updatedInput 注入答案(其 handler 忽略输入里的 answers),a4phone 采用"阻断工具调用、把手机答案写进阻断原因"的方式(permissionDecision: "deny" + permissionDecisionReason),让模型看到答案后直接采用继续。

信任 Hook:Codex 会话中需运行 /hooks 并手动信任(trust)新 Hook。

8. DSH(DeepSeek Harness)配置

从 a4phone 1.1.6 起,a4p setup 检测到 DSH 环境(~/.dsh/profiles/web 目录存在)时,会把本包内置的 dsh-hook 插件自动挂载到 web profile 的 cordis.patch.yml,让 DeepSeek Harness 同样具备手机远程交互。插件复用与 Claude Code / Codex 相同的 a4phone 话题与模式,手机端无需额外订阅。

Hook DSH 触发事件 手机交互(外出模式)
任务完成 turn/endreason.kind === 'completed' 系统通知 + 手机推送(含 AI 最后输出)
提问 ask_user_question 工具调用 手机点选选项 / 文字自由作答
权限请求 approval/request 手机 Approve / Deny

8.1 插件挂载机制

  • 插件位于 a4phone 包的 dsh/ 目录(Cordis 插件,监听 DSH 的 session/eventtools/executeapproval/request 事件),a4p setup 以 insert 形式写入 patch,幂等可重复执行。
  • 运行原理:Cordis 是 DSH 的底层插件框架,拉丁语意为「心脏」(词根 cor/cordis,与英语 cordial「衷心的」同源),DSH 本身即构建于其上。dsh-hook 是标准 Cordis 插件(导出 name / inject / apply 并声明对 @deepseek-ai/cordis 的依赖),通过 ctx.on 监听会话事件流、inject 注入 toolsapproval 服务,再以 waterfall 拦截工具执行与审批请求——相当于挂在 DSH 的「心脏」上感知会话并完成手机交互。
  • 若检测到旧版手动挂载(指向 C:\ProgramMine\dsh-hookid: dsh-hook),a4p setup 会自动替换为本包路径。
  • cordis.patch.yml 被 DSH 热监视(watchUserPatches),挂载即时生效,无需重启;若插件代码有更新,可重启 dsh web 使新逻辑生效。
  • 目前仅挂载到 web profile;a4p uninstall 会同时移除该挂载。

8.2 模式、日志与限制

  • 模式切换复用同一套a4p out(手机优先)/ a4p home(终端优先)。
  • Hook 日志:写入 ~/.a4phone/dsh-logs/,按事件分类为 task-complete.jsonlquestion-asked.jsonlpermission-request.jsonl
  • 远程续聊(1.2.0 起支持):手机发文字继续 DSH 会话已实现,见 8.3 节

机制说明:DSH 插件通过 Cordis 的 waterfall 机制拦截工具与审批——外出模式下返回自定义结果即替换原生提问/审批,把手机答案注入;返回 null 则回退 DSH 原生交互。行为与 Claude Code / Codex 一致。

8.3 DSH 远程续聊

从 a4phone 1.2.0 起,DSH 同样支持手机远程续聊。与其他 Agent 的续聊方式不同,DSH 续聊不另起进程,而是让 dsh web 进程内的插件直接把手机消息注入桌面上正在运行的同一个会话——因此手机消息与 AI 回复都会实时出现在桌面端会话里,手机与桌面看到同一段对话。

DSH 远程续聊示意图

工作流程

graph LR A[手机向主话题
发送文字] --> B[ntfy.sh
消息服务器] B --> C[守护进程
a4p listen] C --> D[写入文件队列
~/.a4phone/dsh-jobs/req-*.json] D --> E[dsh web 进程内插件
agent.followup 注入会话] E --> F[AI 处理
新请求] F --> G[提取回复
写入 resp-*.json] G --> H[守护进程取回
推送手机] H --> A style A fill:#fadbd8 style B fill:#fadbd8 style C fill:#ebdef0 style D fill:#d5f5e3 style E fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style F fill:#ffecd6 style G fill:#d5f5e3 style H fill:#d5f5e3

机制细节

  1. 记录最近会话:插件在每次顶层会话 turn/end(completed)时写入 ~/.a4phone/last.jsonagent: "DSH"),与 Claude Code / Codex 的 Stop Hook 记录方式一致,a4p resume / a4p listen 据此路由到 DSH 会话。
  2. 文件队列协议:a4p 把手机消息原子写入 ~/.a4phone/dsh-jobs/req-<id>.json;插件每秒扫描,处理后写 resp-<id>.json,a4p 轮询取回并推回手机;插件同时刷新 ~/.a4phone/dsh-heartbeat.json 心跳,a4p 据此快速判断 dsh web 是否在运行——未运行会立即给出提示,而不是干等 30 分钟超时。
  3. 注入桌面会话:插件用 DSH 的 agent.followup() 把手机文字作为普通 user/message 写入当前 live 会话(目标会话优先取 last.json 记录的会话,兜底最近顶层会话),await agent.whenIdle() 等待轮次结束,提取最后一条 assistant 文本回推。
  4. 无会话锁冲突:不另起进程,不存在 Claude Code --resume 式的独占锁问题;续聊轮次内若触发提问/审批,仍走手机交互,形成完整闭环。
  5. 去重推送:续聊轮次的"任务完成"推送自动跳过(回复已由 a4p 推回手机)。

使用前提

  • dsh web 正在运行,且已挂载新版 dsh-hook 插件(a4p setup 自动挂载;插件代码更新后需重启 dsh web)。
  • 桌面端至少完成过一轮会话(触发插件记录最近会话,a4p last 显示来源为 DSH)。
  • 守护进程使用 1.2.0 及以上版本(推荐 1.4.2+;升级后需 a4p listen --stop && a4p listen 重启守护进程)。

9. 生效步骤与注意事项

9.1 生效步骤

  1. 重启会话:Hook 在会话启动时加载并缓存,配置后需完全退出并重新启动 Claude Code / Codex。
  2. Codex 信任 Hook:Codex 需在会话中运行 /hooks 命令,然后手动信任(trust)新安装的 Hook 才会生效。Claude Code 无此步骤。
  3. DSH 插件热生效cordis.patch.yml 被 DSH 热监视,挂载即时生效,无需重启 dsh web(插件代码有更新时除外)。
  4. 模式切换即时生效a4p out/home 切换的是模式文件,Hook 每次触发时实时读取,无需重启会话。

9.2 手机收不到通知

第一步:开启订阅的"即时交付"

ntfy App 对每个订阅默认不启用实时推送,导致消息要手动下拉刷新才能看到。开启后消息即可实时到达:

  1. 打开 ntfy App,进入会话列表
  2. 点击右上角三个点订阅设置
  3. 开启 "即时交付"

开启后,锁屏状态下任务完成通知和提问/权限请求的交互按钮都能实时弹出,无需手动刷新。此设置是整套方案能否"即时交互"的关键,务必开启。主话题 话题名(同时也是续聊对话通道)同样需要开启"即时交付",续聊回复才能实时到达手机。

第二步:确认配置与订阅无误

  1. 确认手机 ntfy App 已正确订阅了话题(与 a4p setup 生成的话题名称一致)。
  2. 确认手机网络连接正常(ntfy.sh 需要互联网访问)。
  3. 检查手机通知权限是否对 ntfy App 开放。
  4. 尝试在浏览器访问 https://ntfy.sh/你的话题名称,看是否能看到消息记录。

第三步:排查 App 长连接断开

如果已开启"即时交付"仍收不到,且浏览器能显示消息、但 App 里看不到,说明推送已成功发到服务器,问题在 App 的常驻连接被系统中断。

ntfy App 靠一条常驻连接接收新消息。当手机锁屏、App 被切到后台、或网络在 Wi-Fi 与流量间切换时,这条连接可能被系统掐断,新消息就都收不到。

解决办法:

  1. 打开 ntfy App,进入对应话题页面,下拉刷新强制重新建立连接。刷新后若出现之前未收到的消息,说明此前连接断了。
  2. 让 App 保持活跃连接,防止系统杀后台: - Android:设置 → 应用 → ntfy → 电池/后台限制 → 设为"不受限制"。 - iOS:确保不允许 App 被系统挂起,并保持通知权限开启。
  3. 部分国产手机(如华为)有额外的"应用启动管理",需手动关闭"自动管理"并开启自启动/后台活动;必要时可用 adb 将应用加入系统休眠白名单:adb shell dumpsys deviceidle whitelist +io.heckel.ntfy
  4. 若订阅了多个话题,确认没有残留旧的(或错误的)话题订阅导致收错频道。

国内网络提示:ntfy.sh 为国外服务,国内网络下长连接可能不稳定,表现为消息需手动刷新才能看到。若遇到此情况,可结合"即时交付"与刷新使用,或在网络稳定时使用。

9.3 Hook 配置后没有生效

  1. 输入 /hooks 查看 Hook 是否被加载(Codex 需确认已信任)。
  2. 确认 Codex 已设置 hooks = true
  3. 确认 matcher 匹配正确(如实际触发的是 Bash 权限请求,需匹配 Bash 而非 AskUserQuestion)。
  4. DSH 侧确认 ~/.dsh/profiles/web/cordis.patch.yml 中存在 id: dsh-hook 的 insert 块(a4p setup 未检测到 DSH 环境时不会写入)。

9.4 ntfy.sh 限流提示

ntfy.sh 免费托管服务有发布速率/消息保留限制,短时间高频测试可能触发 limited 提示,建议降低推送频率(续聊结果已去重推送,不再重复通知)。

9.5 自动更新提醒(1.3.0 起支持)

a4phone 默认开启版本更新提醒,发现 npm 新版本时主动通知你,避免错过更新:

  • 检查机制:守护进程(a4p listen)启动时及每 updateIntervalHours 小时(默认 6 小时)检查一次 npm registry(npmmirror 镜像优先、官方 registry 兜底),查询失败静默跳过,不影响正常功能。
  • 提醒方式:发现新版本 → 守护进程推送手机提醒(标题 a4phone,含升级命令);其他命令(a4p status / a4p resume 等)发现新版本时终端提示 + 手机推送(1.4.0 起)。
  • 去重与限频:同一新版本只提醒一次(缓存 ~/.a4phone/update-cache.json);升级后立即检查(1.4.2 起,避免旧缓存把升级后的首次检查推迟一整个间隔)。
  • 关闭 / 调整:在 ~/.a4phone/config.json"checkUpdates": false 关闭;"updateIntervalHours": 12 调整检查间隔(小时)。
  • 前提:需要 a4phone ≥ 1.3.0 且守护进程/命令运行新版本代码(升级后 a4p listen --stop && a4p listen 重启守护进程)。

10. 测试验证

# 发送测试通知到手机
a4p test

# 测试手机交互:切换到外出模式,触发一次提问或权限请求
a4p out
# ...触发 AskUserQuestion 提问或 Bash 权限请求,观察手机是否收到带按钮的通知...
a4p home

# 触发一次任务完成(Stop)事件,验证推送含 AI 最后输出
codex -c "print('test')"

# 测试远程续聊
# 1. 启动守护进程:a4p listen(后台无窗口运行)
# 2. 手机向主话题发送文字,验证恢复会话并回推结果
# 3. 或电脑端手动续聊:a4p resume 帮我总结一下刚才的改动
# 4. 查看最近会话:a4p last

# 测试 DSH 交互(需已安装并运行 DSH)
# 1. 确认插件已挂载:cat ~/.dsh/profiles/web/cordis.patch.yml(应含 id: dsh-hook)
# 2. 外出模式下在 DSH 会话触发提问/审批,观察手机是否收到带按钮的通知
# 3. 完成一个任务回合,验证手机收到任务完成通知(含 AI 最后输出)
# 4. 查看 DSH Hook 日志:~/.a4phone/dsh-logs/

# 测试 DSH 远程续聊(需 a4phone ≥ 1.2.0,dsh web 正在运行)
# 1. 确认最近会话为 DSH:a4p last(来源显示 DSH)
# 2. 手机向主话题发送文字(如"我们刚才在做什么?")
# 3. 预期:桌面端会话里实时出现手机消息与 AI 回复;手机收到 DSH 回复推送
# 4. 守护进程日志:~/.a4phone/daemon.log(显示"DSH 续聊请求已提交")

# 测试多选项编号回复(选项 > 3 个,a4p out 模式下触发 5 选项提问)
# 1. 手机向响应话题发送编号(如 3),AI 应收到对应选项
# 2. 或浏览器打开 https://ntfy.sh/话题名-response 直接回复(无需订阅响应话题)

# 测试自动更新提醒(a4phone ≥ 1.3.0)
# 1. 确认 config.json 的 checkUpdates 为 true(默认开启)
# 2. 守护进程运行中,npm 发布新版本后手机应收到"a4phone 新版本"提醒

# 测试开机自启(a4phone ≥ 1.4.0,Windows)
# 1. a4p autostart          # 查看状态(setup 后应为已开启)
# 2. a4p autostart --off / --on   # 关闭 / 重新开启

续聊会真实调用 AI 助手(Claude Code / Codex / DSH)消耗一次回复,请确认已配置好后再测试。

11. 常用命令速查

命令 作用
a4p setup 安装引导:生成话题、注册 Hook、挂载 DSH 插件、启动守护进程、注册开机自启、显示二维码
a4p out 外出模式(手机优先)
a4p home 终端优先模式(默认)
a4p status 查看当前模式
a4p listen 后台启动续聊守护进程(监听主话题)
a4p listen --status 查看守护进程运行状态
a4p listen --stop 停止守护进程
a4p autostart 查看开机自启状态(--on 开启 / --off 关闭)
a4p resume 手动续聊最近会话:a4p resume 要追加的内容
a4p last 查看最近会话记录
a4p test 发送测试通知
a4p uninstall 卸载:移除 Claude Code / Codex / DSH Hook、开机自启和配置
a4p --version 查看 a4phone 版本号

重新安装:卸载后可再次运行 a4p setup,会生成新话题,需在手机重新订阅。