chore(hq): daily sync 2026-02-20
This commit is contained in:
@@ -1,79 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# sync-codex-tokens.sh — Read fresh Codex CLI tokens and propagate to all OpenClaw instances
|
||||
# Run after: codex login
|
||||
# Can also be called from a cron job or post-login hook
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CODEX_AUTH="$HOME/.codex/auth.json"
|
||||
OPENCLAW_AGENTS_DIR="$HOME/.openclaw/agents"
|
||||
ATOMIZER_AGENTS_DIR="$HOME/.openclaw-atomizer/agents" # fallback if shared state
|
||||
|
||||
if [ ! -f "$CODEX_AUTH" ]; then
|
||||
echo "ERROR: No codex auth.json found at $CODEX_AUTH"
|
||||
echo "Run: codex login"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check token freshness (< 1 hour old = fresh)
|
||||
AGE=$(( $(date +%s) - $(stat -c %Y "$CODEX_AUTH") ))
|
||||
if [ "$AGE" -gt 3600 ]; then
|
||||
echo "WARNING: Codex auth.json is ${AGE}s old. Consider running 'codex login' first."
|
||||
fi
|
||||
|
||||
# Python does the heavy lifting
|
||||
python3 << 'PYEOF'
|
||||
import json, os, time, sys, glob
|
||||
|
||||
codex_path = os.path.expanduser("~/.codex/auth.json")
|
||||
with open(codex_path) as f:
|
||||
codex = json.load(f)
|
||||
t = codex["tokens"]
|
||||
|
||||
new_profile = {
|
||||
"type": "oauth",
|
||||
"provider": "openai-codex",
|
||||
"access": t["access_token"],
|
||||
"refresh": t["refresh_token"],
|
||||
"expires": int(time.time() * 1000) + 10 * 24 * 3600 * 1000,
|
||||
"accountId": t.get("account_id", "")
|
||||
}
|
||||
|
||||
# Find all auth-profiles.json files
|
||||
patterns = [
|
||||
os.path.expanduser("~/.openclaw/agents/*/agent/auth-profiles.json"),
|
||||
os.path.expanduser("~/.openclaw-atomizer/agents/*/agent/auth-profiles.json"),
|
||||
]
|
||||
|
||||
updated = 0
|
||||
for pattern in patterns:
|
||||
for path in glob.glob(pattern):
|
||||
try:
|
||||
with open(path) as f:
|
||||
data = json.load(f)
|
||||
changed = False
|
||||
for key in list(data.get("profiles", {}).keys()):
|
||||
if key.startswith("openai-codex:"):
|
||||
data["profiles"][key] = new_profile.copy()
|
||||
changed = True
|
||||
if changed:
|
||||
with open(path, "w") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
agent = path.split("/agents/")[1].split("/")[0]
|
||||
print(f" ✓ {agent}")
|
||||
updated += 1
|
||||
except Exception as e:
|
||||
print(f" ✗ {path}: {e}", file=sys.stderr)
|
||||
|
||||
print(f"\nUpdated {updated} agent profiles.")
|
||||
PYEOF
|
||||
|
||||
# Restart Atomizer cluster if it exists
|
||||
CLUSTER="$HOME/atomizer/cluster.sh"
|
||||
if [ -f "$CLUSTER" ] && [ "${1:-}" = "--restart" ]; then
|
||||
echo ""
|
||||
echo "Restarting Atomizer cluster..."
|
||||
bash "$CLUSTER" restart
|
||||
fi
|
||||
|
||||
echo "Done."
|
||||
@@ -1,178 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# sync-credentials.sh — Single source of truth for all OpenClaw credentials
|
||||
# Reads from canonical sources → pushes to all agent auth-profiles.json
|
||||
#
|
||||
# Usage:
|
||||
# sync-credentials.sh # Sync all credentials
|
||||
# sync-credentials.sh --restart # Sync + restart Atomizer cluster
|
||||
# sync-credentials.sh --check # Just check expiry/health, no changes
|
||||
# sync-credentials.sh --codex-login # Run codex login first, then sync
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||
|
||||
MODE="${1:-sync}"
|
||||
|
||||
if [ "$MODE" = "--codex-login" ]; then
|
||||
echo "Starting codex login..."
|
||||
echo "⚠️ Make sure you have SSH tunnel: ssh -L 1455:localhost:1455 clawdbot"
|
||||
codex login
|
||||
MODE="--restart" # After login, sync and restart
|
||||
fi
|
||||
|
||||
export SYNC_MODE="$MODE"
|
||||
python3 << 'PYEOF'
|
||||
import json, os, time, sys, glob, shutil
|
||||
from pathlib import Path
|
||||
|
||||
mode = os.environ.get("SYNC_MODE", "sync")
|
||||
home = os.path.expanduser("~")
|
||||
now_ms = int(time.time() * 1000)
|
||||
now_s = time.time()
|
||||
warnings = []
|
||||
updates = []
|
||||
|
||||
# ─── Canonical credential sources ───
|
||||
|
||||
# 1. Anthropic token (from Mario's main profile — the "source of truth")
|
||||
mario_auth = f"{home}/.openclaw/agents/main/agent/auth-profiles.json"
|
||||
with open(mario_auth) as f:
|
||||
mario_profiles = json.load(f)["profiles"]
|
||||
|
||||
anthropic_profile = mario_profiles.get("anthropic:default")
|
||||
google_profile = mario_profiles.get("google:default")
|
||||
|
||||
# 2. OpenAI Codex (from Codex CLI — always freshest)
|
||||
codex_auth_path = f"{home}/.codex/auth.json"
|
||||
codex_profile = None
|
||||
if os.path.isfile(codex_auth_path):
|
||||
with open(codex_auth_path) as f:
|
||||
codex = json.load(f)
|
||||
t = codex["tokens"]
|
||||
|
||||
# Estimate expiry from access token (JWT)
|
||||
import base64
|
||||
try:
|
||||
payload = t["access_token"].split(".")[1]
|
||||
payload += "=" * (-len(payload) % 4)
|
||||
jwt = json.loads(base64.urlsafe_b64decode(payload))
|
||||
expires_ms = jwt["exp"] * 1000
|
||||
except:
|
||||
expires_ms = now_ms + 10 * 24 * 3600 * 1000 # fallback: 10 days
|
||||
|
||||
codex_profile = {
|
||||
"type": "oauth",
|
||||
"provider": "openai-codex",
|
||||
"access": t["access_token"],
|
||||
"refresh": t["refresh_token"],
|
||||
"expires": expires_ms,
|
||||
"accountId": t.get("account_id", "")
|
||||
}
|
||||
|
||||
days_left = (expires_ms - now_ms) / (24 * 3600 * 1000)
|
||||
if days_left < 2:
|
||||
warnings.append(f"⚠️ OpenAI Codex token expires in {days_left:.1f} days! Run: codex login")
|
||||
elif days_left < 5:
|
||||
warnings.append(f"⚡ OpenAI Codex token expires in {days_left:.1f} days")
|
||||
else:
|
||||
print(f" ✓ OpenAI Codex token valid for {days_left:.1f} days")
|
||||
else:
|
||||
warnings.append("⚠️ No Codex CLI auth found! Run: codex login")
|
||||
|
||||
# ─── Check mode: just report ───
|
||||
if mode == "--check":
|
||||
# Check Anthropic
|
||||
if anthropic_profile:
|
||||
print(f" ✓ Anthropic token: present (token type, no expiry)")
|
||||
# Check Google
|
||||
if google_profile:
|
||||
print(f" ✓ Google AI token: present")
|
||||
# Check Discord tokens
|
||||
discord_env = f"{home}/atomizer/config/.discord-tokens.env"
|
||||
if os.path.isfile(discord_env):
|
||||
with open(discord_env) as f:
|
||||
count = sum(1 for l in f if l.startswith("DISCORD_TOKEN_"))
|
||||
print(f" ✓ Discord bot tokens: {count} configured")
|
||||
for w in warnings:
|
||||
print(f" {w}")
|
||||
sys.exit(0)
|
||||
|
||||
# ─── Sync mode: push to all instances ───
|
||||
print("\nSyncing credentials to all instances...")
|
||||
|
||||
# Find all auth-profiles.json
|
||||
patterns = [
|
||||
f"{home}/.openclaw/agents/*/agent/auth-profiles.json",
|
||||
f"{home}/.openclaw-atomizer/agents/*/agent/auth-profiles.json",
|
||||
]
|
||||
|
||||
for pattern in patterns:
|
||||
for path in glob.glob(pattern):
|
||||
try:
|
||||
with open(path) as f:
|
||||
data = json.load(f)
|
||||
|
||||
changed = False
|
||||
profiles = data.setdefault("profiles", {})
|
||||
|
||||
# Sync Anthropic
|
||||
if anthropic_profile and "anthropic:default" in profiles:
|
||||
if profiles["anthropic:default"].get("token") != anthropic_profile.get("token"):
|
||||
profiles["anthropic:default"] = anthropic_profile.copy()
|
||||
changed = True
|
||||
|
||||
# Sync OpenAI Codex
|
||||
if codex_profile:
|
||||
for key in list(profiles.keys()):
|
||||
if key.startswith("openai-codex:"):
|
||||
if profiles[key].get("refresh") != codex_profile["refresh"]:
|
||||
profiles[key] = codex_profile.copy()
|
||||
changed = True
|
||||
|
||||
# Sync Google (only for Mario)
|
||||
if "/.openclaw/agents/" in path and google_profile:
|
||||
if "google:default" in profiles:
|
||||
profiles["google:default"] = google_profile.copy()
|
||||
|
||||
if changed:
|
||||
# Backup before writing
|
||||
backup = path + ".bak"
|
||||
shutil.copy2(path, backup)
|
||||
with open(path, "w") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
agent = path.split("/agents/")[1].split("/")[0]
|
||||
instance = "mario" if "/.openclaw/agents/" in path else "atomizer"
|
||||
updates.append(f"{instance}/{agent}")
|
||||
except Exception as e:
|
||||
warnings.append(f"✗ {path}: {e}")
|
||||
|
||||
if updates:
|
||||
print(f"\n Updated {len(updates)} profiles:")
|
||||
for u in updates:
|
||||
print(f" ✓ {u}")
|
||||
else:
|
||||
print("\n All profiles already in sync ✓")
|
||||
|
||||
for w in warnings:
|
||||
print(f"\n {w}")
|
||||
|
||||
PYEOF
|
||||
|
||||
# Restart if requested
|
||||
if [ "$MODE" = "--restart" ]; then
|
||||
echo ""
|
||||
CLUSTER="$HOME/atomizer/cluster.sh"
|
||||
if [ -f "$CLUSTER" ]; then
|
||||
echo "Restarting Atomizer cluster..."
|
||||
bash "$CLUSTER" restart
|
||||
fi
|
||||
|
||||
echo "Restarting Mario gateway..."
|
||||
systemctl --user restart openclaw-gateway.service
|
||||
|
||||
echo "All instances restarted."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Done."
|
||||
Reference in New Issue
Block a user