#!/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"