fix: 全面审查修复 — 安装脚本/JSON配置/Docker/CI

P0 修复:
- install-mac.sh: 修复 create_agent_workspaces() 函数被错误粘贴的注入人设代码块破坏,
  提取为独立步骤;删除重复的 skill 安装块;移除 set -e 下的 $? 死代码
- gui/keepalive.sh: SERVER_DIR 路径从 gui/server 改为 server
- gui/server/keepalive.sh: SERVER_DIR 路径从 gui/server 改为当前目录
- package.json: 合并两个重复的 scripts 块,恢复 jest 为主 test 命令
- .dockerignore: 添加 !**/package-lock.json 例外,修复 npm ci 构建失败
- 删除 docker.yml(与 docker-build.yml 重复,tag push 会触发两次构建)

P1 修复:
- docker-build.yml: 升级 build-push-action v5->v6,启用 provenance/sbom,
  Trivy 锁定到 0.28.0(不用 @master),添加 build step id,workflow 级 permissions
- install.ps1: 配置路径从 ~/clawd/ 改为 ~/.openclaw/(与 Bash 脚本一致),
  MSI 路径引号防空格,添加 ErrorActionPreference,下载失败生成最小配置兜底
- configs/modern-ceo/openclaw.json: CMO agent id 从 cmk 改为 cmo(4处 typo)
- docker/entrypoint.sh: Python 代码改用环境变量传递路径,消除注入风险
- install-lite.sh: 移除 set -e 下的 $? 死代码
- ci.yml + duchayuan-review.yml: 添加 permissions: contents: read

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
chairman's chair
2026-03-26 23:25:27 +08:00
parent 1eea2aa38d
commit ac4b1e1d3f
13 changed files with 95 additions and 143 deletions

View File

@@ -55,6 +55,7 @@ memory/
logs/
*.pid
*.lock
!**/package-lock.json
# 嵌套仓库(可选,减小镜像体积)
meow/

View File

@@ -7,6 +7,9 @@ on:
pull_request:
branches: [main]
permissions:
contents: read
jobs:
lint-and-validate:
runs-on: ubuntu-latest

View File

@@ -6,6 +6,11 @@ on:
branches: [main]
workflow_dispatch:
permissions:
contents: read
packages: write
security-events: write
env:
REGISTRY: docker.io
IMAGE_NAME: boluobobo/ai-court
@@ -13,9 +18,6 @@ env:
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
@@ -47,7 +49,8 @@ jobs:
type=ref,event=tag
- name: Build and push
uses: docker/build-push-action@v5
id: build
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
@@ -57,20 +60,12 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BUILDKIT_INLINE_CACHE=1
provenance: false
sbom: false
provenance: true
sbom: true
- name: Image digest
run: echo ${{ steps.build.outputs.digest }}
- name: Check image size
if: always()
run: |
echo "Image size:"
docker images ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} --format "{{.Size}}"
security-scan:
runs-on: ubuntu-latest
needs: build
@@ -79,7 +74,7 @@ jobs:
steps:
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
uses: aquasecurity/trivy-action@0.28.0
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
format: 'sarif'

View File

@@ -1,37 +0,0 @@
name: Build & Push Docker (multi-arch)
on:
push:
tags: ['v*']
workflow_dispatch:
env:
DOCKERHUB_IMAGE: boluobobo/ai-court
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
username: boluobobo
password: ${{ secrets.DOCKERHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ env.DOCKERHUB_IMAGE }}:latest
${{ env.DOCKERHUB_IMAGE }}:${{ github.ref_name }}
cache-from: type=gha
cache-to: type=gha,mode=max
# [L-11] 供应链安全:添加 provenance 和 SBOM
provenance: true
sbom: true

View File

@@ -11,6 +11,9 @@ on:
- 'images/**'
- 'LICENSE'
permissions:
contents: read
jobs:
duchayuan-review:
runs-on: ubuntu-latest

View File

@@ -113,7 +113,7 @@
}
},
{
"id": "cmk",
"id": "cmo",
"name": "CMO",
"model": {
"primary": "your-provider/fast-model"
@@ -268,7 +268,7 @@
"applicationId": "YOUR_CFO_APPLICATION_ID",
"groupPolicy": "open"
},
"cmk": {
"cmo": {
"name": "CMO",
"token": "YOUR_CMO_BOT_TOKEN",
"applicationId": "YOUR_CMO_APPLICATION_ID",
@@ -359,7 +359,7 @@
"name": "COO",
"groupPolicy": "open"
},
"cmk": {
"cmo": {
"appId": "YOUR_CMO_APP_ID",
"appSecret": "YOUR_CMO_APP_SECRET",
"name": "CMO",
@@ -393,7 +393,7 @@
{ "agentId": "cto", "match": { "channel": "discord", "accountId": "cto" } },
{ "agentId": "coo", "match": { "channel": "discord", "accountId": "coo" } },
{ "agentId": "cfo", "match": { "channel": "discord", "accountId": "cfo" } },
{ "agentId": "cmk", "match": { "channel": "discord", "accountId": "cmk" } },
{ "agentId": "cmo", "match": { "channel": "discord", "accountId": "cmo" } },
{ "agentId": "eng", "match": { "channel": "discord", "accountId": "eng" } },
{ "agentId": "prod", "match": { "channel": "discord", "accountId": "prod" } },
{ "agentId": "qa", "match": { "channel": "discord", "accountId": "qa" } },

View File

@@ -182,7 +182,8 @@ echo ""
# 自动补全 gateway.mode缺少会导致启动失败
if command -v openclaw &>/dev/null && [ -f "$CONFIG_DIR/openclaw.json" ]; then
if ! grep -q '"gateway"' "$CONFIG_DIR/openclaw.json" 2>/dev/null || ! python3 -c "import json; d=json.load(open('$CONFIG_DIR/openclaw.json')); assert d.get('gateway',{}).get('mode')" 2>/dev/null; then
if ! grep -q '"gateway"' "$CONFIG_DIR/openclaw.json" 2>/dev/null || \
! CONFIG_PATH="$CONFIG_DIR/openclaw.json" python3 -c "import json,os; d=json.load(open(os.environ['CONFIG_PATH'])); assert d.get('gateway',{}).get('mode')" 2>/dev/null; then
echo "⚠ gateway.mode 未设置,自动设为 local..."
openclaw config set gateway.mode local 2>/dev/null || true
fi

View File

@@ -2,7 +2,7 @@
# Boluo GUI Server Keepalive
# Ensures node server/index.js stays running on port 18795
SERVER_DIR="$(cd "$(dirname "$0")/gui/server" && pwd)"
SERVER_DIR="$(cd "$(dirname "$0")/server" && pwd)"
LOG_FILE="/tmp/boluo-gui.log"
PID_FILE="/tmp/boluo-gui.pid"
CHECK_INTERVAL=15

View File

@@ -2,7 +2,7 @@
# Boluo GUI Server Keepalive
# Ensures node server/index.js stays running on port 18795
SERVER_DIR="$(cd "$(dirname "$0")/gui/server" && pwd)"
SERVER_DIR="$(cd "$(dirname "$0")" && pwd)"
LOG_FILE="/tmp/boluo-gui.log"
PID_FILE="/tmp/boluo-gui.pid"
CHECK_INTERVAL=15

View File

@@ -298,13 +298,8 @@ generate_config_from_template() {
process.stdout.write(out + '\n');
" "$mode" > "$output"
if [ $? -eq 0 ]; then
chmod 600 "$output"
echo -e " ${GREEN}${mode} 模式配置已生成${NC}"
else
echo -e " ${RED}✗ 配置生成失败${NC}"
return 1
fi
chmod 600 "$output"
echo -e " ${GREEN}${mode} 模式配置已生成${NC}"
}
if [ -f "$CONFIG_DIR/$CONFIG_FILE" ]; then

View File

@@ -166,38 +166,6 @@ create_agent_workspaces() {
local workspaces
workspaces=$(jq -r '.agents.list[]? | "\(.id):\(.workspace // empty)"' "$config_file" 2>/dev/null)
for entry in $workspaces; do
# ---- 注入人设(从 agents/*.md 文件)----
echo ""
echo -e "${YELLOW}[2.5/5] 注入人设...${NC}"
TEMPLATE_AGENTS_DIR="$WORKSPACE/configs/ming-neige/agents"
if [ -d "$TEMPLATE_AGENTS_DIR" ] && [ -f "$CONFIG_DIR/$CONFIG_FILE" ]; then
echo -e " ${CYAN}正在从独立文件注入人设...${NC}"
agent_count=$(jq '.agents.list | length' "$CONFIG_DIR/$CONFIG_FILE" 2>/dev/null || echo "0")
injected=0
for ((i=0; i<agent_count; i++)); do
agent_id=$(jq -r ".agents.list[$i].id" "$CONFIG_DIR/$CONFIG_FILE" 2>/dev/null)
persona_file="$TEMPLATE_AGENTS_DIR/${agent_id}.md"
if [ -f "$persona_file" ]; then
persona=$(tail -n +3 "$persona_file")
persona_escaped=$(echo "$persona" | jq -Rs '.')
jq --argjson idx "$i" --argjson persona "$persona_escaped" \
".agents.list[$idx].identity.theme = \$persona" \
"$CONFIG_DIR/$CONFIG_FILE" > "${CONFIG_DIR}/${CONFIG_FILE}.tmp" && mv "${CONFIG_DIR}/${CONFIG_FILE}.tmp" "$CONFIG_DIR/$CONFIG_FILE"
echo -e " ${GREEN}${NC} $agent_id"
injected=$((injected + 1))
fi
done
echo -e " ${GREEN}${NC} 已注入 $injected 个人设"
else
echo -e " ${YELLOW}${NC} 人设目录不存在,使用模板中的内置人设"
fi
local aws="${entry##*:}"
aws="${aws/\$HOME/$HOME}"
if [ -n "$aws" ] && [ "$aws" != "$WORKSPACE" ]; then
@@ -252,34 +220,6 @@ SOUL_EOF
echo -e " ${GREEN}✓ SOUL.md 已创建${NC}"
fi
# ---- 安装默认 Skill: self-improving ----
echo ""
echo -e "${YELLOW}安装默认 Skill...${NC}"
if ! command -v clawdhub &>/dev/null; then
npm install -g clawdhub 2>/dev/null || true
fi
if command -v clawdhub &>/dev/null; then
# 主工作区
clawdhub install self-improving --workdir "$WORKSPACE" --force 2>/dev/null && \
echo -e " ${GREEN}✓ self-improving 已安装到主工作区${NC}" || \
echo -e " ${YELLOW}⚠ 主工作区 skill 安装失败,可稍后手动安装: clawdhub install self-improving${NC}"
mkdir -p "$WORKSPACE/.learnings"
# 各部门工作区
if [ -f "$CONFIG_DIR/$CONFIG_FILE" ] && command -v jq &>/dev/null; then
SKILL_AGENT_WORKSPACES=$(jq -r '.agents.list[]? | .workspace // empty' "$CONFIG_DIR/$CONFIG_FILE" 2>/dev/null)
echo "$SKILL_AGENT_WORKSPACES" | while IFS= read -r SKILL_WS; do
[ -z "$SKILL_WS" ] && continue
SKILL_WS="${SKILL_WS/\$HOME/$HOME}"
[ "$SKILL_WS" = "$WORKSPACE" ] && continue
clawdhub install self-improving --workdir "$SKILL_WS" --force 2>/dev/null
mkdir -p "$SKILL_WS/.learnings"
done
echo -e " ${GREEN}✓ self-improving 已安装到所有工作区${NC}"
fi
else
echo -e " ${YELLOW}⚠ clawdhub 未安装,跳过 skill 安装。安装后运行: clawdhub install self-improving${NC}"
fi
# IDENTITY.md
if [ ! -f "$WORKSPACE/IDENTITY.md" ]; then
cat > "$WORKSPACE/IDENTITY.md" << 'ID_EOF'
@@ -416,13 +356,8 @@ generate_config_from_template() {
process.stdout.write(out + '\n');
" "$mode" > "$output"
if [ $? -eq 0 ]; then
chmod 600 "$output"
echo -e " ${GREEN}${mode} 模式配置已生成${NC}"
else
echo -e " ${RED}✗ 配置生成失败${NC}"
return 1
fi
chmod 600 "$output"
echo -e " ${GREEN}${mode} 模式配置已生成${NC}"
}
if [ -f "$CONFIG_DIR/$CONFIG_FILE" ]; then
@@ -436,7 +371,37 @@ else
generate_config_from_template "$CONFIG_MODE" "$CONFIG_DIR/$CONFIG_FILE"
fi
# ---- 注入人设(从 agents/*.md 文件)----
echo ""
echo -e "${YELLOW}注入人设...${NC}"
TEMPLATE_AGENTS_DIR="$WORKSPACE/configs/ming-neige/agents"
if [ -d "$TEMPLATE_AGENTS_DIR" ] && [ -f "$CONFIG_DIR/$CONFIG_FILE" ] && command -v jq &>/dev/null; then
echo -e " ${CYAN}正在从独立文件注入人设...${NC}"
agent_count=$(jq '.agents.list | length' "$CONFIG_DIR/$CONFIG_FILE" 2>/dev/null || echo "0")
injected=0
for ((i=0; i<agent_count; i++)); do
agent_id=$(jq -r ".agents.list[$i].id" "$CONFIG_DIR/$CONFIG_FILE" 2>/dev/null)
persona_file="$TEMPLATE_AGENTS_DIR/${agent_id}.md"
if [ -f "$persona_file" ]; then
persona=$(tail -n +3 "$persona_file")
persona_escaped=$(echo "$persona" | jq -Rs '.')
jq --argjson idx "$i" --argjson persona "$persona_escaped" \
".agents.list[\$idx].identity.theme = \$persona" \
"$CONFIG_DIR/$CONFIG_FILE" > "${CONFIG_DIR}/${CONFIG_FILE}.tmp" && mv "${CONFIG_DIR}/${CONFIG_FILE}.tmp" "$CONFIG_DIR/$CONFIG_FILE"
echo -e " ${GREEN}${NC} $agent_id"
injected=$((injected + 1))
fi
done
echo -e " ${GREEN}${NC} 已注入 $injected 个人设"
else
echo -e " ${YELLOW}${NC} 人设目录不存在或缺少 jq使用模板中的内置人设"
fi
# ---- 可选:安装 Dashboard Web UI ----
echo ""

View File

@@ -7,6 +7,8 @@ param(
[switch]$Help
)
$ErrorActionPreference = "Stop"
if ($Help) {
Write-Host @"
AI Windows
@@ -90,7 +92,7 @@ if (Test-NodeJs) {
try {
Invoke-WebRequest -Uri $installerUrl -OutFile $installerPath -UseBasicParsing
Write-Info "正在运行安装器..."
Start-Process msiexec.exe -Wait -ArgumentList "/i $installerPath /quiet /norestart"
Start-Process msiexec.exe -Wait -ArgumentList "/i `"$installerPath`" /quiet /norestart"
Remove-Item $installerPath -Force
# 刷新环境变量
@@ -132,14 +134,40 @@ if (-not (Test-Path $workspacePath)) {
# ============================================
Write-Info "正在下载 AI 朝廷配置..."
$configDir = Join-Path $env:USERPROFILE ".openclaw"
if (-not (Test-Path $configDir)) {
New-Item -ItemType Directory -Path $configDir | Out-Null
}
$configUrl = "https://raw.githubusercontent.com/wanikua/danghuangshang/main/openclaw.example.json"
$configPath = Join-Path $workspacePath "openclaw.json"
$configPath = Join-Path $configDir "openclaw.json"
try {
Invoke-WebRequest -Uri $configUrl -OutFile $configPath -UseBasicParsing
Write-Success "配置文件已下载:$configPath"
} catch {
Write-Warn "配置文件下载失败,将手动创建模板"
Write-Warn "配置文件下载失败,生成最小配置模板"
$minimalConfig = @'
{
"models": {
"providers": {
"your-provider": {
"baseUrl": "https://your-llm-provider-api-url",
"apiKey": "YOUR_LLM_API_KEY",
"api": "openai",
"models": [{ "id": "fast-model", "name": "快速模型", "contextWindow": 200000, "maxTokens": 8192 }]
}
}
},
"gateway": { "mode": "local", "port": 18789 },
"agents": {
"defaults": { "workspace": "$workspacePath", "skipBootstrap": true, "model": { "primary": "your-provider/fast-model" } },
"list": [{ "id": "silijian", "name": "司礼监", "identity": { "theme": "你是AI朝廷的总管负责日常对话和任务调度。回答用中文简洁高效。" } }]
}
}
'@
$minimalConfig | Out-File -FilePath $configPath -Encoding UTF8
Write-Success "最小配置模板已生成:$configPath"
}
# ============================================

View File

@@ -6,18 +6,16 @@
"scripts": {
"test": "jest",
"test:coverage": "jest --coverage",
"test:watch": "jest --watch"
"test:watch": "jest --watch",
"test:manual": "node tests/test-task-store.js && node tests/test-context-compressor.js",
"lint": "bash -n scripts/*.sh && node -c scripts/*.js 2>/dev/null || true",
"health": "bash scripts/health-check.sh",
"cleanup": "bash scripts/cleanup-repo.sh"
},
"bin": {
"task-store": "./scripts/task-store.js",
"context-compressor": "./scripts/context-compressor.js"
},
"scripts": {
"test": "node tests/test-task-store.js && node tests/test-context-compressor.js",
"lint": "bash -n scripts/*.sh && node -c scripts/*.js 2>/dev/null || true",
"health": "bash scripts/health-check.sh",
"cleanup": "bash scripts/cleanup-repo.sh"
},
"keywords": [
"ai",
"multi-agent",