summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/check-token78
-rw-r--r--scripts/fix-permissions43
-rw-r--r--scripts/sync-credentials40
3 files changed, 161 insertions, 0 deletions
diff --git a/scripts/check-token b/scripts/check-token
new file mode 100644
index 0000000..40a3116
--- /dev/null
+++ b/scripts/check-token
@@ -0,0 +1,78 @@
+#!/usr/bin/env bash
+# check-token: Verify Claude OAuth token is valid against the Anthropic API.
+# Usage: check-token [--refresh] [--retry-task <id-prefix>]
+# --refresh re-authenticate via claude CLI if token is bad
+# --retry-task <id> after a successful token check/refresh, retry that task
+#
+# Exit codes: 0=valid, 1=expired/invalid, 2=credentials file missing
+
+set -euo pipefail
+
+CREDS="/root/.claude/.credentials.json"
+REFRESH=0
+RETRY_TASK=""
+
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --refresh) REFRESH=1; shift ;;
+ --retry-task) RETRY_TASK="$2"; shift 2 ;;
+ *) echo "Unknown arg: $1" >&2; exit 2 ;;
+ esac
+done
+
+if [[ ! -f "$CREDS" ]]; then
+ echo "ERROR: credentials file not found: $CREDS" >&2
+ exit 2
+fi
+
+ACCESS_TOKEN=$(python3 -c "
+import json, sys
+d = json.load(open('$CREDS'))
+tok = d.get('claudeAiOauth', {}).get('accessToken', '')
+if not tok:
+ print('MISSING', file=sys.stderr)
+ sys.exit(1)
+print(tok)
+")
+
+# Test token against the API with a minimal request
+HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
+ -X POST https://api.anthropic.com/v1/messages \
+ -H "anthropic-version: 2023-06-01" \
+ -H "anthropic-beta: oauth-2025-04-20" \
+ -H "Authorization: Bearer $ACCESS_TOKEN" \
+ -H "Content-Type: application/json" \
+ -d '{"model":"claude-haiku-4-5-20251001","max_tokens":1,"messages":[{"role":"user","content":"hi"}]}')
+
+if [[ "$HTTP_STATUS" == "200" ]]; then
+ echo "OK: token is valid (HTTP $HTTP_STATUS)"
+ if [[ -n "$RETRY_TASK" ]]; then
+ /workspace/claudomator/scripts/ct-task "$RETRY_TASK" retry
+ fi
+ exit 0
+elif [[ "$HTTP_STATUS" == "401" ]]; then
+ echo "EXPIRED: token rejected by API (HTTP 401)"
+ if [[ "$REFRESH" == "1" ]]; then
+ echo "Re-authenticating via claude CLI..."
+ claude --dangerously-skip-permissions /dev/null 2>&1 || true
+ # Check if creds were updated
+ NEW_TOKEN=$(python3 -c "import json; print(json.load(open('$CREDS')).get('claudeAiOauth',{}).get('accessToken',''))")
+ if [[ "$NEW_TOKEN" != "$ACCESS_TOKEN" ]]; then
+ echo "New token obtained. Syncing credentials..."
+ /workspace/claudomator/scripts/sync-credentials
+ if [[ -n "$RETRY_TASK" ]]; then
+ /workspace/claudomator/scripts/ct-task "$RETRY_TASK" retry
+ fi
+ exit 0
+ else
+ echo "Token unchanged — manual re-auth required: run 'claude' in a terminal" >&2
+ exit 1
+ fi
+ else
+ echo "Run: check-token --refresh or re-authenticate via 'claude'" >&2
+ exit 1
+ fi
+else
+ echo "WARN: unexpected HTTP $HTTP_STATUS from API (token may still be valid)"
+ exit 1
+fi
diff --git a/scripts/fix-permissions b/scripts/fix-permissions
new file mode 100644
index 0000000..408a23e
--- /dev/null
+++ b/scripts/fix-permissions
@@ -0,0 +1,43 @@
+#!/bin/bash
+# claudomator-fix-perms — Fix ownership and permissions for Claudomator components
+set -euo pipefail
+
+SITE_DIR="/site/doot.terst.org"
+GIT_REPOS_DIR="/site/git.terst.org/repos"
+WORKSPACE_DIR="/workspace"
+
+echo "==> Fixing site ownership (www-data:www-data)..."
+chown -R www-data:www-data "${SITE_DIR}"
+
+echo "==> Ensuring binaries are executable..."
+if [ -d "${SITE_DIR}/bin" ]; then
+ find "${SITE_DIR}/bin" -type f -exec chmod +x {} +
+fi
+if [ -f "/usr/local/bin/claudomator" ]; then
+ chmod +x /usr/local/bin/claudomator
+fi
+
+echo "==> Ensuring scripts are executable..."
+if [ -d "${SITE_DIR}/scripts" ]; then
+ find "${SITE_DIR}/scripts" -type f -exec chmod +x {} +
+fi
+if [ -d "${WORKSPACE_DIR}/claudomator/scripts" ]; then
+ find "${WORKSPACE_DIR}/claudomator/scripts" -type f -exec chmod +x {} +
+fi
+
+echo "==> Fixing git bare repo permissions..."
+# Specifically fix object permissions that might be corrupted by root runs
+if [ -d "${GIT_REPOS_DIR}" ]; then
+ chown -R www-data:www-data "${GIT_REPOS_DIR}"
+ find "${GIT_REPOS_DIR}" -type d -exec chmod 775 {} +
+ find "${GIT_REPOS_DIR}" -type f -exec chmod 664 {} +
+fi
+
+echo "==> Fixing database permissions..."
+if [ -f "${SITE_DIR}/data/claudomator.db" ]; then
+ chmod 664 "${SITE_DIR}/data/claudomator.db"
+ # Ensure the data directory is writable for WAL mode
+ chmod 775 "${SITE_DIR}/data"
+fi
+
+echo "==> Done!"
diff --git a/scripts/sync-credentials b/scripts/sync-credentials
new file mode 100644
index 0000000..78e5311
--- /dev/null
+++ b/scripts/sync-credentials
@@ -0,0 +1,40 @@
+#!/bin/bash
+# sync-credentials — copies Claude and Gemini credentials to workspace
+
+set -euo pipefail
+
+# This script is intended to be run by cron every 10 minutes.
+# It copies Claude and Gemini credentials from root home to workspace for claudomator.
+
+# Source paths
+SOURCE_CLAUDE="/root/.claude/.credentials.json"
+SOURCE_GEMINI_OAUTH="/root/.gemini/oauth_creds.json"
+SOURCE_GEMINI_ACCOUNTS="/root/.gemini/google_accounts.json"
+
+# Destination paths
+DEST_CLAUDE="/workspace/claudomator/credentials/claude/.credentials.json"
+DEST_GEMINI_OAUTH="/workspace/claudomator/credentials/gemini/oauth_creds.json"
+DEST_GEMINI_ACCOUNTS="/workspace/claudomator/credentials/gemini/google_accounts.json"
+
+# Sync Claude
+if [[ -f "$SOURCE_CLAUDE" ]]; then
+ mkdir -p "$(dirname "$DEST_CLAUDE")"
+ cp "$SOURCE_CLAUDE" "$DEST_CLAUDE"
+ chmod 600 "$DEST_CLAUDE"
+ echo "Synced Claude credentials."
+fi
+
+# Sync Gemini
+if [[ -f "$SOURCE_GEMINI_OAUTH" ]]; then
+ mkdir -p "$(dirname "$DEST_GEMINI_OAUTH")"
+ cp "$SOURCE_GEMINI_OAUTH" "$DEST_GEMINI_OAUTH"
+ chmod 600 "$DEST_GEMINI_OAUTH"
+ echo "Synced Gemini OAuth credentials."
+fi
+
+if [[ -f "$SOURCE_GEMINI_ACCOUNTS" ]]; then
+ mkdir -p "$(dirname "$DEST_GEMINI_ACCOUNTS")"
+ cp "$SOURCE_GEMINI_ACCOUNTS" "$DEST_GEMINI_ACCOUNTS"
+ chmod 600 "$DEST_GEMINI_ACCOUNTS"
+ echo "Synced Gemini Google accounts."
+fi