- 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>
184 lines
6.3 KiB
Markdown
184 lines
6.3 KiB
Markdown
# 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]` 行。
|