# flowgate **Repository Path**: brt2/flowgate ## Basic Information - **Project Name**: flowgate - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-04-12 - **Last Updated**: 2026-04-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # AgentG AI Agent 会话管理工具,支持多会话并行监控、任务队列自动执行。 ## 版本说明 | 目录 | 状态 | 说明 | |------|------|------| | `flowgate-web/` | **主推版本** | Flask + Alpine.js,功能完整 | | `portable/` | 构建工具 | 构建 Portable 分发包脚本 | | `__depracated/` | Deprecated | Electron 版本,已停止维护 | | `hooks/` | 配套 | Agent Hook 脚本(统一兼容 Claude Code / Codex / Kiro / Hermes) | | `wakeup-ahk/` | 配套 | `flowgate://` URL 协议激活本地窗口 | **推荐使用 `flowgate-web/` 版本。** --- ## 快速部署 (flowgate-web) ### 1. 启动服务 ```bash cd flowgate-web pip install -r requirements.txt python app.py ``` 服务运行在 `http://0.0.0.0:12124` 启动后 Agent 完成任务时弹出 Windows Toast 桌面通知(需要 `winotify`)。如果会话绑定了窗口,通知上会带「激活窗口」按钮,点击直接切换到对应窗口。 ### 2. 配置 Agent Hook FlowGate 提供统一 Hook 脚本 `hooks/flowgate-hook.py`,自动检测 Agent 类型。 所有客户端的 hook 命令都指向同一个脚本。 **Claude Code** — 在 `settings.json` 中添加: ```json { "hooks": { "SessionStart": "python /path/to/hooks/flowgate-hook.py", "UserPromptSubmit": "python /path/to/hooks/flowgate-hook.py", "Stop": "python /path/to/hooks/flowgate-hook.py", "StopFailure": "python /path/to/hooks/flowgate-hook.py", "PostCompact": "python /path/to/hooks/flowgate-hook.py", "SessionEnd": "python /path/to/hooks/flowgate-hook.py" } } ``` **Codex CLI** — 在 `~/.codex/hooks.json` 中配置(需 `codex_hooks = true`),详见 `hooks/codex/README.md` **Kiro IDE** — 将 `hooks/kiro/kiro-ide-hooks/*.json` 复制到 `.kiro/hooks/` **Kiro CLI** — 参考 `hooks/kiro/kiro-cli-agent.json.example` 配置 agent hooks **Hermes** — 将 `hooks/hermes/config.yaml.example` 合并到 `~/.hermes/config.yaml` ### 3. 配置服务地址 flowgate-cli 会自动读取配置文件 `~/.config/flowgate.cfg`,首次运行时会自动创建。 编辑配置文件: ```bash # Windows notepad %USERPROFILE%\.config\flowgate.cfg # Linux/macOS nano ~/.config/flowgate.cfg ``` 内容: ```json { "FLOWGATE_URL": "http://192.168.x.x:12124" } ``` > 配置文件路径:`~/.config/flowgate.cfg` ### 4. 安装 URL 协议(可选) 用于从网页激活本地窗口(如飞书、VS Code): **方式一:自动安装(推荐)** ```bash cd wakeup-ahk # 右键 → 以管理员身份运行 install.bat ``` **方式二:手动注册** 双击 `wakeup-ahk/register_protocol.reg` 导入注册表(路径已写死为 AutoHotkey 默认安装目录)。 ### 排查:点击窗口切换无反应 如果在 Web UI 点击会话卡片的窗口切换按钮后没有反应,按以下步骤排查: **① 检查注册表是否正确** 在 CMD 中执行: ```cmd reg query "HKCR\flowgate\shell\open\command" /ve ``` 正确的输出应该是: ``` (默认) REG_SZ "C:\Program Files\AutoHotkey\flowgate-activate.exe" "%1" ``` 注意:exe 路径和 `%1` 都必须有引号包裹。如果路径没有引号(如 `C:\Program Files\...` 没被引号包住),会因为路径中的空格导致解析失败。重新以管理员身份运行 `install.bat` 修复。 **② 手动测试 exe 是否能正常工作** 在 CMD 中直接调用编译后的 exe,传入一个模拟的协议 URL: ```cmd :: 测试英文关键字(不涉及编码) "C:\Program Files\AutoHotkey\flowgate-activate.exe" "flowgate://activate-window/Visual Studio Code" :: 测试中文关键字(percent-encoded,模拟浏览器实际传递的格式) :: "资源" → UTF-8 字节 E8 B5 84 E6 BA 90 → %E8%B5%84%E6%BA%90 :: 会匹配到"文件资源管理器"等包含"资源"的窗口标题 "C:\Program Files\AutoHotkey\flowgate-activate.exe" "flowgate://activate-window/%E8%B5%84%E6%BA%90" ``` 如果对应窗口被激活了,说明 exe 本身没问题。 也可以用 AHK 解释器直接跑脚本(无需编译),方便调试: ```cmd "C:\Program Files\AutoHotkey\AutoHotkey.exe" "wakeup-ahk\activate_window_by_title.ahk" "flowgate://activate-window/%E8%B5%84%E6%BA%90" ``` 如果需要查看详细的解码过程,使用调试版本脚本,会将每一步结果写入 `debug_output.txt` 并自动打开: ```cmd "C:\Program Files\AutoHotkey\AutoHotkey.exe" "wakeup-ahk\activate_window_by_title_debug.ahk" "flowgate://activate-window/%E8%B5%84%E6%BA%90" ``` 中文关键字的 percent-encoded 值可以用 Python 快速获取: ```cmd python -c "import urllib.parse; print(urllib.parse.quote('资源'))" :: 输出: %E8%B5%84%E6%BA%90 ``` **③ 确认浏览器是否发出了协议调用** Web UI 通过 `window.location.href = 'flowgate://activate-window/xxx'` 触发协议调用。部分浏览器会弹出"是否允许打开外部应用"的确认框,如果选了"拒绝"或勾选了"始终拒绝",后续调用会被静默拦截。 - Chrome:访问 `chrome://settings/handlers` 检查是否被屏蔽 - Edge:访问 `edge://settings/content/protocolHandlers` 检查 **④ 确认会话已绑定窗口关键字** Web UI 只有在会话绑定了 `bound_window` 后才会发出协议调用。在会话卡片上点击绑定按钮,从常用列表选择或手动输入窗口标题关键字(支持中文,如 `飞书`、`Visual Studio Code`)。点击 agent_app 名称(如 "Claude Code")即可激活绑定的窗口。 ### 中文窗口标题的编码处理流程 绑定中文窗口标题(如"飞书")后,从点击到窗口激活经历以下编码转换: ``` 用户点击 agent_app 名称 │ ▼ ① Web 前端(window.js) 构造原始 URL:flowgate://activate-window/飞书 此时 URL 中包含原始中文字符 │ ▼ ② 浏览器自动处理 浏览器对自定义协议 URL 中的非 ASCII 字符执行 percent-encoding (这是浏览器的内置行为,前端无法控制) "飞书" 的 UTF-8 字节为 E9 A3 9E E4 B9 A6 编码后:flowgate://activate-window/%E9%A3%9E%E4%B9%A6 │ ▼ ③ Windows 协议分发 Windows 查找注册表 HKCR\flowgate\shell\open\command 将完整 URL 作为参数传给 flowgate-activate.exe exe 收到的参数就是 percent-encoded 的字符串原文 │ ▼ ④ AHK 脚本(activate_window_by_title.ahk) 从参数中提取 activate-window/ 后面的部分:%E9%A3%9E%E4%B9%A6 调用 UrlDecode() 函数: - 逐个解析 %XX,将十六进制转为字节值(E9→233, A3→163, ...) - 将所有字节写入内存缓冲区(VarSetCapacity + NumPut) - 用 StrGet(&buf, byteCount, "UTF-8") 将 UTF-8 字节还原为 Unicode 字符串 还原结果:"飞书" │ ▼ ⑤ WinActivate 使用 SetTitleMatchMode 2(子串匹配) 在所有窗口标题中搜索包含"飞书"的窗口并激活 ``` 注意事项: - AHK 必须是 v1 Unicode 版本(`A_IsUnicode = 1`),ANSI 版本无法正确处理 UTF-8 解码 - 纯 ASCII 关键字(如 `Visual Studio Code`)不会被 percent-encode,直接透传,无需解码 - `UrlDecode()` 函数会自动判断:没有 `%` 的字符串直接原样返回 ### AHK 窗口激活实现演进 **v1 实现(已废弃):直接使用 WinActivate 传统命令** ```ahk SetTitleMatchMode, 2 WinActivate, %Keyword% ``` 问题:`WinActivate, %Keyword%` 是 AHK v1 的传统命令语法,通过 `%var%` 展开变量。当 `Keyword` 的值来自 `StrGet()` 函数(URL 解码后的 Unicode 字符串)时,传统命令无法正确处理这个字符串,导致中文关键字匹配不到任何窗口。英文关键字不受影响,因为不经过 `StrGet` 转换。 **v2 实现(当前):遍历窗口 + InStr 匹配 + ahk_id 激活** ```ahk SetTitleMatchMode, 2 WinGet, allList, List Loop, %allList% { id := allList%A_Index% WinGetTitle, t, ahk_id %id% if InStr(t, Keyword) { WinActivate, ahk_id %id% ExitApp } } ``` 改为遍历所有窗口,用表达式函数 `InStr(t, Keyword)` 做子串匹配(表达式语法对 `StrGet` 返回的字符串完全兼容),找到后用 `WinActivate, ahk_id %id%` 按窗口 ID 激活,绕过了传统命令的变量展开问题。 ### 开发过程中发现的坑 | 问题 | 原因 | 解决方案 | |------|------|----------| | 浏览器传给 exe 的中文变成 `%E8%B5%84...` | 浏览器对自定义协议 URL 自动 percent-encode,无法控制 | AHK 脚本内实现 `UrlDecode()` 函数 | | AHK v1 的 `"0x" . hexStr + 0` 得到错误值 | 字符串拼接和算术运算优先级问题 | 分两行:先 `hexVal := "0x" . hexStr` 再 `hexVal += 0` | | `UrlDecode` 返回正确中文但 `WinActivate` 匹配不到 | AHK v1 传统命令 `%var%` 展开对 `StrGet()` 返回的字符串不兼容 | 改用 `InStr()` 表达式匹配 + `ahk_id` 激活 | | `ScriptControl` COM 对象不可用 | 64 位 Windows 上 `ScriptControl` 是 32 位 COM,不可用 | 改用纯 AHK 内存操作(`VarSetCapacity` + `NumPut` + `StrGet`) | | AHK 脚本中硬编码的中文显示乱码 | 脚本文件未以 UTF-8 BOM 保存,AHK v1 Unicode 版本要求 BOM | 确保编辑器保存为 UTF-8 with BOM,或避免硬编码中文 | | MsgBox 显示中文乱码 | MsgBox 传统命令的编码处理问题 | 调试时改用 `FileOpen("UTF-8")` 写文件查看 | | Toast 通知点击无法激活窗口到最前面(只闪烁任务栏) | Windows 前台窗口保护机制,见下方详述 | 暂无解,属于系统级限制 | ### 已知限制:Toast 通知点击无法将窗口提到最前面 从 winotify Toast 通知点击触发 `flowgate://` 协议时,目标窗口只在任务栏闪烁红光,不会被提到所有窗口的最前面。但从浏览器页面点击触发同样的协议,窗口能正常激活。 原因:Windows 的 `SetForegroundWindow` 安全限制。只有当前拥有前台焦点的进程才能把窗口提到最前面,其他进程只能让任务栏闪烁。浏览器点击时浏览器本身是前台窗口,启动的 AHK 子进程继承了前台权限;而 Toast 通知的点击由 Windows Shell 通知子系统处理,它启动的 AHK 进程不具备前台权限。 已尝试但无效的绕过方案: | 方案 | 结果 | |------|------| | `WinSet, AlwaysOnTop, On/Off` 闪切 | 改变了 Z-order 但没获得真正的前台焦点 | | `SendInput` 模拟 Alt 键按下/释放 | Toast 启动的进程可能连 SendInput 权限都受限 | | `AttachThreadInput` + `SystemParametersInfo` 设 `ForegroundLockTimeout=0` | 理论上最强组合,仍然无效 | 结论:这是 Windows 系统级的安全限制,Toast `activationType="protocol"` 启动的进程受到比普通非前台进程更严格的约束,代码层面暂时无法绕过。 ```bash curl http://localhost:12124/api/state ``` 检查对应 session 的 `bound_window` 字段是否有值。 --- ## 核心功能 ### 会话管理 - 多会话并行监控(实时状态卡片) - 三种视图:田字格(Grid,默认)、列表(List)、表格(Table) - `session_description` 标签继承(同机器同目录自动追加 V2/V3) - 卡点标记(红色脉冲 + "卡住?"按钮) ### 田字格视图(Grid) - 默认视图,紧凑展示所有会话 - 点击卡片头部(头像/名称/描述)进入会话详情 - 点击卡片下半部分(任务预览/时间)激活绑定窗口 - 支持拖拽排序,网格行列数可配置 - 排序和配置持久化到 state.log ### 任务队列 + 问题池 - **任务队列**:等待执行的任务列表,支持拖拽排序、置顶 - **问题池**:记录思路、想法,打勾完成 - **自动执行**:Stop Hook 自动从队列取任务交给 Claude Code 执行 ### 实时推送 - SSE (`/events`) 实时推送状态更新 - 断线检测(1小时无活动标记灰色) --- ## 项目结构 ``` AgentG/ ├── flowgate-web/ # Flask Web 服务(主版本) │ ├── app.py # 入口:Flask 创建、蓝图注册、启动 │ ├── state.py # 全局状态:load/save/recover、SSE broadcast │ ├── notify.py # Toast 通知(winotify),支持点击激活绑定窗口 │ ├── tray.py # 系统托盘通知(pystray,已停用,保留备用) │ ├── routes/ │ │ ├── session.py # 会话管理:report、delete、description、blocked、window │ │ ├── queue.py # 任务队列 + 问题池 CRUD │ │ ├── grid.py # 网格视图配置 + 排序持久化 │ │ └── misc.py # config、state、clear、SSE events、index │ ├── static/ │ │ ├── css/flowgate.css # 样式 │ │ ├── js/api.js # API 调用模块 │ │ ├── js/flowgate.js # Alpine.js 主组件 │ │ ├── js/queue.js # 拖拽排序 mixin │ │ ├── js/window.js # 窗口绑定/激活 mixin │ │ └── favicon.ico # 图标 │ └── templates/index.html # HTML 骨架 ├── hooks/ # Agent Hook 脚本(统一入口 + 各客户端适配) │ ├── flowgate-hook.py # 统一 Hook(自动检测 Claude Code/Codex/Kiro/Hermes) │ ├── claude-code/ # Claude Code 旧版专用脚本 + 配置示例 │ ├── codex/ # Codex CLI 适配方案(草案) │ ├── kiro/ # Kiro IDE/CLI 适配脚本 + hook 配置 │ └── hermes/ # Hermes Agent 适配脚本 + 配置示例 ├── wakeup-ahk/ # URL 协议激活本地窗口 │ ├── activate_window_by_title.ahk # AHK 脚本(含 URL 解码,支持中文) │ ├── install.bat # 编译 + 注册协议 │ └── uninstall.bat ├── portable/ # Portable 分发包构建脚本 │ └── build_portable.py ├── CLAUDE.md └── README.md ``` --- ## Portable 分发包 将 FlowGate 打包成解压即用的绿色版本,内置 Python 3.12 轻量运行时和所有依赖,无需安装 Python 环境。 ### 构建方法 在任意 **Windows + 有 Python** 的机器上运行: ```bat cd flowgate\portable python build_portable.py --output ..\FlowGate-Portable ``` 或直接打包成 zip: ```bat python build_portable.py --zip ..\FlowGate-Portable-v1.0.zip ``` 可选指定 Python 版本(默认 3.12.4): ```bat python build_portable.py --python-version 3.12.4 --output ..\FlowGate-v3.12 ``` ### 生成结构 ``` FlowGate-Portable/ ├── flowgate.bat ← 双击启动 ├── install-protocol.bat ← 注册 flowgate:// 协议(需 AutoHotkey) ├── uninstall-protocol.bat ← 卸载协议 ├── python-3.12.4/ ← Python 轻量运行时(含 pip + 全部依赖) │ ├── python.exe │ └── Scripts/pip.exe ├── flowgate-web/ ← Flask 后端 + 前端 ├── wakeup-ahk/ ← AHK 窗口激活脚本 ├── hooks/claude-code/ ← Claude Code Hook(旧版兼容) ├── hooks/flowgate-hook.py ← 统一 Hook 入口 └── README.md ``` ### 内置依赖 | 包 | 用途 | |----|------| | Flask >=3.0.0 | Web 框架 | | winotify >=1.1.0 | Windows Toast 通知 | | pystray >=0.19.0 | 系统托盘图标(可选) | | Pillow >=10.0.0 | 图片处理(pystray 依赖) | ### 修改端口 ```bat set PORT=19930 && flowgate.bat ``` ### 故障排除 | 问题 | 解决 | |------|------| | 端口被占用 | `netstat -ano \| findstr :12124` 查进程,kill | | Toast 通知不弹 | 安装 [Visual C++ Redistributable](https://aka.ms/vs/17/release/vc_redist.x64.exe) | | 协议安装失败 | `install-protocol.bat` 需要以**管理员身份**运行 | --- ## 技术栈 | 层 | 技术 | |----|------| | 后端 | Python Flask (threaded),Blueprint 模块化 | | 前端 | Alpine.js + TailwindCSS (CDN),模块化 JS | | 状态 | In-memory + JSON 持久化 (`state.log`) | | 推送 | SSE (Server-Sent Events) | | 通知 | winotify Toast 通知(支持点击激活绑定窗口) | | 窗口激活 | AutoHotkey v1 Unicode + `flowgate://` 自定义协议 |