Files
kanban/README.md
Gahow Wang 7ab826ac7e feat: add Claude Code and Qoder agent engines
- Introduce `engine` field on agent profiles (codex/claude/qoder); auto-inferred from command when omitted.
- Default profiles now include `claude-bypass` (mirrors `ccx='claude --permission-mode bypassPermissions'`) and `qoder-yolo` (mirrors `qxx='qodercli --yolo'`), both running headless with `-p --output-format stream-json` so session ids can be extracted for resume.
- Stream-json engines share parsing: session_id pulled from `system.init`, task stdout shows only assistant text plus a `[done]` line, tool_use/tool_result noise dropped.
- Resume builds `--resume <sid> <prompt>` for stream-json engines, keeps Codex's `resume <sid> <prompt>` positional form.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 15:08:28 +08:00

184 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Local Kanban
本地 Web 看板,用来汇总 SSH 可访问开发机的 GPU 使用情况、IPADS API 额度、本地 Gitea 项目,并从选定 project/branch 准备 workspace 后启动 agent。
## 启动
```bash
npm start
```
默认地址是 `http://127.0.0.1:5180`,生产访问通常走 `https://gahow-pc.ipads-lab.se.sjtu.edu.cn:8443/` 的本地 Nginx 反代。
首次启动会生成访问 token并保存到 `~/.config/local-kanban/state.json`。登录页需要输入这个 token
```bash
jq -r .authToken ~/.config/local-kanban/state.json
```
也可以用固定环境变量覆盖:
```bash
KANBAN_AUTH_TOKEN='long-random-token' npm start
```
如果通过 HTTPS 反代访问,可设置 `KANBAN_AUTH_SECURE_COOKIE=1` 让浏览器只在 HTTPS 连接发送 cookie。纯 HTTP 暴露在局域网时token 登录能阻止未授权用户直接访问服务,但链路本身不能抵御同网段抓包;需要“强隔离”时应配合 HTTPS、VPN 或 SSH tunnel。
HTTPS 反代推荐让 Node 只监听本机:
```bash
KANBAN_HOST=127.0.0.1 \
KANBAN_PORT=5180 \
KANBAN_AUTH_SECURE_COOKIE=1 \
KANBAN_REQUIRE_HTTPS=1 \
npm start
```
Nginx 示例在 `deploy/nginx.local-kanban.conf`。当前设计是:
- `https://gahow-pc.ipads-lab.se.sjtu.edu.cn/` 是入口首页
- 每个服务仍然保留自己的端口,例如 Kanban 是 `https://gahow-pc.ipads-lab.se.sjtu.edu.cn:8443/`
- 入口首页展示 Gitea、Kanban、Stock Agent 三个常用服务
- 后端 API 链接默认折叠,需要时展开
系统 Nginx 负责:
- TLS 终止
- 在 443 提供静态入口首页
- 在 3443 反代 Gitea 的 3300
- 在 5443 反代 Stock Agent 前端的 5174并把 `/api` 转发到 8787
- 在 8788 直接反代 Stock Agent API 的 8787
- 默认不监听 80避免和现有本地服务冲突
没有 sudo 时,可以使用当前仓库里的用户级 Nginx 配置监听 8443详见 `deploy/README.md`
如果需要持久运行 Node app 和用户级 Nginx可以安装用户级 systemd 服务:
```bash
./scripts/install-user-systemd.sh
```
## 配置
可以用环境变量快速配置:
```bash
KANBAN_GPU_HOSTS=dash0,dash1 \
KANBAN_GITEA_BASE_URL="http://gahow-pc.ipads-lab.se.sjtu.edu.cn:3300" \
npm start
```
也可以创建 `~/.config/local-kanban/config.json`
```json
{
"gpuHosts": ["dash0", "dash1"],
"gitea": {
"baseUrl": "http://gahow-pc.ipads-lab.se.sjtu.edu.cn:3300",
"tokenEnv": "GITEA_TOKEN"
},
"workspaceRoot": "/home/gahow/.local/share/local-kanban/workspaces",
"authSecureCookie": true,
"requireHttps": true,
"quotaSources": [
{
"id": "ipads",
"label": "IPADS API",
"type": "command",
"command": "bash",
"args": ["-lc", "curl -sS -H \"Authorization: Bearer $IPADS_API_KEY\" http://tianx.ipads-lab.se.sjtu.edu.cn:8319/v1/usage | jq '{remaining, usage}'"],
"timeoutMs": 15000
}
],
"agentProfiles": [
{
"id": "codex-full",
"label": "Codex Full Permission",
"engine": "codex",
"command": "codex",
"args": [
"--dangerously-bypass-approvals-and-sandbox",
"exec",
"--sandbox",
"danger-full-access",
"--cd",
"{workspace_path}"
]
},
{
"id": "claude-bypass",
"label": "Claude Code Bypass",
"engine": "claude",
"command": "claude",
"args": [
"--permission-mode",
"bypassPermissions",
"-p",
"--output-format",
"stream-json",
"--verbose"
]
},
{
"id": "qoder-yolo",
"label": "Qoder YOLO",
"engine": "qoder",
"command": "qodercli",
"args": [
"--yolo",
"-p",
"--output-format",
"stream-json"
]
}
]
}
```
GPU 机器列表可以在 Web 页面里编辑保存,保存位置是 `~/.config/local-kanban/state.json`。默认只读取当前项目目录的 `/home/gahow/projects/kanban/.env`。需要的 key 可以参考 `.env.example`
```bash
GITEA_TOKEN=replace-with-your-gitea-token
IPADS_API_KEY=replace-with-your-ipads-api-key
```
也可以用逗号分隔的 `KANBAN_ENV_FILE` 指向其他 env 文件。
## API
- `GET /api/gpus`: 查询 `ssh <host> nvidia-smi --query-gpu=index,name,memory.used,memory.total,utilization.gpu --format=csv,noheader,nounits`
- `PUT /api/settings/gpu-hosts`: 保存需要监控的 GPU 机器列表
- `POST /api/auth/login`: 使用本地 token 登录并设置 HttpOnly cookie
- `POST /api/auth/logout`: 清除登录 cookie
- `GET /api/quotas`: 执行默认的 IPADS API 额度命令
- `GET /api/repos`: 从 Gitea `/user/repos` 读取仓库
- `GET /api/branches?repo=owner/name`: 从 Gitea 读取 branch list
- `GET /api/agent-profiles`: 返回可选 AI 配置
- `POST /api/tasks`: 准备选定 project/branch 的 workspace 后启动 agent 命令
- `GET /api/dashboard`: 聚合以上数据,供 Web 和后续 iOS 复用
## Agent Session 历史
任务历史会保存到 `~/.config/local-kanban/state.json`。每次 agent 启动后,服务会从输出中提取 `session id` 并记录到对应的 project/branch。之后在同一个 project/branch 下,可以在 Web UI 的 “历史 Session” 下拉框里选择之前的任务,点击 “继续 Session” 发送新指令。
默认内置三个 agent profile可在 Agent 配置下拉框里切换:
- `codex-full` — Codex完全权限模式
- `claude-bypass` — Claude Code等价于本地 `alias ccx='claude --permission-mode bypassPermissions'`,但跑在 headless 模式下(`-p --output-format stream-json --verbose`),方便提取 `session_id`
- `qoder-yolo` — Qoder CLI等价于本地 `alias qxx='qodercli --yolo'`headless 模式下加 `-p --output-format stream-json`
继续会话使用(按 engine
```bash
# codex
codex --dangerously-bypass-approvals-and-sandbox exec --sandbox danger-full-access --cd <workspace> resume <session_id> <prompt>
# claude
claude --permission-mode bypassPermissions -p --output-format stream-json --verbose --resume <session_id> <prompt>
# qoder
qodercli --yolo -p --output-format stream-json --resume <session_id> <prompt>
```
Profile 自定义时可设 `engine: "codex"` / `"claude"` / `"qoder"` 来选择参数构造与 session id 解析方式;省略时根据 `command` 推断。Claude 和 Qoder 的 stream-json 输出 schema 一致,前端展示时都只渲染 assistant 文本和最终 `[done]` 行。