# 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 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 resume # claude claude --permission-mode bypassPermissions -p --output-format stream-json --verbose --resume # qoder qodercli --yolo -p --output-format stream-json --resume ``` Profile 自定义时可设 `engine: "codex"` / `"claude"` / `"qoder"` 来选择参数构造与 session id 解析方式;省略时根据 `command` 推断。Claude 和 Qoder 的 stream-json 输出 schema 一致,前端展示时都只渲染 assistant 文本和最终 `[done]` 行。