From 888f3014b42ff48f597d0a81e9f52104d19be6db Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Sat, 21 Mar 2026 21:23:42 +0000 Subject: feat: Phase 2 — project registry, legacy field cleanup, credential path fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - task.Project type + storage CRUD + UpsertProject + SeedProjects - Remove AgentConfig.ProjectDir, RepositoryURL, SkipPlanning - Remove ContainerRunner fallback git init logic - Project API endpoints: GET/POST /api/projects, GET/PUT /api/projects/{id} - processResult no longer extracts changestats (pool-side only) - claude_config_dir config field; default to credentials/claude/ - New scripts: sync-credentials, fix-permissions, check-token Co-Authored-By: Claude Sonnet 4.6 --- scripts/check-token | 78 ++++++++++++++++++++++++++++++++++++++++++++++++ scripts/fix-permissions | 43 ++++++++++++++++++++++++++ scripts/sync-credentials | 40 +++++++++++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 scripts/check-token create mode 100644 scripts/fix-permissions create mode 100644 scripts/sync-credentials (limited to 'scripts') 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 ] +# --refresh re-authenticate via claude CLI if token is bad +# --retry-task 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 -- cgit v1.2.3