commit 3112ca1d7cb2aa374b9aae55ff63b03c70f93b25 Author: gongzhiyong Date: Thu Apr 23 23:11:16 2026 +0800 initial: team agent workstation for 6-repo matrix - Dockerfile (Node/Python/Go/Bun + claude-code + gh cli) - docker-compose.yml (flexible REPOS_DIR, 7 mounts, 6 cache volumes) - CLAUDE.md (team conventions + red lines + per-repo build/test cmds) - .claude/settings.json (allow/deny permissions) - Makefile + scripts/{enter,bootstrap,run-task}.sh - README.md (onboarding guide) diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..5c5751f --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,101 @@ +{ + "$schema": "https://json.schemastore.org/claude-code-settings.json", + "permissions": { + "allow": [ + "Bash(git status*)", + "Bash(git diff*)", + "Bash(git log*)", + "Bash(git branch*)", + "Bash(git checkout*)", + "Bash(git switch*)", + "Bash(git add*)", + "Bash(git commit*)", + "Bash(git pull*)", + "Bash(git fetch*)", + "Bash(git stash*)", + "Bash(git push origin*)", + "Bash(gh pr*)", + "Bash(gh issue*)", + "Bash(gh run*)", + "Bash(gh workflow*)", + "Bash(gh auth status*)", + "Bash(npm install*)", + "Bash(npm test*)", + "Bash(npm run*)", + "Bash(npm audit*)", + "Bash(npm outdated*)", + "Bash(pnpm install*)", + "Bash(pnpm run*)", + "Bash(pnpm test*)", + "Bash(bun install*)", + "Bash(bun test*)", + "Bash(bun run*)", + "Bash(pip install*)", + "Bash(pip list*)", + "Bash(uv pip*)", + "Bash(uv run*)", + "Bash(pytest*)", + "Bash(ruff*)", + "Bash(black*)", + "Bash(mypy*)", + "Bash(alembic*)", + "Bash(go build*)", + "Bash(go test*)", + "Bash(go vet*)", + "Bash(go fmt*)", + "Bash(go mod*)", + "Bash(go list*)", + "Bash(make*)", + "Bash(prisma*)", + "Bash(npx prisma*)", + "Bash(docker compose build*)", + "Bash(docker compose config*)", + "Bash(docker compose ps*)", + "Bash(docker compose logs*)", + "Bash(rg*)", + "Bash(fd*)", + "Bash(jq*)", + "Bash(ls*)", + "Bash(cat*)", + "Bash(head*)", + "Bash(tail*)", + "Bash(wc*)" + ], + "deny": [ + "Bash(git push --force*)", + "Bash(git push -f*)", + "Bash(git push origin main*)", + "Bash(git push origin master*)", + "Bash(git reset --hard*)", + "Bash(git clean -fd*)", + "Bash(git commit --amend*)", + "Bash(rm -rf /*)", + "Bash(rm -rf ~*)", + "Bash(sudo*)", + "Bash(docker system prune*)", + "Bash(docker volume rm*)", + "Bash(terraform destroy*)", + "Bash(terraform apply*)", + "Bash(kubectl delete*)", + "Bash(kubectl apply*)", + "Bash(az containerapp update*)", + "Bash(az containerapp delete*)", + "Bash(alembic downgrade*)", + "Bash(dropdb*)", + "Bash(psql*production*)", + "Read(./**/.env)", + "Read(./**/.env.*)", + "Read(./**/secrets/**)", + "Read(./**/*.pem)", + "Read(./**/*.key)", + "Read(./casdoor-internal/conf/app.conf)", + "Write(./**/.env)", + "Write(./**/.env.*)", + "Write(./**/secrets/**)" + ] + }, + "env": { + "DISABLE_TELEMETRY": "0", + "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "32000" + } +} diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e46fb8c --- /dev/null +++ b/.env.example @@ -0,0 +1,40 @@ +# ================================================================= +# 登录方式(订阅模式无需填任何 KEY) +# ================================================================= +# +# 方式 A(推荐):Claude Max/Pro 订阅 OAuth +# —— 本文件任何 KEY 都不用填。启动容器后运行 `/login` 即可。 +# 凭证会写入 docker named volume `claude-home`,下次启动自动复用。 +# +# 方式 B:Anthropic API Key(按量计费,适合 CI 无人值守) +# ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +# ================================================================= +# 仓库根目录(如果你的 6 个仓库不在 ai-ops 的父目录) +# ================================================================= +# 默认:REPOS_DIR 不设 → 使用 ai-ops/../ (即 ai-ops 的同级) +# 自定义示例: +# REPOS_DIR=/Users/alice/dev/work +# REPOS_DIR=${HOME}/projects +# 注意:6 个仓库必须都在 REPOS_DIR 下,名字与 GitHub 一致: +# chat-gw / xiaoshou / gongdan / casdoor-internal / CloudCostbrank / lobechat-enterprise +# REPOS_DIR=${HOME}/work + +# ================================================================= +# Git / GitHub(Agent 提 PR 时需要) +# ================================================================= +# GH_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxx +GIT_AUTHOR_NAME=Your Name +GIT_AUTHOR_EMAIL=you@example.com + +# ================================================================= +# 企业合规:走 AWS Bedrock(代替 Anthropic API) +# ================================================================= +# CLAUDE_CODE_USE_BEDROCK=1 +# AWS_REGION=us-west-2 +# AWS_PROFILE=default + +# ================================================================= +# 模型覆盖(默认 Opus 4.7,1M 上下文) +# ================================================================= +# ANTHROPIC_MODEL=claude-sonnet-4-6 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4ec79b6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.env +.env.local +.claude/state/ +.claude/logs/ +.claude/sessions/ +.claude/projects/ +.claude/todos/ +.omc/ +*.log +.DS_Store diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..45bf372 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,70 @@ +# 6-Repo Enterprise Matrix — Agent Operating Guide + +你是这个企业级 SaaS 矩阵的维护者。工作目录 `/workspace` 挂载了 6 个协同工作的仓库。 + +## 仓库清单与职责 + +| 目录 | 角色 | 栈 | 端口 | 测试/构建 | +|---|---|---|---|---| +| `chat-gw/` | MCP 工具网关(权限 + 审计 + 路由) | Python 3.12 / FastAPI / asyncpg / Redis | 8000 | `pytest`;`docker compose up --build -d` | +| `xiaoshou/` | B2B 销售管理(客户 / 订单 / 账单) | FastAPI + React 18 / AntD 5 / Vite 5 | 后端 8000 / 前端 5173 | `pytest` + `npm run build`;`alembic upgrade head` | +| `gongdan/` | 工单系统 + KB 聊天 | NestJS + React + Python FastAPI | 后端 3000 / KB 8001 / 前端 5173 | `npm test`;`prisma migrate dev`;`pytest kb-chat-python` | +| `casdoor-internal/` | IAM(Casdoor fork) | Go 1.25 / Beego / xorm / PostgreSQL | 8000 | `make ut`;`make fmt vet`;`make run` | +| `CloudCostbrank/` | 多云成本聚合 | Python FastAPI + Celery + SQLAlchemy 2.0 | 8000 | `pytest`;`alembic upgrade head`;`docker compose up` | +| `lobechat-enterprise/` | 企业版聊天前端 | Next.js 16 + pgvector + Drizzle | 3010 | `bun test`;`docker compose build`;pnpm monorepo | + +## 系统拓扑 + +``` + ┌──── 用户浏览器 ────┐ + │ │ + ▼ ▼ +[lobechat-enterprise] [xiaoshou / gongdan 前端] + │ │ + │ ◄── JWT via Casdoor ──► + ▼ ▼ + [chat-gw MCP] ◄──► [xiaoshou API] [gongdan API] [CloudCostbrank API] + │ ▲ + └── 统一身份 ────► [casdoor-internal] +``` + +## 工作原则 + +1. **改动前先读当前仓库的 README / Makefile / package.json**,遵循本仓库既有的命名、lint、测试约定;各仓库风格不统一,不要跨仓库硬套。 +2. **跨仓库改动要成套提 PR**:例如改 Casdoor 的 role claim 字段,必须同步更新 chat-gw、xiaoshou、gongdan、lobechat 的 JWT 解析代码,并在 PR 描述里互相链接。 +3. **验证闭环前不得声称完成**:改了 Python → `pytest`;改了 Go → `make ut`;改了前端 → `npm run build`;改了 SQL 模型 → 跑 `alembic revision --autogenerate` 确认 diff。 +4. **提 PR 而非直推 main**:使用 `gh pr create`,描述必须含 "What / Why / Test"。 +5. **数据库迁移**:xiaoshou 当前有"pending migrations"遗留问题,任何 model 改动都要显式 `alembic revision` 并检查生成的文件。 + +## 硬红线(禁止) + +- ❌ 修改 `.env` / `secrets/**` / `conf/app.conf`(casdoor-internal 已 `skip-worktree`) +- ❌ 直接 push 到 main / master,force push 任何分支 +- ❌ 跑 `docker system prune`、`rm -rf /`、`terraform destroy`、`kubectl delete` +- ❌ 读取或打印任何包含 `TOKEN` / `SECRET` / `PASSWORD` / `AZURE_STORAGE_CONNECTION_STRING` 的值 +- ❌ 触碰 lobechat-enterprise 的 242 个 i18n 文件里的 "Enterprise AI Workspace" 品牌字符串(这是 de-brand 改动,升级上游时必须保留) +- ❌ 对 casdoor-internal 的 upstream merge 留下未标注的冲突决策 + +## CI/CD 现状(每个仓库自带 workflow) + +- `chat-gw/.github/workflows/main_gaw-chat-tools.yml` — Azure 部署 +- `xiaoshou/.github/workflows/{ci,deploy,frontend-deploy}.yml` — CI + 前后端部署 +- `gongdan/.github/workflows/{backend-deploy,post-deploy-smoke,azure-static-web-apps-*}.yml` +- `casdoor-internal/.github/workflows/{build,build-and-deploy,sync}.yml` — 含上游同步 +- `CloudCostbrank/` — **缺 CI,需补齐(优先级 P1)** +- `lobechat-enterprise/.github/workflows/deploy-aca.yml` — 仅部署,**缺 CI(优先级 P1)** + +任何会改变 lint / test 行为的 PR,都要先本地跑一遍对应 workflow 的核心命令。 + +## 常用任务入口 + +- 跨仓库搜索:`rg -t py '' chat-gw xiaoshou CloudCostbrank` +- 依赖审计:`cd && npm audit` / `pip list --outdated` / `go list -u -m all` +- 启动真实环境验证:`cd && docker compose up -d`,完事 `docker compose down -v` +- 提交前自我审查:调用 `code-reviewer` 子 agent,不要自评自批 + +## 失败处理 + +- CI 红:读 `gh run view --log-failed`,定位到文件再动手,不要猜 +- Casdoor upstream merge 冲突:先 `git log --oneline origin/upstream-main ^HEAD` 看上游新增,按 `sync.yml` 的策略逐个 hunk 决策 +- 测试 flake:不要直接 `@pytest.mark.skip`,先 rerun 3 次确认是否真 flake diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..97259e5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,52 @@ +FROM node:22-bookworm + +ARG TARGETARCH +ARG GO_VERSION=1.25.0 + +ENV DEBIAN_FRONTEND=noninteractive \ + PATH=/usr/local/go/bin:/root/go/bin:/root/.bun/bin:/root/.local/bin:$PATH \ + PYTHONDONTWRITEBYTECODE=1 \ + PIP_NO_CACHE_DIR=off \ + PIP_DISABLE_PIP_VERSION_CHECK=on + +# 换成清华 Debian 镜像(国内网络更稳)+ apt 重试策略 +RUN sed -i 's|http://deb.debian.org|https://mirrors.tuna.tsinghua.edu.cn|g; s|http://security.debian.org|https://mirrors.tuna.tsinghua.edu.cn/debian-security|g' /etc/apt/sources.list.d/debian.sources \ + && echo 'Acquire::Retries "8";' > /etc/apt/apt.conf.d/80-retries \ + && echo 'Acquire::http::Timeout "60";' >> /etc/apt/apt.conf.d/80-retries \ + && echo 'Acquire::https::Timeout "60";' >> /etc/apt/apt.conf.d/80-retries + +RUN apt-get update && apt-get install -y --no-install-recommends --fix-missing \ + git curl ca-certificates gnupg lsb-release \ + make build-essential \ + python3 python3-pip python3-venv python3-dev \ + postgresql-client redis-tools \ + jq ripgrep fd-find less vim-tiny tini \ + && ln -s /usr/bin/fdfind /usr/local/bin/fd \ + && rm -rf /var/lib/apt/lists/* + +RUN curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${TARGETARCH}.tar.gz" \ + | tar -C /usr/local -xz + +RUN curl -fsSL https://bun.sh/install | bash + +RUN npm install -g pnpm@latest @anthropic-ai/claude-code + +RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \ + | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \ + && chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \ + > /etc/apt/sources.list.d/github-cli.list \ + && apt-get update && apt-get install -y --no-install-recommends gh \ + && rm -rf /var/lib/apt/lists/* + +RUN pip install --break-system-packages --no-cache-dir \ + uv ruff black pytest pytest-asyncio httpx + +RUN git config --system --add safe.directory '*' \ + && git config --system pull.rebase false \ + && git config --system init.defaultBranch main + +WORKDIR /workspace + +ENTRYPOINT ["/usr/bin/tini", "--"] +CMD ["claude"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..58c1361 --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +.PHONY: help bootstrap build up enter task clean logout + +help: + @echo "Targets:" + @echo " bootstrap 一键初始化:clone 6 仓库 + 构建镜像" + @echo " build (重)构建镜像" + @echo " enter 进入容器(交互式)" + @echo " task CMD=.. 非交互执行单任务,例: make task CMD='审计 6 仓库的过时依赖'" + @echo " logout 清掉容器内的登录状态" + @echo " clean 删除镜像和所有卷(慎用)" + +bootstrap: + ./scripts/bootstrap.sh + +build: + docker compose build + +up enter: + ./scripts/enter.sh + +task: + @[ -n "$(CMD)" ] || (echo "用法: make task CMD='<任务描述>'" && exit 1) + ./scripts/run-task.sh "$(CMD)" + +logout: + docker volume rm ai-ops_claude-home 2>/dev/null || true + @echo "已清除登录状态,下次 enter 需要重新 /login" + +clean: + docker compose down -v --rmi local + @echo "镜像和卷已清除" diff --git a/README.md b/README.md new file mode 100644 index 0000000..75a162a --- /dev/null +++ b/README.md @@ -0,0 +1,106 @@ +# ai-ops —— 六仓库企业矩阵的 Agent 工作站 + +给 `chat-gw / xiaoshou / gongdan / casdoor-internal / CloudCostbrank / lobechat-enterprise` +这六个仓库提供一个开箱即用的 Claude Code 容器化工作环境。 + +每个团队成员**在自己的机器上**跑自己的容器,用自己的 Claude Max/Pro 订阅登录。 +共享的只是这份仓库里的 Dockerfile / CLAUDE.md / settings.json。 + +## 前置条件 + +- **macOS / Linux**,装好 Docker Desktop(>= 4.30)并能拉公网镜像 +- 有 **Claude Max 或 Pro 订阅**(或 Anthropic API Key) +- 能访问 6 个业务仓库的 GitHub 权限(已配好 ssh-key 或 gh auth login) + +## 快速上手(3 分钟) + +```bash +# 1. clone 本仓库(ai-ops)到你想放的地方,例如 ~/work +cd ~/work +git clone git@github.com:your-org/ai-ops.git +cd ai-ops + +# 2. 一键初始化:clone 6 个业务仓库 + 构建镜像 +make bootstrap + +# 3. 进容器并登录 +make enter +# 容器内: +/login # 浏览器完成 Claude Max/Pro 授权 +/status # 确认登录成功 +``` + +完成后,容器里 `/workspace` 下能看到 7 个目录(6 个业务仓库 + `ai-ops` 本身)。 + +## 目录约定 + +默认布局(`REPOS_DIR` 不设时): + +``` +~/work/ ← 任意名字 +├── ai-ops/ ← 本仓库 +├── chat-gw/ +├── xiaoshou/ +├── gongdan/ +├── casdoor-internal/ +├── CloudCostbrank/ +└── lobechat-enterprise/ +``` + +如果你 6 个仓库放在别处,编辑 `.env`: + +```bash +REPOS_DIR=/Users/alice/projects +``` + +然后 `make build && make enter` 即可。 + +## 常用命令 + +```bash +make enter # 交互进入 Agent +make task CMD="扫描 6 仓库的 CVE 并提 PR" # 非交互单任务 +make logout # 清除登录状态 +make build # 重新构建镜像(改了 Dockerfile 后) +``` + +## 权限模型 + +- 容器内的 Agent 按 `.claude/settings.json` 的 allow/deny 列表行事: + - ✅ 允许:git 操作、`pytest` / `npm test` / `make` / `gh pr` + - ❌ 拒绝:`git push --force`、`rm -rf /`、读 `.env`、`alembic downgrade`、生产数据库操作 +- 你宿主机的 `~/.ssh` 和 `~/.gitconfig` 只读挂载到容器内,git push 能用,但 Agent 改不了你本机配置 +- 登录凭证存在 docker volume `claude-home`,**不要把这个 volume 导出给队友**——每人各自 `/login` 自己的订阅 + +## 改团队规范(CLAUDE.md / settings.json) + +这两个文件是**全队共享的 Agent 行为约束**: + +- `CLAUDE.md` —— 告诉 Agent 每个仓库是做什么的、该怎么测、红线在哪 +- `.claude/settings.json` —— allow/deny 权限清单 + +改动流程:**提 PR 到本仓库(ai-ops),团队 review 合并**。每人下次 `make build && make enter` 就同步到本地。 + +## 出问题排查 + +**Docker pull 失败 / TLS 证书错误** +—— 检查是否有 VPN / 代理在做 HTTPS 拦截(例如 Astrill 的 OpenWeb 模式)。换成 WireGuard / StealthVPN,或关闭 VPN 后再拉镜像。 + +**apt 装包失败** +—— Dockerfile 已配清华镜像 + 重试 8 次。如果仍失败,检查网络对 `mirrors.tuna.tsinghua.edu.cn` 的访问。 + +**`/login` 后仍提示未登录** +—— 确认你的 Claude 订阅类型是 Max 或 Pro。Free 账号没有 Claude Code 权限。 + +**想切到 API Key 模式** +—— 编辑 `.env`,填 `ANTHROPIC_API_KEY=sk-ant-...`。API Key 优先级高于订阅。 + +## 不做什么 + +- **不是**一个跑在云上的服务,不支持 SSH 远程访问 +- **不是**给 CI/CD 用的(CI 场景另用 `.github/workflows/claude-review.yml` + API Key) +- **不**共享登录凭证 —— 每人用自己的订阅,违反 Claude TOS 会被踢 + +## 许可 / 归属 + +内部工具,仅限团队使用。六个业务仓库的许可证以各自仓库为准。 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..70588ef --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,59 @@ +name: ai-ops + +services: + claude-agent: + build: + context: . + dockerfile: Dockerfile + image: claude-agent-6repos:latest + container_name: claude-agent + hostname: claude-agent + stdin_open: true + tty: true + working_dir: /workspace + environment: + ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-} + ANTHROPIC_MODEL: ${ANTHROPIC_MODEL:-claude-opus-4-7} + CLAUDE_CODE_USE_BEDROCK: ${CLAUDE_CODE_USE_BEDROCK:-} + AWS_REGION: ${AWS_REGION:-} + AWS_PROFILE: ${AWS_PROFILE:-} + GH_TOKEN: ${GH_TOKEN:-} + GITHUB_TOKEN: ${GH_TOKEN:-} + GIT_AUTHOR_NAME: ${GIT_AUTHOR_NAME:-Claude Agent} + GIT_AUTHOR_EMAIL: ${GIT_AUTHOR_EMAIL:-claude-agent@local} + GIT_COMMITTER_NAME: ${GIT_AUTHOR_NAME:-Claude Agent} + GIT_COMMITTER_EMAIL: ${GIT_AUTHOR_EMAIL:-claude-agent@local} + TERM: xterm-256color + volumes: + # REPOS_DIR 默认指向 ai-ops 的父目录;队友可在 .env 里改成自己的工作目录 + - ${REPOS_DIR:-..}/chat-gw:/workspace/chat-gw + - ${REPOS_DIR:-..}/xiaoshou:/workspace/xiaoshou + - ${REPOS_DIR:-..}/gongdan:/workspace/gongdan + - ${REPOS_DIR:-..}/casdoor-internal:/workspace/casdoor-internal + - ${REPOS_DIR:-..}/CloudCostbrank:/workspace/CloudCostbrank + - ${REPOS_DIR:-..}/lobechat-enterprise:/workspace/lobechat-enterprise + - ./CLAUDE.md:/workspace/CLAUDE.md:ro + - ./.claude:/workspace/.claude + - claude-home:/root/.claude + - pip-cache:/root/.cache/pip + - go-cache:/root/go/pkg + - npm-cache:/root/.npm + - pnpm-store:/root/.local/share/pnpm/store + - bun-cache:/root/.bun/install/cache + - ${HOME}/.ssh:/root/.ssh:ro + # 注意:不要挂载 ~/.gitconfig —— 若宿主机没这个文件,Docker 会 + # 把它自动创建为「空目录」,导致 git 报错。用上面 GIT_AUTHOR_* + # 环境变量就足够了。 + healthcheck: + test: ["CMD", "claude", "--version"] + interval: 30s + timeout: 10s + retries: 3 + +volumes: + claude-home: + pip-cache: + go-cache: + npm-cache: + pnpm-store: + bun-cache: diff --git a/scripts/bootstrap.sh b/scripts/bootstrap.sh new file mode 100755 index 0000000..6499289 --- /dev/null +++ b/scripts/bootstrap.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# 新成员一键初始化: +# 1. 克隆 6 个业务仓库到 REPOS_DIR(默认为 ai-ops 的父目录) +# 2. 创建 .env(如果还没有) +# 3. 构建 Docker 镜像 +set -euo pipefail + +cd "$(dirname "$0")/.." +AI_OPS_DIR="$(pwd)" + +# 读取 .env 里的 REPOS_DIR(如果存在) +if [[ -f .env ]]; then + # shellcheck disable=SC1091 + source <(grep -E '^REPOS_DIR=' .env || true) +fi +REPOS_DIR="${REPOS_DIR:-$(cd .. && pwd)}" + +echo ">>> 仓库将克隆到: $REPOS_DIR" +mkdir -p "$REPOS_DIR" + +declare -A REPOS=( + [chat-gw]="https://github.com/Fasthei/chat-gw.git" + [xiaoshou]="https://github.com/Fasthei/xiaoshou.git" + [gongdan]="https://github.com/Fasthei/gongdan.git" + [casdoor-internal]="https://github.com/zsbgnw12/casdoor-internal.git" + [CloudCostbrank]="https://github.com/zsbgnw12/CloudCostbrank.git" + [lobechat-enterprise]="https://github.com/zsbgnw12/lobechat-enterprise.git" +) + +for name in "${!REPOS[@]}"; do + target="$REPOS_DIR/$name" + if [[ -d "$target/.git" ]]; then + echo " [skip] $name 已存在" + else + echo " [clone] $name" + git clone "${REPOS[$name]}" "$target" + fi +done + +cd "$AI_OPS_DIR" +if [[ ! -f .env ]]; then + cp .env.example .env + echo ">>> 已创建 .env(订阅模式无需改动;用 API Key 的话填上 ANTHROPIC_API_KEY)" +fi + +echo ">>> 构建镜像(首次约 5 分钟)" +docker compose build + +echo "" +echo ">>> 完成。下一步:" +echo " ./scripts/enter.sh # 进入容器" +echo " 容器内运行 /login # 用你自己的 Claude Max/Pro 订阅登录" diff --git a/scripts/enter.sh b/scripts/enter.sh new file mode 100755 index 0000000..5e2442e --- /dev/null +++ b/scripts/enter.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")/.." + +# .env 可选(订阅模式下完全无需 key) +if [[ ! -f .env ]]; then + cp .env.example .env + echo "已创建 .env(订阅模式无需填 key,直接继续)" +fi + +# 首次构建提示 +if ! docker image inspect claude-agent-6repos:latest >/dev/null 2>&1; then + echo ">>> 首次构建镜像(约 5 分钟,下载 Node/Python/Go/Bun 工具链)" + docker compose build +fi + +# 检查容器内是否已登录(claude-home 卷里有 .credentials.json) +LOGGED_IN=$(docker volume inspect ai-ops_claude-home >/dev/null 2>&1 \ + && docker run --rm -v ai-ops_claude-home:/h alpine \ + sh -c 'test -f /h/.credentials.json && echo yes || echo no' 2>/dev/null || echo no) + +if [[ "$LOGGED_IN" != "yes" && -z "${ANTHROPIC_API_KEY:-$(grep -E '^ANTHROPIC_API_KEY=sk-' .env 2>/dev/null || true)}" ]]; then + cat <<'EOF' + +>>> 首次登录提示: + 进入容器后执行 /login,用浏览器完成 Claude Max 订阅 OAuth。 + 凭证会持久化到 docker volume,下次直接复用。 + +EOF +fi + +exec docker compose run --rm claude-agent "$@" diff --git a/scripts/run-task.sh b/scripts/run-task.sh new file mode 100755 index 0000000..f6bbfe0 --- /dev/null +++ b/scripts/run-task.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# 非交互跑单任务:./scripts/run-task.sh "审计 6 个仓库的过时依赖并提 PR" +set -euo pipefail + +cd "$(dirname "$0")/.." + +if [[ $# -lt 1 ]]; then + echo "用法: $0 '<任务描述>'" + exit 1 +fi + +docker compose run --rm claude-agent \ + claude -p "$1" --permission-mode acceptEdits