c2638668e3
leo 2026-08-20:「同一個 plugin 你用,薄殼也用,保證兩邊同步」
「我要你幫雲端做薄殼,永遠都有問題,你要做的就是這組設定
你自己可以 dogfooding」
搬進來:41 支 hook(51 條註冊)/7 支 command/2 支 skill/23 支腳本。
不搬 .env、wiki、docs——那些是知識不是環境。
51 條 hook 路徑全部從 $CLAUDE_PROJECT_DIR/.claude/hooks/ 改成 ${CLAUDE_PLUGIN_ROOT}/hooks/,
零漏網。那正是薄殼一直壞掉的根:雲端 cwd 不是真身,寫死路徑就斷。
尚未驗證:Claude Code 能不能從私有 Gitea repo 裝 marketplace(要憑證)。
下一步就是在本機實際裝一次,通了才動雲端 bootstrap.sh。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
146 lines
6.7 KiB
Bash
Executable File
146 lines
6.7 KiB
Bash
Executable File
#!/bin/bash
|
||
# gitea-arm-request.sh — 開一個「等 leo 在 Gitea 上回覆」的請求
|
||
#
|
||
# 用法:scripts/gitea-arm-request.sh <票號> "任務描述" [有效分鐘,預設30,上限60]
|
||
#
|
||
# 這支**machine 自己就能跑**(不像 scripts/github-arm.sh 要求終端機互動)——
|
||
# 因為「請求」本身不是安全邊界,**leo 用 Leo 帳號回覆才是**。
|
||
# 這支只負責:生一組一次性代碼、貼上呼叫端指定的那張 Gitea 票、把代碼與人話訊息印出來
|
||
# (讓呼叫者拿去用 notify_leo/Telegram 轉告 leo;本支不碰 Telegram)。
|
||
#
|
||
# 🪦 2026-08-16 起票號不再寫死(原本固定貼 inkstone/InkStoneCo#34「頻道票」)——
|
||
# leo:「我不要把所有的票都放在一個 issues,不然就難 track 歷史記錄」。
|
||
# **解哪張票的保險,就把請求貼在那張票上**:要出貨 X 就傳 X 那張票的號碼,
|
||
# 不要再統一貼 #34(#34 保留 open 當歷史,見 issues/34#issuecomment-2804)。
|
||
#
|
||
# 之後由 scripts/gitea-arm-check.sh 核對 leo 有沒有在同一張票回覆同一組代碼。
|
||
set -euo pipefail
|
||
|
||
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
|
||
# shellcheck source=lib/gitea-arm-common.sh
|
||
. "$SCRIPT_DIR/lib/gitea-arm-common.sh"
|
||
|
||
ISSUE="${1:-}"
|
||
MISSION="${2:-}"
|
||
MINUTES="${3:-30}"
|
||
if [ -z "$ISSUE" ] || [ -z "$MISSION" ]; then
|
||
echo "用法:scripts/gitea-arm-request.sh <票號> \"任務描述\" [分鐘,預設30,上限60]" >&2
|
||
echo "例:scripts/gitea-arm-request.sh 41 \"出 prod:xxx\"" >&2
|
||
exit 1
|
||
fi
|
||
# 票號可寫 `N`(預設 InkStoneCo)或 `repo#N`(同 org 的別的 repo,例:arcrun-rag#115)
|
||
case "$ISSUE" in
|
||
*"#"*)
|
||
_R="${ISSUE%%#*}"; ISSUE="${ISSUE##*#}"
|
||
gitea_arm_set_repo "$_R" || exit 1;;
|
||
esac
|
||
if ! gitea_arm_valid_issue "$ISSUE"; then
|
||
echo "❌ 票號要是純數字,或 repo#N(收到:$ISSUE)——解哪張票的保險就填那張票" >&2
|
||
exit 1
|
||
fi
|
||
case "$MINUTES" in
|
||
''|*[!0-9]*)
|
||
echo "❌ 第三個參數要純數字分鐘(收到:$MINUTES)" >&2
|
||
exit 1;;
|
||
esac
|
||
[ "$MINUTES" -le 60 ] || MINUTES=60
|
||
[ "$MINUTES" -ge 1 ] || MINUTES=1
|
||
|
||
TOKEN=$(gitea_arm_token) || { echo "❌ 讀不到 GITEA_TOKEN_CLAUDE_CODE(頂層 .env)" >&2; exit 1; }
|
||
|
||
NONCE="ARM-$(python3 -c 'import secrets; print(secrets.token_hex(4))')"
|
||
NOW=$(date +%s)
|
||
EXPIRES=$((NOW + MINUTES * 60))
|
||
EXPIRES_HUMAN=$(date -r "$EXPIRES" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || date -d "@$EXPIRES" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "$EXPIRES")
|
||
|
||
STATE_DIR=$(gitea_arm_state_dir)
|
||
PENDING_FILE="$STATE_DIR/pending/${NONCE}.json"
|
||
|
||
# ── 貼留言到呼叫端指定的那張票 ─────────────────────────────────────────
|
||
COMMENT_BODY=$(MISSION="$MISSION" NONCE="$NONCE" EXPIRES_HUMAN="$EXPIRES_HUMAN" MINUTES="$MINUTES" python3 -c '
|
||
import os
|
||
mission = os.environ["MISSION"]
|
||
nonce = os.environ["NONCE"]
|
||
expires_human = os.environ["EXPIRES_HUMAN"]
|
||
minutes = os.environ["MINUTES"]
|
||
print(f"""🔐 ARM 請求
|
||
|
||
任務:{mission}
|
||
有效時限:{minutes} 分鐘內({expires_human} 前)
|
||
|
||
📱 回這則、貼上下面這串就解鎖這一次(用完就失效,別的請求不能借用):
|
||
{nonce}
|
||
""")
|
||
')
|
||
|
||
REQUEST_JSON=$(jq -n --arg body "$COMMENT_BODY" '{body: $body}')
|
||
|
||
RESP=$(curl -s -w '\n%{http_code}' -X POST \
|
||
-H "Authorization: token $TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d "$REQUEST_JSON" \
|
||
"$GITEA_ARM_API/repos/$GITEA_ARM_OWNER/$GITEA_ARM_REPO/issues/$ISSUE/comments")
|
||
HTTP_CODE=$(printf '%s' "$RESP" | tail -1)
|
||
RESP_BODY=$(printf '%s' "$RESP" | sed '$d')
|
||
|
||
if [ "$HTTP_CODE" != "201" ]; then
|
||
echo "❌ 貼留言失敗(HTTP $HTTP_CODE),沒有建立請求——票號 #$ISSUE 存在嗎?" >&2
|
||
echo "$RESP_BODY" >&2
|
||
exit 1
|
||
fi
|
||
|
||
COMMENT_ID=$(printf '%s' "$RESP_BODY" | jq -r '.id // empty')
|
||
CREATED_AT=$(printf '%s' "$RESP_BODY" | jq -r '.created_at // empty')
|
||
|
||
if [ -z "$COMMENT_ID" ] || [ -z "$CREATED_AT" ]; then
|
||
echo "❌ Gitea 回應解不出留言 id/時間,視為失敗(fail-closed,沒寫本地狀態)" >&2
|
||
exit 1
|
||
fi
|
||
|
||
jq -n \
|
||
--arg nonce "$NONCE" \
|
||
--arg mission "$MISSION" \
|
||
--arg issue "$ISSUE" \
|
||
--arg repo "$GITEA_ARM_REPO" \
|
||
--argjson requested_at "$NOW" \
|
||
--argjson expires_at "$EXPIRES" \
|
||
--arg request_comment_id "$COMMENT_ID" \
|
||
--arg request_created_at "$CREATED_AT" \
|
||
'{nonce:$nonce, mission:$mission, issue:$issue, repo:$repo, requested_at:$requested_at, expires_at:$expires_at,
|
||
request_comment_id:$request_comment_id, request_created_at:$request_created_at}' \
|
||
> "$PENDING_FILE"
|
||
|
||
# ── 貼完請求就指派給 leo + 掛 Human(leo 2026-08-16:「沒有指派,沒有加 Human」)──
|
||
#
|
||
# 為什麼要自動:總管同一天才立下「指派給 leo 的每一張都要真的在等他」,
|
||
# 貼完 ARM 就忘了做 ⇒ leo 的「指派給您的」清單漏掉那張真正在等他的票。
|
||
# ⇒ 這件事不該靠人記得:**貼出 ARM 請求 = 這張票此刻在等 leo**,兩者是同一件事。
|
||
_LBL=$(curl -s -H "Authorization: token $TOKEN" \
|
||
"$GITEA_ARM_API/repos/$GITEA_ARM_OWNER/$GITEA_ARM_REPO/labels?limit=60" \
|
||
| jq -r '.[] | select(.name=="Human") | .id' 2>/dev/null | head -1)
|
||
_CUR=$(curl -s -H "Authorization: token $TOKEN" \
|
||
"$GITEA_ARM_API/repos/$GITEA_ARM_OWNER/$GITEA_ARM_REPO/issues/$ISSUE" \
|
||
| jq -r '[.labels[].id] | @csv' 2>/dev/null | tr -d '"')
|
||
if [ -n "$_LBL" ]; then
|
||
_IDS=$(printf '%s,%s' "${_CUR:-}" "$_LBL" | tr ',' '\n' | grep -E '^[0-9]+$' | sort -u | paste -sd, -)
|
||
curl -s -o /dev/null -X PUT -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
|
||
-d "{\"labels\":[${_IDS}]}" \
|
||
"$GITEA_ARM_API/repos/$GITEA_ARM_OWNER/$GITEA_ARM_REPO/issues/$ISSUE/labels"
|
||
fi
|
||
curl -s -o /dev/null -X PATCH -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
|
||
-d "{\"assignees\":[\"$GITEA_ARM_APPROVER_LOGIN\"]}" \
|
||
"$GITEA_ARM_API/repos/$GITEA_ARM_OWNER/$GITEA_ARM_REPO/issues/$ISSUE"
|
||
echo " (已指派給 $GITEA_ARM_APPROVER_LOGIN 並掛上 Human——他的「指派給您的」看得到這張)"
|
||
|
||
echo "✅ 已在 Gitea 貼出請求:https://git.uncle6.me/$GITEA_ARM_OWNER/$GITEA_ARM_REPO/issues/$ISSUE"
|
||
echo ""
|
||
echo "── 轉告 leo(照這樣發,一句狀況+一個可回的詞)──────────────────"
|
||
echo "[總管] 需要你解一道鎖:$MISSION"
|
||
echo "回這則 Gitea 留言貼「$NONCE」就好($MINUTES 分鐘內有效):https://git.uncle6.me/$GITEA_ARM_OWNER/$GITEA_ARM_REPO/issues/$ISSUE"
|
||
echo "──────────────────────────────────────────────────────────────"
|
||
echo ""
|
||
echo "nonce=$NONCE"
|
||
echo "issue=$ISSUE"
|
||
echo "expires_at=$EXPIRES ($EXPIRES_HUMAN)"
|
||
echo "本地請求檔:$PENDING_FILE"
|