# Remote Cli **Repository Path**: sunshinewithmoonlight/remote-cli ## Basic Information - **Project Name**: Remote Cli - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-02-28 - **Last Updated**: 2026-04-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # remote-cli `remote-cli` 用于把飞书会话桥接到本地 `Codex RPC` 服务,并提供基于 macOS `LaunchAgent` 的系统任务运行时、飞书出站发送能力和本地调试入口。 ## 当前模型 - 主服务启动 `codex app-server`,通过 `stdio` 讲 `JSON-RPC 2.0` - 每个已配置的飞书 bot 都有各自独立的 long connection,由 `BotManager` 管理 - 入站飞书会话按 `bot_app_id + chat_id` 映射到独立的 Codex thread - Codex 的 `agentMessage/delta` 会通过当前自适应出站 sender 回传飞书,默认使用 `interactive` 卡片并尽量 patch 同一张卡片 - `/new` 会为当前 bot/chat scope 新建 thread,后续消息继续写入这个新 thread ## 当前能力 - 交互式配置:`remote-cli config` / `remote-cli setup` - RPC 多 bot 配置:`port`、`full_access`、`latest-reply-only`、`codex_rpc_proxy_ip`、`codex_rpc_proxy_port`、`debug_codex_stream`、`immediately-process`、`audio-processor`、`bots[]` - 飞书文本入站到 Codex RPC - 飞书附件入站下载到运行时根目录下的 `files/` 目录 - 飞书附件可按 `immediately-process` 配置决定是立即开始本轮处理,还是先在当前 chat 下暂存等待后续文本 - 飞书对话级 `/new` 切换 - 飞书出站辅助命令:`send-feishu-text`、`send-feishu-message` - 任务运行时命令: - `task resolve` - `task create` - `task list`(`task find` 兼容别名) - `task status` - `task pause` - `task resume` - `task cancel` - `task delete` - 服务控制:`start`、`stop`、`restart`、`status`、`logs`、`autorun` - 运行路径查看:`service paths` - 健康检查端点:`/livez`、`/readyz`、`/healthz` - 本地入站调试:`inject-feishu-text` ## 当前任务链路 当前系统任务采用两阶段链路: 1. `task resolve` - 是模型参与的自然语义解析步骤 - 会在 `tasks/` 下创建新的任务草稿目录 - 会写入 `resolve.json` 与脚本草稿 - 返回 `task_id`、`task_dir`、`script_dir` - `--json` 输出顶层会直接包含 `create_spec` 2. `task create` - 是纯安装步骤,不承担 AI 语义解析 - 优先消费 `resolve.json` 中的 `create_spec` - 把 `task.json`、`dispatch.sh`、`run.sh`、plist 等产物写回同一草稿目录 - 再注册系统级 `LaunchAgent` 当前约束: - `task create` 当前只支持 `type=time` - 创建行为和执行行为是分离的 - 先完成任务创建,再由系统在计划时刻执行任务 - 相对时间任务会在创建阶段按当前时间和系统时区计算整分触发时刻 - 当前直接创建入口就是 `task resolve -> task create`;仓库根目录不再依赖内置 skill 目录 ## 构建 ```bash make test make build ``` 二进制输出到: ```bash ./bin/remote-cli ``` ## 配置 配置文件查找顺序: 1. `--config ` 2. `~/.config/remotecli.yaml` 当前配置结构: ```yaml port: 6230 full_access: true latest-reply-only: true codex_rpc_proxy_ip: "" codex_rpc_proxy_port: 0 debug_codex_stream: false immediately-process: image: true file: false audio: true media: false audio-processor: - doubao: false app-id: your-doubao-app-id access-token: your-doubao-access-token bots: - mode: feishu id: xxx secret: xxx schedule: system_tasks_root: /absolute/path/for/system-tasks ``` 说明: - `port` 缺省为 `6230` - `full_access` 缺省为 `true` - `latest-reply-only` 缺省为 `true` - `codex_rpc_proxy_ip` 缺省为空字符串 - `codex_rpc_proxy_port` 缺省为 `0` - `debug_codex_stream` 缺省为 `false` - `immediately-process` 缺省为: - `image: true` - `file: false` - `audio: true` - `media: false` - `audio-processor` 缺省为: - 一个按顺序生效的服务项列表 - 默认写出一条 `doubao: false` 的占位项 - 当前只支持 `doubao` - `app-id` 与 `access-token` 仅在该服务项 `doubao: true` 时必填,且必须由部署时显式填写 - `bots` 至少需要一个飞书 bot - `schedule.system_tasks_root` 可选;为空时默认使用 `~/Library/Application Support/remote-cli` 关于关键字段: - `full_access: true` - 会在 `thread/start` 与 `turn/start` 中显式传递 danger-full-access sandbox override - `full_access: false` - 不强制覆盖 sandbox,回退到 Codex app-server 的默认行为 - `latest-reply-only: true` - 同一轮回复卡片实时 patch,只展示最新 `agentMessage` 内容 - `latest-reply-only: false` - 同一轮回复卡片仍实时 patch,但会累积这轮所有 `agentMessage` 内容 - `codex_rpc_proxy_ip: ""` 且 `codex_rpc_proxy_port: 0` - 不使用代理;`remote-cli` 会在启动 `codex app-server` 前显式清理继承来的代理环境变量,按直连方式运行 Codex RPC - `codex_rpc_proxy_ip` 与 `codex_rpc_proxy_port` 同时填写 - `remote-cli` 会先对 `ip:port` 做短超时 TCP 连通性探测 - 探测成功时,才会为 `codex app-server` 子进程注入 `HTTP_PROXY`、`HTTPS_PROXY`、`ALL_PROXY` 及其小写变体 - 探测失败时,会自动回退为直连,不会因为代理不可达而阻止主服务或 `task resolve` 启动 - 当前按 HTTP 代理解释为 `http://ip:port` - `debug_codex_stream: true` - 会把可见的 Codex 中间输出写入 `/tmp/remote-cli/remote-cli.debug.log` - 内容为 `JSONL` - 包括 `turn started`、`agentMessage/delta`、`item completed`、自动审批事件,以及镜像后的飞书 send/patch 事件 - `immediately-process` - 默认 `image` 与 `audio` 一到达就开始本轮处理 - 默认 `file` 与 `media` 仍先进入附件草稿,等待后续文本 - 如果显式覆盖某个子字段,则该类型按配置值决定是否立即处理 - `audio-processor` - 配置是有序列表,运行时只会选择顺序上最前面的一个已启用服务项 - 当前仅支持 `doubao`;后续即使增加 `ollama` 也不会做串联 fallback - 仓库示例不会内置任何固定 Doubao App ID,`app-id` 必须填成你自己的 deployment-specific 值 - 某个服务项 `doubao: true` 时,飞书 `audio` 下载后会先调用豆包流式语音识别大模型的 WebSocket 接口转录 - 当前实现按已验证可行的方案,把归一化后的整段音频作为最后一个音频包发送到 `wss://openspeech.bytedance.com/api/v3/sauc/bigmodel` - 转录成功后,送入 Codex RPC 的是普通文本消息,不再混入附件元数据与 `local_path` - 转录失败时会回退到当前附件描述路径 - 飞书语音若不是可直接使用的格式,会在 macOS 上优先调用 `ffmpeg` 转成标准 `pcm_s16le wav`;若本机没有 `ffmpeg`,再回退到 `afconvert` - 当前豆包配置示例统一写成 `app-id: your-doubao-app-id`、`access-token: your-doubao-access-token` - 当前这台机器的 20 小时时长包归属 `App ID 7499016332`;已有本机 `remotecli.yaml` 不会自动迁移,必须手动更新 - 当前按 `resource_id: volc.bigasr.sauc.duration` 接入,匹配该 App 已开通的 `流式语音识别大模型` - 如果把同一组凭据误接到 `录音文件识别` 的 `auc` 资源,会得到 `45000030 requested resource not granted` ## 交互式配置 推荐命令: ```bash ./bin/remote-cli config ``` `remote-cli setup` 是等价别名。 行为: - 真正的 TTY:使用交互表单 - 非 TTY / 管道 / 测试环境:回退到逐行文本提示 - 默认输出路径:`~/.config/remotecli.yaml` - 生成的默认配置会包含 `full_access: true`、`latest-reply-only: true`、空的 `codex_rpc_proxy_ip` / `codex_rpc_proxy_port`、默认的 `immediately-process`,以及一条默认禁用的 `audio-processor` 服务项 如果要使用自定义配置路径,请显式传: ```bash --config ``` ## 校验与运行 只校验配置: ```bash ./bin/remote-cli --dry-run ./bin/remote-cli --config /path/to/remotecli.yaml --dry-run ``` 前台运行服务: ```bash ./bin/remote-cli ./bin/remote-cli --config /path/to/remotecli.yaml ``` ## 服务命令 常用命令: ```bash ./bin/remote-cli autorun ./bin/remote-cli start ./bin/remote-cli restart ./bin/remote-cli stop ./bin/remote-cli status ./bin/remote-cli logs ./bin/remote-cli service paths ``` 说明: - `autorun` 会写入或刷新 `~/Library/LaunchAgents/com.remotecli.service.plist` - 如果已经安装主服务 `LaunchAgent`,后续 `start` / `restart` / `stop` / `status` 会优先操作该 `LaunchAgent` - 运行路径查看当前走 `service paths`,不是顶层 `paths` - 如果要切换 `LaunchAgent` 使用的配置文件,请重新执行 `autorun --config ` - 单独执行 `restart --config ...` 不会重写已安装 plist ## 健康检查 ```bash ./bin/remote-cli health curl -sS http://127.0.0.1:6230/healthz curl -sS http://127.0.0.1:6230/livez curl -sS http://127.0.0.1:6230/readyz ``` 说明: - `/livez`:进程活着且未进入 shutdown - `/readyz`:服务已经 ready,可接业务 - `/healthz`:兼容入口,当前语义与 `/readyz` 相同 - JSON 里仍保留历史字段名 `main_session_running`,但现在它表示主服务是否健康,不再表示 tmux 会话是否存在 运维上应优先相信: - `./bin/remote-cli health` - `/readyz` - 实际监听端口 - 对应 `LaunchAgent` 状态 不要再把: - 单独存在的 pid - 旧 tmux session - 遗留 helper script 当成唯一事实源。 ## 最小烟测 1. 构建:`make build` 2. 执行配置:`./bin/remote-cli config` 3. 校验配置:`./bin/remote-cli --dry-run` 4. 启动服务:`./bin/remote-cli` 或 `./bin/remote-cli autorun` 5. 检查健康:`./bin/remote-cli health` 6. 从飞书发一条普通消息,确认收到回复 7. 从飞书发送 `/new`,确认后续回复进入新 thread ## 任务链路烟测 解析任务: ```bash ./bin/remote-cli task resolve --request '5分钟后给当前会话发送一条测试消息' --json ``` 预期: - 返回顶层 `create_spec` - 返回 `task_id`、`task_dir`、`script_dir` - 对应草稿目录下出现 `resolve.json` 和脚本草稿 创建任务: ```bash ./bin/remote-cli task create --task-dir /path/to/task_dir --json ``` 预期: - 返回安装成功 - 同一草稿目录下出现 `task.json`、`dispatch.sh`、`run.sh`、plist - 之后才能进入计划时刻执行,而不是在创建链路里直接执行 ## 飞书辅助命令 发送纯文本: ```bash ./bin/remote-cli send-feishu-text --config /path/to/remotecli.yaml --chat-id oc_xxx --text "hello" ``` 发送结构化飞书消息: ```bash ./bin/remote-cli send-feishu-message --config /path/to/remotecli.yaml --chat-id oc_xxx --msg-type interactive --content-file /path/to/card.json ``` 对系统任务 bundle 来说,运行时参数当前按以下顺序解析: 1. 显式 flag 2. `REMOTE_CLI_TASK_DIR` 指向的 bundle `task.json` 3. 兼容性 env 回退: - `REMOTE_CLI_CONFIG` - `REMOTE_CLI_CHAT_ID` - `REMOTE_CLI_BOT_APP_ID` `task.json` 会持久化这些非敏感运行时上下文: - `chat_id` - `bot_app_id` - `config_path` - `system_tasks_root` 如果你手工执行飞书任务创建: - `task create --config ` 必须使用和任务命令里 `bot_id` 对应的同一份配置文件 ## 本地入站注入 为了本地烟测,当前提供最小入站调试入口: ```bash ./bin/remote-cli inject-feishu-text --text "请创建一个明天上午 9:30 给当前对话发送提醒的任务" ``` 行为: - CLI 会向 `POST /debug/inject-feishu-text` 发送本地 HTTP 请求 - 该调试端点只允许 loopback 调用 - 缺失的 `chat_id`、`bot_app_id`、`sender_id` 会优先从 `system/runtime/current_chat.json` 回填 - 服务会先更新 `current_chat.json`,再把合成后的入站消息送入当前进程内的 `codexRPCBridge` 这意味着: - 会复用真实的 thread 映射 - 会复用真实的飞书出站 sender - 适合验证“创建任务”这类真实入站链路 - 它不是公开 API,也不是单独的一套 mock runtime ## 飞书附件落盘 当前飞书附件会优先保存到运行时根目录下的 `files/` 目录: ```bash ~/Library/Application Support/remote-cli/files ``` 如果配置了自定义 `schedule.system_tasks_root`,则会保存到: ```bash /files ``` 当前命名策略: - 文件名包含 `message_id` 和 `resource_key` - 会尽量保留原始文件名的可读部分 - 同名附件不会互相覆盖 - 当前仍需要“附件后的下一条文本”来真正提交给 Codex 常见排障路径: ```bash find "$HOME/Library/Application Support/remote-cli/files" -maxdepth 1 -type f | sort | tail -n 20 cat "$HOME/Library/Application Support/remote-cli/system/runtime/current_chat.json" tail -n 200 /tmp/remote-cli/remote-cli.error.log | rg "attachment|download|local_path|files/" ``` ## 读取 Codex 中间输出 如果 `debug_codex_stream: true`,可以直接查看: ```bash /tmp/remote-cli/remote-cli.debug.log ``` 常见过滤方式: ```bash rg -n 'codex_turn_started|codex_item_started|codex_agent_message_delta|codex_item_completed|codex_turn_completed' /tmp/remote-cli/remote-cli.debug.log ``` 如果要绑定到具体一次请求,可按 `turn_id`、`thread_id` 或消息 id 过滤。 ## 文档入口 - [docs/index.md](docs/index.md) - [docs/quick-deploy.md](docs/quick-deploy.md) - [docs/architecture.md](docs/architecture.md) - [docs/technical-details/README.md](docs/technical-details/README.md)