Skip to content

Repository files navigation

afk logo

afk

afk = Away From Keyboard(离开键盘)

一个 OpenCode 插件:在终端输入 /afk 离开屏幕后,运行中的智能体遇到决策点时会把问题通过邮件发给你;你在任何地方回复邮件,会话便自动唤醒继续——无需回到终端。

License: MIT Node.js PRs Welcome

English · 简体中文

功能

  • 邮件决策:智能体提问,你在任何地方作答,无需终端
  • 从邮件新建会话:回复任意 afk 邮件、正文以 /new 任务 开头,即可在当前会话所在的项目目录开出一个全新会话派发任务
  • 结论通知:智能体达成关键结论/完成任务时,通过 notify_user 单向邮件同步给你,不打断其工作;你回复该邮件同样能唤醒会话
  • 停顿自动通知:监听 opencode 的 turn-end 事件,agent 停下一段时间后自动邮件告知当前进展——不再依赖它记得发通知
  • /afk 模式开关:只有在你离开屏幕之后(/back 可返回)才发送邮件
  • 零轮询:IMAP IDLE 推送加一次性补扫,绝无定时轮询器
  • 推送投递:SSE 广播加按实例的属主自检(多实例安全)
  • 消息不丢失:持久化的 pending-store 加注入后确认(崩溃安全)
  • 国际化:默认英文,可通过 config.messages 覆盖为任意语言
  • 数据而非指令:每条注入的回复都带有提示注入防护
  • 一键安装器node install.js 负责复制、安装、注册并生成配置骨架
  • npm 分发opencode-afk 发布到 npm,opencode 重启即自动跟进 @latest(GitHub Actions 自动发布)

面向智能体的使用说明请参阅 AGENTS.mdrequest_decision 契约、「提问后暂停」规则、notify_user 结论通知与 /new 新建会话)。


安装

方式一:npm(推荐)

~/.config/opencode/opencode.jsoncplugin 数组中加入一行:

"plugin": [
  "opencode-afk@latest"
]

重启 opencode 时,它会从 npm 自动安装 opencode-afk@latest 到插件目录。首次安装后,创建稳定的用户级配置(类似 oh-my-openagent.jsonc):

mkdir -p ~/.config/opencode
cp <插件目录>/config.example.json ~/.config/opencode/afk.json

编辑 ~/.config/opencode/afk.json 填入凭据,重启即可使用。配置放在 opencode 配置目录而非插件目录,这样每次 @latest 更新重装插件后配置依然存活

更新:维护者发布新版本后,你只需重启 opencode@latest 即自动跟进——无需手动拷贝或运行安装器,配置也不会被刷新目录覆盖。

运行时状态与迁移:运行时状态文件(daemon-secretmode.jsonlast-uid.jsonpending.jsonjournal.json)现存放于 XDG 状态目录 ~/.local/state/opencode/afk/(可用 XDG_STATE_HOME 覆盖),因此每次 opencode-afk@latest 重装插件后它们依然存活。首次运行时会做一次性迁移:当旧的 <插件目录>/store/*<插件目录>/core/journal.json 仍存在时,将其复制进状态目录(绝不覆盖已有文件)。迁移是尽力而为的:只有当旧文件仍在时才能恢复(源码安装 / 原地升级);若在全新的 npm @latest 目录中旧 store 已被清除,则此前状态无法找回,但从今往后不会再丢失。升级后请重启 opencode。

复制粘贴式安装(直接交给你的 LLM)

将以下内容粘贴给任意编码智能体,让它照着在你的 opencode 环境中完成安装:

Install and configure the afk plugin by following the instructions here:
https://raw.githubusercontent.com/7emotions/afk/main/INSTALLATION.md

方式二:源码安装 / 本地开发(install.js)

git clone https://github.com/7emotions/afk
cd afk
node install.js
# then edit ~/.config/opencode/plugins/afk/config.json and fill credentials

安装器会执行以下操作:

  1. 将源码复制到 ~/.config/opencode/plugins/afk/(跳过 node_modules.git 以及所有密钥/运行时文件);
  2. 运行 npm install --omit=dev
  3. config.example.json 生成 config.json(绝不覆盖已有的);
  4. ~/.config/opencode/opencode.jsonc 中注册插件(采用保留注释的文本插入方式,绝不对 JSONC 文件执行 JSON.parse);
  5. command/afk.mdcommand/back.md 复制到 ~/.config/opencode/command/

随后编辑 config.json(已被 git 忽略),填入 imap.user / imap.password / smtp.user / smtp.password / recipient,然后重新加载 opencode。

手动步骤

与安装器等价的手动步骤:

  1. 将此目录复制到 ~/.config/opencode/plugins/afk/
  2. cd 进入该目录并运行 npm install
  3. config.example.json 创建 config.json 并填写凭据。
  4. 将插件路径添加到 ~/.config/opencode/opencode.jsonc 中的 plugin 数组。
  5. command/afk.mdcommand/back.md 复制到 ~/.config/opencode/command/
  6. 重新加载 opencode。

该插件依赖 @opencode-ai/plugin@opencode-ai/sdkimapflowmailparsernodemailer

发布(维护者)

npm 包名为 opencode-afk。发布流程:本地打版本号并推送 tag,GitHub Actions(.github/workflows/publish.yml)自动跑单测并发布到 npm:

npm version patch   # 0.1.0 → 0.1.1:同步 package.json / package-lock.json / git tag
git push --tags

用户侧无需任何操作——重启 opencode 即拉取 @latest


快速上手

  1. 安装插件并编写配置:npm 方式在 ~/.config/opencode/afk.json(复制 config.example.json,更新后仍然存活);源码方式在插件目录的 config.json。至少需要有效的 IMAP 与 SMTP 凭据(QQ 授权码即可作为密码)以及一个 recipient
  2. 重新加载 opencode。插件会确保其守护进程正在运行并完成加载。
  3. 在会话中,智能体可以调用 request_decision(subject, question, { context, options, recommendation })。该工具会把问题以邮件形式发出,并返回 Decision requested — pause and end this turn; wait for the reply to be injected
  4. 回复邮件。守护进程检测到回复,属主实例将其注入,会话随即继续。
  5. (可选)你离开屏幕后,智能体在完成任务或有关键结论时,可以调用 notify_user(subject, message) 单向邮件通知你,且不暂停其工作;你回复该通知邮件同样会作为反馈注入回会话。
# run the unit test suite (no network, no real mailbox)
npm test

从邮件新建会话(/new

想趁离屏时派一个全新任务给当前项目?回复任意一封 afk 邮件(决策或通知均可),正文以 /new 开头:

/new 修复登录页在 Safari 上的样式错位

效果:

  • 不注入你回复的那个会话——而是在那个会话所在的项目目录里创建一个全新的会话 B,以 /new 后的文字作为它的首个任务;
  • 同一目录若开着多个 opencode 实例,先抢到广播者创建(多实例不重复);
  • 你会收到一封带新会话 token 的确认邮件,回复它就相当于直接跟新会话 B 对话
  • 任务正文按「数据而非指令」框架注入,绝不当作可执行指令;仅 allowList 白名单发件人可触发;
  • 新会话后续的决策/结论邮件自动走现有 [omo:token] 路由,无需额外配置。

一个裸 /new(无任务文字)会被当作普通回复注入原会话。


邮件模式(/afk/back

默认情况下,request_decisionnotify_user 都拒绝向用户发送邮件。只有当用户明确离开屏幕时才允许发送邮件,这由守护进程持久化存储于 mode.json(已被 git 忽略)中的单一 GLOBAL 模式标志("on"/"off")控制。该标志按设计是全局的:无论运行多少个会话/实例,用户要么在屏幕前,要么不在。

模式 含义 邮件工具行为
"off"(默认) 用户在屏幕前 拒绝:request_decision 返回模式关闭消息(改用内置 question 工具);notify_user 同样拒绝(直接在对话中陈述结论即可)。不发邮件。
"on" 用户已离开(/afk 照常向用户发送邮件。

用户通过两个命令切换该模式:

  • /afk:调用 set_email_mode(mode: "on")(打开邮件模式)。
  • /back:调用 set_email_mode(mode: "off")(关闭邮件模式)。

当模式关闭而智能体需要决策时,它必须改用 opencode 内置的 question 工具在对话中提问,而不是发送邮件;notify_user 模式关闭时则在对话中直接陈述结论。

待办检查点(Todo checkpoint)。request_decision 将要以邮件形式发出(模式开启)时,智能体必须先把待办列表写入检查点(写进消息正文,然后清空待办),以免挂起等待的用户回复被 todo-continuation 覆盖。回复被注入后,智能体再依据检查点重建待办列表。(notify_user 不暂停,无需检查点。)

注册命令

OpenCode 会从全局命令目录 ~/.config/opencode/command(s)/<name>.md(文件名即命令名)中的 markdown 文件加载自定义命令。下面这两个文件就实现了上述行为:

  • ~/.config/opencode/command/afk.md:打开邮件模式。
  • ~/.config/opencode/command/back.md:关闭邮件模式。

(如果你更愿意用配置而不是 markdown 文件,下面是等价的 opencode.jsonc 形式:)

{
  "command": {
    "afk": { "template": "Call the set_email_mode tool with mode \"on\", then confirm briefly.", "description": "Turn email mode ON" },
    "back": { "template": "Call the set_email_mode tool with mode \"off\", then confirm briefly.", "description": "Turn email mode OFF" }
  }
}

模式状态保存在守护进程中(mode.json),通过 GET /mode / POST /mode 提供访问,并以 SSE mode 事件推送给每个已连接的实例(连接时还会推送一次初始 mode 事件),因此在某个会话中做出的更改,所有实例都会立刻看到。


配置

插件(index.js)与守护进程(daemon.js)按以下优先级解析配置文件(首个命中生效):AFK_CONFIG 环境变量 → ~/.config/opencode/afk.json(稳定用户级配置,npm 更新后仍存活)→ 插件目录下的 config.json(旧路径兜底,源码安装/测试用)。完整、以结构本身作为注释的示例请参阅 config.example.json(其中不含任何真实密钥)。

config.json 字段

字段 含义
imap.host / imap.port / imap.secure 用于读取回复的 IMAP 服务器(默认 imap.qq.com:993secure:true
imap.user / imap.password IMAP 凭据(密码使用 QQ 授权码)
smtp.host / smtp.port / smtp.secure 用于发送决策邮件的 SMTP 服务器(默认 smtp.qq.com:465
smtp.user / smtp.password SMTP 凭据
recipient 决策邮件的收件人;默认取 smtp.user
allowList 允许注入其回复的发件人地址 —— prompt 注入防护。默认 [smtp.user](仅你自己的邮箱)
folder 要监听的邮箱文件夹(默认 INBOX
tuning 可选的运行行为/时序覆盖(见下文)
messages 可选的用户/智能体可见文案本地化(见下文)

安全

  • 发件人白名单request_decision 将问题邮件发送给 recipient;只有来自 allowList(默认:你自己的 smtp.user)地址的回复才会被注入会话。即使陌生人复制了 [omo:<sessionID>] 主题令牌,也无法注入内容 —— 除非其地址在白名单中,否则回复会被忽略。如需添加其他地址(例如第二个个人邮箱),请列出它们:"allowList": ["me@qq.com", "me@outlook.com"]
  • 数据而非指令 — 每条注入的回复都被标注为数据,绝不是可执行的指令(prompt 注入防护)。

tuning(可选,当前所列各值即默认值)

每个字段都是可选的,缺省时采用表中所列默认值;省略整个块即可保持现有行为不变。

字段 默认值 所替代的源码常量
tuning.claimTtlMs 60000 pending-store.js 中的 CLAIM_TTL_MS:一条 claim 在另一实例可夺取之前保持权威的时间
tuning.reconnectBaseMs 1000 subscribe.js 中的 DEFAULT_RECONNECT_BASE_MS:SSE 重连退避起始值
tuning.reconnectMaxMs 30000 subscribe.js 中的 DEFAULT_RECONNECT_MAX_MS:SSE 重连退避上限
tuning.idleRenewMs 1500000 watcher.js 中的 IDLE_RENEW_MS(25 分钟):在服务器断开前每隔多久重新发起一次 IDLE
tuning.autoIdleDelayMs 1000 watcher.js 中的 AUTO_IDLE_DELAY_MS:imapflow 自动启动 IDLE 前的静默期
tuning.backoffInitialMs 1000 watcher.js 中的 BACKOFF_INITIAL_MS:IMAP 重连退避起始值
tuning.backoffMaxMs 60000 watcher.js 中的 BACKOFF_MAX_MS:IMAP 重连退避上限
tuning.pauseNotifyCooldownMs 60000 pause-notify.js 中的 DEFAULT_COOLDOWN_MS:turn 结束后静默多久判定「一波工作收尾」并邮件通知

这些值由 config.tuning 注入到各消费模块(daemon.jsindex.js),各模块自身从不读取配置文件。

messages(可选,默认为英文)

所有用户/智能体可见的文案都通过 messages.js 表进行本地化。默认值为英文;在 config.messages 下覆盖任意键即可实现本地化(采用深合并,未提及的键保留英文默认值):

{
  "messages": {
    "decisionBody": {
      "context": "Context:",
      "question": "Decision:",
      "options": "Options:",
      "recommendation": "Recommendation:",
      "replyInstruction": "Reply to this email with your answer"
    },
    "notifyBody": {
      "replyHint": "Reply to this email to send feedback to the running session."
    },
    "newSessionBody": {
      "createdSubject": "new session created",
      "createdIntro": "A new opencode session was created from your /new email",
      "replyHint": "Reply to this email to talk to the new session directly."
    },
    "pauseNotifyBody": {
      "pauseSubject": "paused after a working burst — here's where things stand",
      "intro": "This session finished a working burst and is now idle",
      "replyHint": "Reply to this email to tell the agent to continue."
    },
    "tool": {
      "requested": "Decision requested — pause and end this turn; wait for the reply to be injected",
      "alreadyPending": "A decision is already pending — wait for the reply",
      "mainSessionOnly": "Call from the main session only",
      "modeOff": "Email mode is off — the human is at the screen. Use the question tool to ask in the conversation instead.",
      "notifyModeOff": "Email mode is off — the human is at the screen. State the conclusion in the conversation instead of emailing.",
      "modeOn": "Email mode is ON — the human has left the screen; request_decision will email them.",
      "modeDisabled": "Email mode is OFF — request_decision will use the question tool instead.",
      "notified": "Notification emailed — continue working; the human will read it when back"
    }
  }
}

注意:inject.jsbuildPayload)中的 DATA-NOT-INSTRUCTION 框架属于安全框架而非显示文案,因此刻意列入上表,其默认行为保持不变。

环境变量

所有连接相关字段都可以通过环境变量覆盖(优先级高于 config.json):

变量 含义
AFK_CONFIG 配置文件路径(最高优先级;默认链见上:未设置时读 ~/.config/opencode/afk.json,否则插件目录 config.json
AFK_IMAP_HOST / AFK_IMAP_PORT / AFK_IMAP_USER / AFK_IMAP_PASSWORD IMAP 覆盖
AFK_SMTP_HOST / AFK_SMTP_PORT / AFK_SMTP_USER / AFK_SMTP_PASSWORD SMTP 覆盖
AFK_RECIPIENT 收件人覆盖
AFK_JOURNAL journal 文件路径(已处理 UID 去重)
AFK_LAST_UID UID 游标文件路径(检测状态)
AFK_PENDING pending-store 文件路径(持久化投递)
AFK_MODE 模式存储文件路径(GLOBAL 邮件模式,mode.json
AFK_DAEMON_SECRET 守护进程共享密钥文件路径(守护进程 HTTP 鉴权 + 路由令牌签名);默认 $XDG_STATE_HOME/opencode/afk/daemon-secret
AFK_DAEMON_URL 守护进程基础 URL(默认 http://127.0.0.1:4100
AFK_DAEMON_HOST / AFK_DAEMON_PORT 守护进程绑定主机/端口(默认 127.0.0.1 / 4100
AFK_DEBUG 设为 1/true 以启用调试日志

密码绝不会被记录(序列化配置时以 *** 打码)。


工作原理:单一监听守护进程 + PUSH 投递

一台机器上只运行一个守护进程daemon.js),它持有共享邮箱唯一的 IMAP IDLE 监听器。每个 opencode 实例只加载轻量客户端插件index.js),该插件会确保守护进程处于运行状态,并向其建立一条 SSE 流。守护进程解析回复、持久化保存,再通过 SSE 推送给实例;每个实例自检属主、抢占 claim,并在进程内完成注入。

flowchart TB
    subgraph instances["opencode instances — thin client plugins"]
        A["instance A<br/>ensure daemon · SSE subscriber · request_decision"]
        B["instance B<br/>ensure daemon · SSE subscriber · request_decision"]
        dots["... N instances"]
    end

    subgraph daemon_box["afk DAEMON — one process (atomic single-instance)"]
        D["HTTP endpoints<br/>/health · /events · /claim · /ack · /pending · /register · /mode"]
        W["the only IMAP IDLE watcher<br/>+ catch-up scan"]
        P["pending-store<br/>durable replies (pending.json)"]
        M["mode-store<br/>durable global mode (mode.json)"]
    end

    A --> D
    B --> D
    D --> W
    D --> P
    D --> M
    D -. SSE push .-> A
    D -. SSE push .-> B
Loading

守护进程绑定固定端口(默认 4100,可用 AFK_DAEMON_PORT 覆盖)。绑定即是原子的单实例锁:若两个实例同时启动守护进程,只有一个能赢得 bind,落败者会安静退出(EADDRINUSE)。插件探测 /health 无响应时,会启动一个分离的守护进程并轮询直至其健康就绪。

完整流程

sequenceDiagram
    participant Agent
    participant Plugin
    participant Daemon
    participant Human

    Agent->>Plugin: request_decision(subject, question)
    Plugin->>Daemon: GET /mode
    alt mode is "off"
        Plugin-->>Agent: refuse → "use the question tool"
    else mode is "on"
        Plugin->>Daemon: POST /register {rootSessionID}
        Plugin->>Human: SMTP "[omo:rootSessionID] subject"
        Note over Agent: STOPS and ends its turn (pauses)
        Human->>Daemon: reply email (reply prefix / In-Reply-To)
        Daemon->>Daemon: IMAP IDLE push → scan UID > cursor → parse → persist pending.json
        Note over Daemon: mail stays UNSEEN, no journal yet
        Daemon-->>Plugin: SSE delivery {uid, sessionID, body, from}
        Plugin->>Plugin: self-check ownership → POST /claim
        Plugin->>Agent: inject reply (data, not instruction)
        Plugin->>Daemon: POST /ack
        Daemon->>Daemon: markSeen + journal → remove pending → release reservation
        Agent->>Agent: wakes with the answer and continues
    end
Loading

P0 修复:消息不丢失

该设计会先把解析出的回复持久化到可持久化的 pending-storepending.json,已被 git 忽略),然后才做其他任何事。\Seen 标记加 journal 确认只发生在 /ack 处理器中,即在属主实例完成回复注入之后。若守护进程在持久化与确认之间崩溃,邮件服务器上的回复仍处于 UNSEEN 状态,且 pending.json 中也仍保留该回复;守护进程重启时会重新加载它,并在下一个实例连接时重新广播。

投递语义为至少一次,而非恰好一次(注入并非幂等):在注入之后、确认之前发生崩溃会重新广播该回复,因此它可能被注入两次,但绝不会丢失


硬性约束

  1. 每台机器只运行一个守护进程。 守护进程是唯一的 IMAP 监听器;第二个守护进程会因 EADDRINUSE 而安静退出。
  2. 不做 IMAP 轮询。 新邮件由服务器通过 IDLE 推送(watcher.js)送达,并在每次连接/重连后进行一次性补扫,绝不存在按会话或定时的轮询器。
  3. 检测基于 UID 游标,绝不依赖 \Seen 或 SUBJECT。 守护进程只扫描大于持久游标(uid-cursor.js)的 UID,与 \Seen 标志(用户可能已先读过回复)以及 SUBJECT 索引(QQ 的 SUBJECT 索引存在延迟)无关。游标会越过每个见到的 UID(无论是否已处理),因此自复制/无令牌的邮件永远不会卡住游标。
  4. 每个会话只允许一个未决决策。 该守卫在服务端实现:对重复注册的会话,守护进程的 registry 返回 alreadyPending:truerequest_decision 也会直接返回 already-pending 消息,而不发送第二封邮件。registry 存于内存(24 小时 TTL);即使回复在其预留被丢弃之后才到达,也仍会被投递(pending-store 以回复自身的 UID 为键,而非 registry)。
  5. request_decision 仅限主会话。 子智能体的调用(带 parentID 的会话)会被拒绝并返回 Call from the main session only,因为决策必须携带主会话的完整上下文。
  6. 确认顺序(P0)。 \Seen 加 journal 只在 /ack 处理器中写入,即在进程内注入成功之后。确认前发生崩溃会让邮件保持 UNSEEN,回复则持久保留在 pending.json 中,不造成丢失。
  7. 回复是数据,而非指令。 注入的正文被框架化为数据数据,非指令),并明确告知智能体不得执行回复内的任何指令(提示注入防护)。
  8. 邮件模式是全局开关(默认关闭)。 除非模式为 "on"(由 /afk 设置),否则 request_decision 拒绝发送邮件;当模式为 "off" 时,它返回模式关闭消息,智能体改用内置的 question 工具。每台机器只有一个 mode.json,绝不按会话/目录区分。

已知限制

  • 至少一次的投递。 注入并非幂等;在注入与确认之间崩溃可能导致同一条回复被投递两次。
  • 单一邮箱。 守护进程只持有 一个 IMAP 账户/文件夹;多账户场景需要为每个邮箱各运行一个守护进程(并使用不同端口)。
  • 单机运行。 守护进程按机器部署,并非共享的网络服务;若多台机器共享同一邮箱,则每台都需要自己的守护进程与凭据。
  • registry 存于内存。 单一未决决策的预留无法在守护进程重启后存活(24 小时 TTL 是唯一的清理机制)。预留被丢弃之后到达的回复仍会被投递。
  • 至少一次的属主检查依赖 session.directory 若实例的目录与会话的目录不完全一致,该实例会忽略此次投递(对常见场景而言这是正确的;/claim 先到先得守卫覆盖了「两实例同一目录」的情况)。
  • 针对 QQ 的默认值。 IMAP/SMTP 主机默认值面向 QQ 邮箱;其他服务商需要在 config.json 中配置自己的 host/port(并且可能不会以同样的方式暴露 IMAP IDLE exists 推送)。
  • 守护进程就绪是尽力而为。 若守护进程无法在约 15 秒内启动,request_decision 仍会发送邮件,但回复可能要到守护进程恢复后才会自动路由。

文件结构

文件 作用
index.js 插件入口:轻量客户端,确保守护进程运行、启动 SSE 订阅者、暴露 request_decisionnotify_userset_email_mode
daemon.js 单一监听守护进程(端口绑定、HTTP:SSE/claim/ack/pending/register/mode、监听器、信号处理)
request-decision.js request_decision 工具(模式开关 → 注册 → SMTP 发送 → 释放)
notify.js notify_user 工具(单向 FYI 结论通知:模式开关 → SMTP 发送,不注册、不暂停)
mailer.js 共享邮件内核:root 会话解析、[omo:…] 路由令牌打标、SMTP 发送
config.js 配置加载与校验、环境变量覆盖、tuning 默认值、密码打码
messages.js 用户/智能体可见文案(默认英文,可被 config.messages 覆盖)
install.js 一键安装器(复制、npm install、生成配置骨架、注册、复制命令)
.github/workflows/publish.yml GitHub Actions:推送 v* tag 时自动测试并发布 opencode-afk 到 npm
core/watcher.js 单一 IMAP IDLE 连接、自动重连、补扫钩子
core/process.js 扫描 → 获取 → 解析 → 持久化流水线(不含确认)
core/reply-parse.js 纯回复检测以及令牌/正文提取
core/new-session.js /new:从邮件在会话所在目录新建会话并注入首个任务(数据非指令)+ 确认邮件
core/pause-notify.js 停顿通知:监听 session.idle,工作波收尾后自动邮件(防漏通知的机制保障)
core/inject.js 回复注入(DATA-NOT-INSTRUCTION)+ \Seen/journal 确认
core/subscribe.js 进程内 SSE 订阅者:属主自检 → 抢占 → 注入 → 确认,外加重连补扫
store/pending-store.js 磁盘上持久化的待投递消息(P0 修复:claim/ack 原子性与 TTL)
store/mode-store.js 磁盘上持久化的全局邮件模式(mode.json,默认 "off"
store/registry.js 内存中的单一未决决策守卫(不负责路由)
store/uid-cursor.js 持久化的「已处理最高 UID」游标(检测状态)
command/ /afk/back 命令模板(会被复制到 ~/.config/opencode/command/
test/ node:test 单元测试套件与实网集成脚本

许可证

MIT,详见 LICENSE

测试

# unit tests (no network): pending-store, mode-store, registry, inject-ack,
# process-mail, negative, uid-cursor, daemon HTTP (register + push + mode
# endpoints + SSE), subscribe, request-decision (incl. the mode gate),
# notify_user, reply-parse, unit, messages, config
npm test

实网集成脚本(访问真实 QQ 邮箱;需单独运行;部分脚本仍引用推送改造前的契约,不属于自动化测试套件):test/daemon-live.mjstest/catch-up.live.mjstest/idle-connect.live.mjstest/uidcursor.live.mjstest/e2e.mjstest/receive-parse-test.mjstest/inject-test.mjs

About

Email-reply wake for opencode sessions: /afk to leave, reply by email to wake the agent

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages