chore: sync local project state

This commit is contained in:
Your Name
2026-05-12 18:49:52 +08:00
parent afdbea6fb5
commit 1c0084afe8
105 changed files with 13221 additions and 420 deletions

View File

@@ -0,0 +1,116 @@
#!/usr/bin/env bash
set -euo pipefail
BASE_URL="${BASE_URL:-http://127.0.0.1:8080}"
CONSUMER="${CONSUMER:-gateway}"
APPLIED_RATIO_THRESHOLD="${APPLIED_RATIO_THRESHOLD:-0.95}"
FAILED_BURST_THRESHOLD="${FAILED_BURST_THRESHOLD:-3}"
PENDING_RETRY_THRESHOLD="${PENDING_RETRY_THRESHOLD:-10}"
need() {
command -v "$1" >/dev/null 2>&1 || {
echo "missing required command: $1" >&2
exit 1
}
}
need curl
need python3
health=$(curl -fsS "$BASE_URL/healthz")
metrics=$(curl -fsS "$BASE_URL/metrics")
status=$(curl -fsS "$BASE_URL/internal/supply-intelligence/gateway/runtime-status")
echo "=== healthz ==="
echo "$health"
echo "=== runtime status ==="
echo "$status"
echo "=== metrics excerpt ==="
printf '%s
' "$metrics" | grep 'supply_intelligence_gateway_' || true
export METRICS_TEXT="$metrics"
export RUNTIME_STATUS_JSON="$status"
export CONSUMER
export APPLIED_RATIO_THRESHOLD
export FAILED_BURST_THRESHOLD
export PENDING_RETRY_THRESHOLD
python3 <<'PY'
import json
import os
import re
import sys
metrics = os.environ['METRICS_TEXT']
status = json.loads(os.environ['RUNTIME_STATUS_JSON'])
consumer = os.environ['CONSUMER']
ratio_threshold = float(os.environ['APPLIED_RATIO_THRESHOLD'])
failed_threshold = int(os.environ['FAILED_BURST_THRESHOLD'])
pending_threshold = int(os.environ['PENDING_RETRY_THRESHOLD'])
processed = {}
for line in metrics.splitlines():
if not line.startswith('supply_intelligence_gateway_events_processed_total'):
continue
head, _, tail = line.rpartition(' ')
if not tail:
continue
m = re.search(r'\{([^}]*)\}$', head)
if not m:
continue
labels = {}
for part in m.group(1).split(','):
if '=' not in part:
continue
k, v = part.split('=', 1)
labels[k.strip()] = v.strip().strip('"')
result_label = labels.get('result')
if not result_label:
continue
processed[result_label] = processed.get(result_label, 0.0) + float(tail)
pending_retry = 0.0
failed_events = 0.0
for line in metrics.splitlines():
if line.startswith('supply_intelligence_gateway_pending_retry_events') and f'consumer="{consumer}"' in line:
pending_retry = float(line.rsplit(' ', 1)[-1])
if line.startswith('supply_intelligence_gateway_failed_events') and f'consumer="{consumer}"' in line:
failed_events = float(line.rsplit(' ', 1)[-1])
total_terminal = processed.get('applied', 0.0) + processed.get('failed', 0.0)
applied_ratio = (processed.get('applied', 0.0) / total_terminal) if total_terminal > 0 else 1.0
decision = 'continue'
reasons = []
if not status.get('started', False):
decision = 'pause'
reasons.append('runtime_not_started')
if status.get('last_error'):
decision = 'pause'
reasons.append('runtime_last_error')
if pending_retry > pending_threshold:
decision = 'pause'
reasons.append('pending_retry_threshold_exceeded')
if applied_ratio < ratio_threshold:
decision = 'pause'
reasons.append('applied_ratio_below_threshold')
if failed_events >= failed_threshold:
decision = 'rollback'
reasons.append('failed_events_threshold_exceeded')
print(json.dumps({
'decision': decision,
'reasons': reasons,
'applied_ratio': applied_ratio,
'processed': processed,
'pending_retry_events': pending_retry,
'failed_events': failed_events,
'runtime': status,
}, ensure_ascii=False, indent=2))
if decision == 'rollback':
sys.exit(2)
if decision == 'pause':
sys.exit(1)
PY

View File

@@ -0,0 +1,33 @@
#!/usr/bin/env bash
set -euo pipefail
BASE_URL="${BASE_URL:-http://127.0.0.1:8080}"
need() {
command -v "$1" >/dev/null 2>&1 || {
echo "missing required command: $1" >&2
exit 1
}
}
need curl
need python3
echo "[1/3] pause gateway runtime"
curl -fsS -X POST "$BASE_URL/internal/supply-intelligence/gateway/runtime/pause"
echo
echo "[2/3] fetch runtime status for rollback assessment"
status=$(curl -fsS "$BASE_URL/internal/supply-intelligence/gateway/runtime-status")
echo "$status"
echo "[3/3] operator checklist"
python3 <<'PY'
print('''Manual rollback checklist:
1. Confirm runtime paused and record pending_retry_events / failed_events.
2. Inspect GET /internal/supply-intelligence/gateway/package-changes for the affected event IDs.
3. If a replacement package is prepared, publish the replacement package-event and verify admission-state.
4. If the bad event must remain blocked, keep runtime paused until manual remediation is completed.
5. After remediation, call POST /internal/supply-intelligence/gateway/runtime/resume and rerun gateway_closure_inspect.sh.
''')
PY

View File

@@ -0,0 +1,76 @@
#!/usr/bin/env bash
set -euo pipefail
BASE_URL="${BASE_URL:-http://127.0.0.1:8080}"
PLATFORM="${PLATFORM:-openai}"
MODEL="${MODEL:-gpt-4.1-mini}"
EVENT_ID="${EVENT_ID:-evt-smoke-$(date +%s)}"
OCCURRED_AT="${OCCURRED_AT:-$(date -u +%Y-%m-%dT%H:%M:%SZ)}"
CANDIDATE_STATUS_EXPECTED="${CANDIDATE_STATUS_EXPECTED:-published}"
need() {
command -v "$1" >/dev/null 2>&1 || {
echo "missing required command: $1" >&2
exit 1
}
}
need curl
need python3
json_get() {
local expr="$1"
python3 -c "import json,sys; data=json.load(sys.stdin); print($expr)"
}
echo "[1/4] publish package event"
publish_resp=$(curl -fsS -X POST "$BASE_URL/internal/supply-intelligence/publish/package-event" \
-H 'Content-Type: application/json' \
-d "{\"event_id\":\"$EVENT_ID\",\"platform\":\"$PLATFORM\",\"model\":\"$MODEL\",\"occurred_at\":\"$OCCURRED_AT\"}")
echo "$publish_resp"
publish_event_id=$(printf '%s' "$publish_resp" | json_get "data['event']['event_id']")
[ "$publish_event_id" = "$EVENT_ID" ] || {
echo "publish returned unexpected event id: $publish_event_id" >&2
exit 1
}
echo "[2/4] trigger consume-once"
consume_resp=$(curl -fsS -X POST "$BASE_URL/internal/supply-intelligence/gateway/consume-once" \
-H 'Content-Type: application/json' \
-d '{"consumer":"gateway"}')
echo "$consume_resp"
consume_items=$(printf '%s' "$consume_resp" | json_get "len(data['items'])")
[ "$consume_items" -ge 1 ] || {
echo "consume-once returned no items" >&2
exit 1
}
echo "[3/4] verify package change list includes event"
changes_resp=$(curl -fsS "$BASE_URL/internal/supply-intelligence/gateway/package-changes")
echo "$changes_resp"
found=$(printf '%s' "$changes_resp" | python3 -c "import json,sys; data=json.load(sys.stdin); print(any(item.get('event_id') == '$EVENT_ID' for item in data.get('items', [])))")
[ "$found" = "True" ] || {
echo "package change list missing event $EVENT_ID" >&2
exit 1
}
echo "[4/4] verify admission-state reflects publish/consume state"
admission_resp=$(curl -fsS "$BASE_URL/internal/supply-intelligence/models/$PLATFORM/$MODEL/admission-state")
echo "$admission_resp"
candidate_status=$(printf '%s' "$admission_resp" | json_get "data['candidate']['status'] if data.get('candidate') else ''")
gateway_status=$(printf '%s' "$admission_resp" | json_get "data.get('gateway_sync_status', '')")
[ "$candidate_status" = "$CANDIDATE_STATUS_EXPECTED" ] || {
echo "unexpected candidate status: $candidate_status" >&2
exit 1
}
case "$gateway_status" in
applied|pending|failed) ;;
*)
echo "unexpected gateway sync status: $gateway_status" >&2
exit 1
;;
esac
echo "gateway closure smoke passed: event=$EVENT_ID candidate_status=$candidate_status gateway_sync_status=$gateway_status"

View File

@@ -0,0 +1,55 @@
# Hermes Daily Review Prompt
目标:基于当前仓库真实状态,对 `supply-intelligence` 做一次严谨的日度 review并输出专业报告与 Hermes 优化建议。
执行要求:
1. 只基于真实事实,不基于记忆或假设。
2. 这个 review 默认**不更新任何 TASKS/GOALS 状态**,只产出报告与建议。
3. 如果后续用户明确要求同步任务状态,而且本项目已经引入项目内 `TASKS.md` / `GOALS.md`
- 只能写项目内任务文件,禁止写 `~/.openclaw/workspace/TASKS.md``~/.openclaw/workspace/GOALS.md`
- 写回前必须先执行:
- `bash /home/long/.openclaw/workspace/scripts/preflight_task_write_guard.sh project-review /home/long/project/supply-intelligence /home/long/project/supply-intelligence/TASKS.md`
- 守卫失败时立即停止,不得继续 `edit``write`
4. 必须先检查:
- `git status --short`
- 最近提交记录
- 当前关键文档与脚本目录
- 当前可执行的验证命令
5. 优先执行非破坏性验证:
- `go build ./...`
- `go test ./...`
- 如果有更贴近真实链路的校验脚本,也可以补充执行
6. 如果命令失败,记录精确失败点、失败命令、错误摘要,不得模糊描述。
7. 这个 review 任务只产出报告与建议,不改业务代码;如果发现必须立即修复的问题,只在报告中列出。
输出文件:
1. 每日 review 报告:
- 路径:`reports/hermes/YYYY-MM-DD-review.md`
- 如果当天文件已存在,则覆盖为最新真实状态
2. Hermes 优化建议文档:
- 路径:`reports/hermes/HERMES_OPTIMIZATION_SUGGESTIONS.md`
- 追加或更新当天小节
`YYYY-MM-DD-review.md` 必须包含:
- 标题与时间
- Executive Summary
- 当前真实完成度判断
- 今日验证证据
- 已完成事项
- 进行中事项
- 阻塞项与风险
- 发现的文档/实现偏差
- 下一步最值得推进的 3 件事
`HERMES_OPTIMIZATION_SUGGESTIONS.md` 必须包含:
- 日期
- 本次 review 暴露出的 Hermes 工作方式问题
- 每个问题的优化建议
- 优先级P0/P1/P2
- 建议的验证方式
完成后,在最终回复中只做简洁摘要,并明确写出生成/更新了哪些文件。

106
scripts/run_migrations.sh Normal file
View File

@@ -0,0 +1,106 @@
#!/bin/bash
# Migration runner for supply-intelligence
# Supports both in-memory mode (no DB) and PostgreSQL mode (via DATABASE_URL)
#
# Usage:
# ./scripts/run_migrations.sh # runs all pending migrations
# ./scripts/run_migrations.sh --status # show migration status
# ./scripts/run_migrations.sh --baseline <id> # baseline an existing DB
set -e
PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
MIGRATIONS_DIR="${PROJECT_DIR}/migrations"
DATABASE_URL="${DATABASE_URL:-}"
# Resolve absolute path to migrations folder
MIGRATIONS_DIR="$(cd "$MIGRATIONS_DIR" && pwd)"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log_info() { echo -e "${GREEN}[INFO]${NC} $*"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
log_error() { echo -e "${RED}[ERR]${NC} $*" >&2; }
run_postgres_migrations() {
if [ -z "$DATABASE_URL" ]; then
log_error "DATABASE_URL not set. Cannot run SQL migrations."
log_info "Set DATABASE_URL to run PostgreSQL migrations."
return 1
fi
local conn="$DATABASE_URL"
local db_name
db_name=$(echo "$conn" | sed -E 's|.*/([^?]+)(\?.*)?|\1|')
echo "CREATE TABLE IF NOT EXISTS schema_history (
installed_rank INTEGER PRIMARY KEY,
version VARCHAR(50),
description VARCHAR(200),
type VARCHAR(20),
script VARCHAR(1000),
checksum BIGINT,
installed_by VARCHAR(100),
installed_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
execution_time_ms BIGINT,
success SMALLINT
);" | PGPASSWORD="${PGPASSWORD:-}" psql -h "${PGHOST:-localhost}" -U "${PGUSER:-supply}" -d "$db_name" 2>/dev/null || true
log_info "PostgreSQL migration runner ready"
log_info "DB: $db_name"
log_info "Migrations dir: $MIGRATIONS_DIR"
local count=0
for f in "$MIGRATIONS_DIR"/*.sql; do
[ -e "$f" ] || continue
echo " $(basename "$f")"
count=$((count + 1))
done
log_info "Found $count SQL migration file(s)"
}
run_inmemory_migrations() {
log_info "In-memory mode: migrations are embedded in application startup"
log_info "Set DATABASE_URL to enable PostgreSQL migration runner"
echo ""
echo "Available migrations in $MIGRATIONS_DIR:"
local count=0
for f in "$MIGRATIONS_DIR"/*.sql; do
[ -e "$f" ] || continue
echo " $(basename "$f")"
count=$((count + 1))
done
log_info "Total: $count migration(s)"
}
main() {
case "${1:-}" in
--status)
if [ -n "$DATABASE_URL" ]; then
log_info "PostgreSQL mode"
run_postgres_migrations
else
log_info "In-memory mode (no DATABASE_URL)"
run_inmemory_migrations
fi
;;
--baseline)
log_warn "Baseline not implemented — use golang-migrate or flyway"
;;
*)
if [ -n "$DATABASE_URL" ]; then
log_info "Running PostgreSQL migrations..."
run_postgres_migrations
else
log_info "No DATABASE_URL — showing available migrations"
run_inmemory_migrations
fi
;;
esac
}
main "$@"

47
scripts/sub2api-bridge.sh Normal file
View File

@@ -0,0 +1,47 @@
#!/bin/bash
set -euo pipefail
SUPPLY_URL="${SUPPLY_URL:-http://127.0.0.1:8081}"
CONSUMER="${CONSUMER:-sub2api-bridge}"
CURSOR=""
# Create bridge log table in sub2api database
docker exec sub2api-postgres psql -U sub2api -d sub2api -c "
CREATE TABLE IF NOT EXISTS supply_bridge_log (
id SERIAL PRIMARY KEY,
event_id TEXT NOT NULL,
package_id BIGINT,
status TEXT,
result TEXT,
detail TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);" 2>/dev/null || true
while true; do
RESP=$(curl -fsS -X POST "${SUPPLY_URL}/internal/supply-intelligence/gateway/consume-once?consumer=${CONSUMER}&cursor=${CURSOR}" 2>/dev/null || echo '{}')
NEXT_CURSOR=$(echo "$RESP" | jq -r '.next_cursor // empty')
ITEMS_LEN=$(echo "$RESP" | jq '.items | length')
if [ "$ITEMS_LEN" -eq 0 ]; then
sleep 10
continue
fi
echo "$RESP" | jq -c '.items[]' | while read -r item; do
EVENT_ID=$(echo "$item" | jq -r '.event_id')
PKG_ID=$(echo "$item" | jq -r '.package_id')
STATUS=$(echo "$item" | jq -r '.gateway_sync_status')
RESULT=$(echo "$item" | jq -r '.result')
DETAIL=$(echo "$item" | jq -r '.detail // empty')
echo "$(date -Is) bridge event=$EVENT_ID package=$PKG_ID status=$STATUS result=$RESULT"
# Insert into sub2api database
docker exec sub2api-postgres psql -U sub2api -d sub2api -c \
"INSERT INTO supply_bridge_log (event_id, package_id, status, result, detail) VALUES ('$EVENT_ID', $PKG_ID, '$STATUS', '$RESULT', '$DETAIL');" 2>/dev/null || true
done
CURSOR="$NEXT_CURSOR"
if [ -z "$CURSOR" ]; then
sleep 10
fi
done