#!/usr/bin/env bash # 管什麼: 產生「可以直接貼進 claude.ai Cloud environment」的兩塊內容,值由本腳本自己去 .env 拉。 # 為什麼: leo 2026-08-20「這些值你都有,你可以只寫名字然後 build 一個檔案給我」—— # 之前的做法是叫他自己拼湊,或叫他把設定貼給 AI 看,兩種都錯(一個沒效率,一個讓值經過對話)。 # 誤觸時怎麼關: 這支不擋任何東西。不想產生就別跑;產物在版控外,刪掉即可。 # # 用法:bash scripts/make-cloud-env.sh # 產物:~/Desktop/cloud-env-<版本>-<日期>.txt(權限 600,**不在任何 repo 裡**);版本紀錄 docs/cloud-env-versions.tsv # 本腳本只寫「變數名字」,值在執行當下才從既有 .env 讀出來寫進產物 —— 值不進版控、不進對話。 set -euo pipefail # ── 雲端需要哪些變數(只有名字。要加就加在這裡)────────────────── # # 範圍=**youlin(stage)與 geek6688(測試 prod & 出貨機)兩台全給**, # **外加 uncle6**——出貨的三個站實際部署到的是那個帳號。 # leo 2026-09-01:「youlin 和 geek6688 都全給,因為這兩個都是讓 CC 操作 # 一個 stage 一個 test prod,**不給所有的就是受阻**。」 # uncle6 是同一句話的延伸(inkstone/ISEP#115 → comment `5577`): # `products/arcrun-rag/installer/ship.targets.json` 六處 accountId 去重後只有一個值 # `58309bb90fd93ad6d0fe0aae99170e9d`,而本機出貨靠的是家目錄裡的 OAuth 登入態 # (`~/.wrangler/config/default.toml`)——**那個檔不隨 repo clone 走,雲端永遠拿不到**。 # ⚠️ 這把「是 uncle6 的、而且 active」是驗過的;「它有沒有 Workers Scripts:Edit # =真的部署得動」**沒驗**(驗它必須寫入,那是紅線)。缺這格會在部署當下才知道。 NEEDED=( # ── 機器帳號 ── GITEA_TOKEN_CLAUDE_CODE # claude-code 的 Gitea token;bootstrap 與 plugin 安裝都靠它 # ── youlin = AI 的 stage(沒指定帳號時的預設,agent-memory.md §2)── CLOUDFLARE_API_TOKEN_YOULIN_CC_USE # D37 起這把就是設計給 CC 用的 CLOUDFLARE_ACCOUNT_ID_YOULIN # 上面那把的配對 account id(帳號 id 不是機密) YOULIN_MCP_OWNER_SECRETS # youlin 那台 MCP 的 owner secret STAGE_USERNAME_YOULIN # ⚠️ 密碼類,見下面那條限制 STAGE_PASSWORD_YOULIN # ⚠️ 密碼類,見下面那條限制 # ── geek6688 = 測試 prod & 出貨機 ── CLOUDFLARE_API_TOKEN_CC_SHIPPING_CORE # 出貨線寫入用 CLOUDFLARE_ACCOUNT_ID_GEEK6688 # 上面那把的配對 account id # ── uncle6 = 出貨三站真正部署到的帳號(installer/docsSite/mailRelay,stage 與 prod 都是)── UNCLE6_CF_API_KEY # 住在 products/arcrun-rag/.env;本機是靠 ~/.wrangler 的 OAuth 登入態出貨,那個檔不隨 clone 走 # ── Arcrun 出貨線的實例座標(inkstone/Arcrun#195 起)── # 網址隨 repo 走(Arcrun 的 shared/instance-coordinates/instances.json), # 只有 namespace 是機敏 ⇒ 走環境變數。原本只住在 ~/.arcrun/config.yaml 的 api_key, # 那個檔在家目錄、不隨 clone 走 ⇒ 雲端永遠拿不到。 ARCRUN_NS_YOULIN # youlin 實例的 namespace(X-Arcrun-API-Key) ARCRUN_NS_UNCLE6 # uncle6 實例的 namespace:出貨第 1、19、20 站派給 uncle6 工作流(inkstone/InkStoneCo#98 → comment 7739) # ── prod 出貨把產物推上 GitHub 鏡像(第 14、21、22 站)── # 🔴 名字進清單 ≠ 雲端可以自由寫 GitHub:`github-contact-guard.sh` 照樣擋, # 寫入仍要 D20 開閘(保險檔落在哪由 inkstone/ISEP#157 處理)。 # 進清單是因為「拿不到這兩個名字」會讓 prod 那半在雲端**連開閘之後都走不下去**。 GITHUB_MIRROR_TOKEN # GitHub 鏡像 repo 的寫入 token(走標頭,不進網址) GITHUB_ACCOUNT_NAME # 上面那把的帳號名(組鏡像 repo 路徑用,不是機密) # ── 雲端工人的通知出口(v7,2026-09-18:leo 貼出現行雲端環境才發現 v1–v6 漏收)── # 雲端走 Bot API 直送(`scripts/isep-notify`;leo21c 的 notify_leo 已於 08-14 消失,InkStoneCo#110) TELEGRAM_CHAT_ID # 收訊的 chat id(不是機密) TELEGRAM_BOT_TOKEN # 通知 bot 的 token ) # 🔓 例外:值只住在 polaris/mira/.env、但**不是**打得到 leo21c 的憑證——點名放行,其他照擋(B8) # Telegram 兩把是通知 bot,不碰任何 CF 帳號;雲端 09-18 前就已手動設了同樣的值。 MIRA_ALLOWED=(TELEGRAM_CHAT_ID TELEGRAM_BOT_TOKEN) # ── 固定值(不是金鑰,產生器直接寫)── FIXED=( "ISEP_DRAIN=1" # 雲端工人=接票環境:ticket pick 自帶 --pool、佇列閘不准有票就收工(ISEP#130) ) # # 🔴 這份清單的邊界——加一把之前先問「這把打得到哪個實例」,不是「這把方便嗎」 # # 可以進來的:**youlin 與 geek6688** 這兩台的全部,加上機器帳號自己的 token。 # 🔴 不准進來的: # ① `CLOUDFLARE_API_TOKEN_leo21c`——leo 2026-09-01 只點名那兩台; # leo21c 是現役正式環境,`inkstone/InkStoneCo#14` → comment `3890` 已分進 B 段不放。 # ② `polaris/mira/.env` 裡的任何東西——那整份都是 leo21c 現役正式環境。 # 其中 `CLOUDFLARE_API_TOKEN` / `CLOUDFLARE_ACCOUNT_ID` 這兩個**沒有名字**的最危險: # 所有 CF 工具都把它們當預設 ⇒ 雲端一個沒指定帳號的指令就會打到正式環境。 # 2026-08-14 那輪審查把它列為「本輪最危險的發現」。 # 🔎 這一條不必靠人記得:`scripts/test-make-cloud-env.sh` 的 B8 會去看 # **每一把的值是從哪個檔案拿到的**,出自 `polaris/mira/.env` 就紅。 # # 🔴 密碼類(`STAGE_PASSWORD_YOULIN` 等)進清單是為了讓**人**拿得到。 # `credentials-map.md`「密碼類憑證的用法限制」(2026-08-17 立)照舊有效: # **AI 不得把密碼打進登入欄位**,這是 surface 層硬規則,leo 交代了也不解除。 # 機器要驗登入後的畫面走那節寫的兩條:帶 token/cookie 打 API,或接 leo 已登入的分頁。 # # 📌 兩台的 account id 都有具名變數了(`…_YOULIN` 是 2026-09-01 才補進頂層 `.env` 的, # inkstone/ISEP#115 → comment `5542`;帳號 id 不是機密,wiki 的帳號表本來就公開記著)。 # **所以永遠不需要去用那個沒有名字的 `CLOUDFLARE_ACCOUNT_ID`**——那是 leo21c。 # ── 去哪裡找值(credentials-map.md 記的六個 .env)──────────────── BASE="${INKSTONE_ROOT:-$HOME/Documents/tech_projects/InkStoneCo}" ENV_FILES=( "$BASE/.env" "$BASE/matrix/arcrun/.env" "$BASE/products/arcrun-rag/.env" "$BASE/polaris/mira/.env" "$BASE/matrix/kbdb-graph-plugin/.env" "$BASE/arcrun_harness/.env" ) lookup() { # $1=變數名 → 印出值(找不到就空) local name="$1" f v for f in "${ENV_FILES[@]}"; do [ -f "$f" ] || continue v=$(grep -E "^[[:space:]]*(export[[:space:]]+)?${name}=" "$f" 2>/dev/null | tail -1 \ | sed -E "s/^[[:space:]]*(export[[:space:]]+)?${name}=//" \ | sed -E 's/^"(.*)"$/\1/; s/^'"'"'(.*)'"'"'$/\1/') [ -n "$v" ] && { printf '%s' "$v"; return 0; } done return 1 } # ── 版本(leo 2026-09-18:「你要有版本,因為這件事做了很多次,但你都沒記錄版本」)── # 真相源=docs/cloud-env-versions.tsv(只記 key,不記值)。最後一列=現行版。 # 🔴 NEEDED 跟最後一列的 key 不一致 ⇒ 拒絕產生:改了清單就必須在 tsv 加一版。 VERSIONS="$(cd "$(dirname "$0")/.." && pwd)/docs/cloud-env-versions.tsv" [ -f "$VERSIONS" ] || { echo "🔴 找不到 $VERSIONS" >&2; exit 1; } CUR_LINE=$(grep -v '^#' "$VERSIONS" | grep -v '^[[:space:]]*$' | tail -1) PREV_LINE=$(grep -v '^#' "$VERSIONS" | grep -v '^[[:space:]]*$' | tail -2 | head -1) VER=$(printf '%s' "$CUR_LINE" | cut -f1); VER_DATE=$(printf '%s' "$CUR_LINE" | cut -f2) VER_KEYS=$(printf '%s' "$CUR_LINE" | cut -f3); VER_WHY=$(printf '%s' "$CUR_LINE" | cut -f4) PREV_VER=$(printf '%s' "$PREV_LINE" | cut -f1); PREV_KEYS=$(printf '%s' "$PREV_LINE" | cut -f3) FIXED_NAMES=(); for kv in "${FIXED[@]}"; do FIXED_NAMES+=("${kv%%=*}"); done NEEDED_JOINED=$(IFS=,; printf '%s' "${NEEDED[*]} ${FIXED_NAMES[*]}" | tr ' ' ',') if [ "$NEEDED_JOINED" != "$VER_KEYS" ]; then echo "🔴 NEEDED 與 $VERSIONS 最後一版($VER)的 key 不一致——改了清單就要在 tsv 加一列新版本再跑。" >&2 echo " NEEDED:$NEEDED_JOINED" >&2 echo " $VER :$VER_KEYS" >&2 exit 2 fi ADDED=$(comm -13 <(tr , '\n' <<<"$PREV_KEYS" | sort) <(tr , '\n' <<<"$VER_KEYS" | sort) | paste -sd, -) REMOVED=$(comm -23 <(tr , '\n' <<<"$PREV_KEYS" | sort) <(tr , '\n' <<<"$VER_KEYS" | sort) | paste -sd, -) # 預設丟桌面(leo 2026-09-18 指定)。要放別的地方= OUT_DIR=<目錄> bash scripts/make-cloud-env.sh OUT_DIR="${OUT_DIR:-$HOME/Desktop}" mkdir -p "$OUT_DIR" OUT="$OUT_DIR/cloud-env-${VER}-$(date +%Y%m%d-%H%M).txt" SETUP="$(cd "$(dirname "$0")/.." && pwd)/docs/cloud-setup-script.sh" [ -f "$SETUP" ] || { echo "🔴 找不到 $SETUP" >&2; exit 1; } MISSING=() # 🔴 兩個檔分開產(2026-09-18 實撞):原本兩段放同一檔、中間用「════」分隔, # leo 把分隔線一起貼進 Setup script 欄 ⇒ 雲端開機 exit 127「'════…': command not found」, # 每一趟 cloud-worker 在開機就死。⇒ 變數一個檔(只有 KEY=VALUE)、開機腳本一個檔(逐位元組=cloud-setup-script.sh), # **兩個檔都整份貼**,沒有任何要人自己裁切的地方。 SETUP_OUT="${OUT%.txt}-setup-script.sh" { for n in "${NEEDED[@]}"; do if v=$(lookup "$n"); then echo "$n=$v" else echo "$n=<🔴 這台機器的 .env 裡找不到,要 leo 提供>" MISSING+=("$n") fi done for kv in "${FIXED[@]}"; do echo "$kv"; done } > "$OUT" cp "$SETUP" "$SETUP_OUT" chmod 600 "$OUT" "$SETUP_OUT" { echo "claude.ai → Cloud environments → 你的環境(${VER},${VER_DATE}:${VER_WHY})" echo "比 ${PREV_VER} 多:${ADDED:-(無)}|少:${REMOVED:-(無)}" echo "① Environment variables 欄:整份貼 $(basename "$OUT")" echo "② Setup script 欄:整份貼 $(basename "$SETUP_OUT")(一字不改,不要加任何東西)" echo "貼完刪掉這三個檔。" } > "${OUT%.txt}-README.txt" echo "✅ 產生完成:$OUT" echo " 版本:${VER}(比 ${PREV_VER} 多:${ADDED:-無}|少:${REMOVED:-無})" echo " 變數 ${#NEEDED[@]} 個|找不到值的 ${#MISSING[@]} 個 ${MISSING[*]:-}" echo " 開機腳本:$SETUP_OUT" echo " 🔴 含金鑰真身:貼完就刪(rm '${OUT%.txt}'*),它刻意不在任何 repo 裡。"