Spaces:
Running
Running
Z User commited on
Commit ·
a9f57ed
1
Parent(s): 31a92da
feat: persist WeChat credentials via HF Space Secrets + account JSON
Browse filesThree-layer persistence ensures WeChat NEVER needs re-scanning:
1. HF Space Secrets (WEIXIN_TOKEN, WEIXIN_ACCOUNT_ID) — survives everything
2. .env file — gateway's load_env picks these up
3. /data/hermes/weixin/accounts/<id>.json — gateway's load_weixin_account()
On every startup, start.sh:
- Reads WEIXIN_TOKEN/ACCOUNT_ID from HF Secrets (env vars)
- Writes them to both .env and account JSON
- Falls back to reading from persisted account JSON if secrets missing
- Logs masked values (never full tokens in build logs)
start.sh
CHANGED
|
@@ -33,42 +33,48 @@ if [ ! -L "$WEIXIN_DIR" ]; then
|
|
| 33 |
echo "Symlink: weixin -> /data/hermes/weixin"
|
| 34 |
fi
|
| 35 |
|
| 36 |
-
#
|
| 37 |
-
#
|
| 38 |
-
#
|
| 39 |
ACCOUNTS_DIR="/data/hermes/weixin/accounts"
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
fi
|
| 49 |
fi
|
| 50 |
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
ACCOUNT_FILE="$ACCOUNTS_DIR/${WEIXIN_ACCOUNT_ID}.json"
|
| 55 |
-
if [ -f "$ACCOUNT_FILE" ]; then
|
| 56 |
-
|
| 57 |
-
import json,
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
" 2>/dev/null)
|
| 63 |
-
if [ -n "$DISCOVERED_TOKEN" ]; then
|
| 64 |
-
export WEIXIN_TOKEN="$DISCOVERED_TOKEN"
|
| 65 |
-
echo "Restored WEIXIN_TOKEN from persisted account file"
|
| 66 |
-
else
|
| 67 |
-
echo "WARNING: WeChat account file found but token is empty — QR re-scan needed"
|
| 68 |
-
fi
|
| 69 |
-
else
|
| 70 |
-
echo "WARNING: WeChat account file $ACCOUNT_FILE not found — QR scan needed"
|
| 71 |
fi
|
|
|
|
|
|
|
| 72 |
fi
|
| 73 |
|
| 74 |
# -- Persist .env across container rebuilds --
|
|
@@ -98,6 +104,8 @@ if [ ! -f "$ENV_DATA" ] && [ -n "$OPENROUTER_API_KEY" ]; then
|
|
| 98 |
echo "HERMES_ACCEPT_HOOKS=1"
|
| 99 |
[ -n "$MEMPALACE_PALACE_PATH" ] && echo "MEMPALACE_PALACE_PATH=$MEMPALACE_PALACE_PATH"
|
| 100 |
[ -n "$FIRECRAWL_API_KEY" ] && echo "FIRECRAWL_API_KEY=$FIRECRAWL_API_KEY"
|
|
|
|
|
|
|
| 101 |
} > "$ENV_DATA"
|
| 102 |
chmod 600 "$ENV_DATA"
|
| 103 |
echo "Created .env from Space Secrets (keys masked below)"
|
|
@@ -106,6 +114,7 @@ if [ ! -f "$ENV_DATA" ] && [ -n "$OPENROUTER_API_KEY" ]; then
|
|
| 106 |
[ -n "$FEISHU_APP_ID" ] && echo " FEISHU_APP_ID=$FEISHU_APP_ID"
|
| 107 |
[ -n "$FEISHU_APP_SECRET" ] && echo " FEISHU_APP_SECRET=$(_mask_val "$FEISHU_APP_SECRET")"
|
| 108 |
[ -n "$FIRECRAWL_API_KEY" ] && echo " FIRECRAWL_API_KEY=$(_mask_val "$FIRECRAWL_API_KEY")"
|
|
|
|
| 109 |
fi
|
| 110 |
|
| 111 |
# Fallback: if no secrets and no persistent data
|
|
@@ -123,6 +132,14 @@ else
|
|
| 123 |
echo "Symlink exists: .env"
|
| 124 |
fi
|
| 125 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
# ── Persist config.yaml across container rebuilds ──
|
| 127 |
# WebUI settings page and WeChat save flow update ~/.hermes/config.yaml at runtime
|
| 128 |
CFG_FILE="$HERMES_HOME/config.yaml"
|
|
|
|
| 33 |
echo "Symlink: weixin -> /data/hermes/weixin"
|
| 34 |
fi
|
| 35 |
|
| 36 |
+
# ── WeChat credential persistence ──
|
| 37 |
+
# Priority: HF Space Secrets > persisted account JSON file > .env file
|
| 38 |
+
# Once set via HF Space Secrets, WeChat survives ALL container rebuilds.
|
| 39 |
ACCOUNTS_DIR="/data/hermes/weixin/accounts"
|
| 40 |
+
mkdir -p "$ACCOUNTS_DIR"
|
| 41 |
+
|
| 42 |
+
if [ -z "$WEIXIN_ACCOUNT_ID" ] || [ -z "$WEIXIN_TOKEN" ]; then
|
| 43 |
+
# Fallback: restore from persisted account JSON file
|
| 44 |
+
if [ -z "$WEIXIN_ACCOUNT_ID" ] && [ -d "$ACCOUNTS_DIR" ]; then
|
| 45 |
+
LATEST=$(find "$ACCOUNTS_DIR" -name "*.json" ! -name "*.context-tokens.json" ! -name "*.sync.json" -type f -printf '%T@ %p\n' 2>/dev/null | sort -rn | head -1 | awk '{print $2}')
|
| 46 |
+
if [ -n "$LATEST" ]; then
|
| 47 |
+
DISCOVERED_ID=$(basename "$LATEST" .json)
|
| 48 |
+
export WEIXIN_ACCOUNT_ID="$DISCOVERED_ID"
|
| 49 |
+
echo "Auto-discovered WEIXIN_ACCOUNT_ID=$DISCOVERED_ID"
|
| 50 |
+
fi
|
| 51 |
+
fi
|
| 52 |
+
if [ -z "$WEIXIN_TOKEN" ] && [ -n "$WEIXIN_ACCOUNT_ID" ]; then
|
| 53 |
+
ACCOUNT_FILE="$ACCOUNTS_DIR/${WEIXIN_ACCOUNT_ID}.json"
|
| 54 |
+
if [ -f "$ACCOUNT_FILE" ]; then
|
| 55 |
+
DISCOVERED_TOKEN=$(python3 -c "import json; d=json.load(open('$ACCOUNT_FILE')); print(d.get('token',''))" 2>/dev/null)
|
| 56 |
+
if [ -n "$DISCOVERED_TOKEN" ]; then
|
| 57 |
+
export WEIXIN_TOKEN="$DISCOVERED_TOKEN"
|
| 58 |
+
echo "Restored WEIXIN_TOKEN from persisted account file"
|
| 59 |
+
fi
|
| 60 |
+
fi
|
| 61 |
fi
|
| 62 |
fi
|
| 63 |
|
| 64 |
+
if [ -n "$WEIXIN_ACCOUNT_ID" ] && [ -n "$WEIXIN_TOKEN" ]; then
|
| 65 |
+
echo "WeChat credentials ready (account=$(_mask_val "$WEIXIN_ACCOUNT_ID"))"
|
| 66 |
+
# Persist credentials to account JSON so gateway's load_weixin_account() also finds them
|
| 67 |
ACCOUNT_FILE="$ACCOUNTS_DIR/${WEIXIN_ACCOUNT_ID}.json"
|
| 68 |
+
if [ ! -f "$ACCOUNT_FILE" ] || ! python3 -c "import json; d=json.load(open('$ACCOUNT_FILE')); exit(0 if d.get('token') else 1)" 2>/dev/null; then
|
| 69 |
+
python3 -c "
|
| 70 |
+
import json, time
|
| 71 |
+
payload = {'token': '$WEIXIN_TOKEN', 'base_url': 'https://ilinkai.weixin.qq.com', 'saved_at': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())}
|
| 72 |
+
with open('$ACCOUNT_FILE', 'w') as f: json.dump(payload, f)
|
| 73 |
+
" 2>/dev/null && chmod 600 "$ACCOUNT_FILE"
|
| 74 |
+
echo "WeChat credentials persisted to account file"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
fi
|
| 76 |
+
else
|
| 77 |
+
echo "WARNING: WeChat not configured (no token/account). Run 'hermes gateway setup' to scan QR."
|
| 78 |
fi
|
| 79 |
|
| 80 |
# -- Persist .env across container rebuilds --
|
|
|
|
| 104 |
echo "HERMES_ACCEPT_HOOKS=1"
|
| 105 |
[ -n "$MEMPALACE_PALACE_PATH" ] && echo "MEMPALACE_PALACE_PATH=$MEMPALACE_PALACE_PATH"
|
| 106 |
[ -n "$FIRECRAWL_API_KEY" ] && echo "FIRECRAWL_API_KEY=$FIRECRAWL_API_KEY"
|
| 107 |
+
[ -n "$WEIXIN_ACCOUNT_ID" ] && echo "WEIXIN_ACCOUNT_ID=$WEIXIN_ACCOUNT_ID"
|
| 108 |
+
[ -n "$WEIXIN_TOKEN" ] && echo "WEIXIN_TOKEN=$WEIXIN_TOKEN"
|
| 109 |
} > "$ENV_DATA"
|
| 110 |
chmod 600 "$ENV_DATA"
|
| 111 |
echo "Created .env from Space Secrets (keys masked below)"
|
|
|
|
| 114 |
[ -n "$FEISHU_APP_ID" ] && echo " FEISHU_APP_ID=$FEISHU_APP_ID"
|
| 115 |
[ -n "$FEISHU_APP_SECRET" ] && echo " FEISHU_APP_SECRET=$(_mask_val "$FEISHU_APP_SECRET")"
|
| 116 |
[ -n "$FIRECRAWL_API_KEY" ] && echo " FIRECRAWL_API_KEY=$(_mask_val "$FIRECRAWL_API_KEY")"
|
| 117 |
+
[ -n "$WEIXIN_TOKEN" ] && echo " WEIXIN_TOKEN=$(_mask_val "$WEIXIN_TOKEN")"
|
| 118 |
fi
|
| 119 |
|
| 120 |
# Fallback: if no secrets and no persistent data
|
|
|
|
| 132 |
echo "Symlink exists: .env"
|
| 133 |
fi
|
| 134 |
|
| 135 |
+
# Ensure WEIXIN_TOKEN/ACCOUNT_ID are in .env even if file was created earlier without them
|
| 136 |
+
if [ -f "$ENV_DATA" ] && [ -n "$WEIXIN_TOKEN" ] && ! grep -q '^WEIXIN_TOKEN=' "$ENV_DATA" 2>/dev/null; then
|
| 137 |
+
echo "WEIXIN_TOKEN=$WEIXIN_TOKEN" >> "$ENV_DATA"
|
| 138 |
+
fi
|
| 139 |
+
if [ -f "$ENV_DATA" ] && [ -n "$WEIXIN_ACCOUNT_ID" ] && ! grep -q '^WEIXIN_ACCOUNT_ID=' "$ENV_DATA" 2>/dev/null; then
|
| 140 |
+
echo "WEIXIN_ACCOUNT_ID=$WEIXIN_ACCOUNT_ID" >> "$ENV_DATA"
|
| 141 |
+
fi
|
| 142 |
+
|
| 143 |
# ── Persist config.yaml across container rebuilds ──
|
| 144 |
# WebUI settings page and WeChat save flow update ~/.hermes/config.yaml at runtime
|
| 145 |
CFG_FILE="$HERMES_HOME/config.yaml"
|