Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions .agents/scripts/auto-update-helper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ readonly STATE_FILE="$HOME/.aidevops/cache/auto-update-state.json"
readonly CRON_MARKER="# aidevops-auto-update"
readonly DEFAULT_INTERVAL=10
readonly DEFAULT_SKILL_FRESHNESS_HOURS=24
readonly LAUNCHD_LABEL="com.aidevops.auto-update"
readonly LAUNCHD_LABEL="com.aidevops.aidevops-auto-update"
readonly LAUNCHD_DIR="$HOME/Library/LaunchAgents"
readonly LAUNCHD_PLIST="${LAUNCHD_DIR}/${LAUNCHD_LABEL}.plist"

Expand Down Expand Up @@ -573,6 +573,15 @@ cmd_enable() {
if [[ "$backend" == "launchd" ]]; then
local interval_seconds=$((interval * 60))

# Migrate from old label if present (t1260)
local old_label="com.aidevops.auto-update"
local old_plist="${LAUNCHD_DIR}/${old_label}.plist"
if launchctl list 2>/dev/null | grep -qF "$old_label"; then
launchctl unload -w "$old_plist" 2>/dev/null || true
log_info "Unloaded old LaunchAgent: $old_label"
fi
rm -f "$old_plist"

# Auto-migrate existing cron entry if present
_migrate_cron_to_launchd "$script_path" "$interval_seconds"

Expand All @@ -584,7 +593,15 @@ cmd_enable() {
fi

mkdir -p "$LAUNCHD_DIR"
_generate_auto_update_plist "$script_path" "$interval_seconds" "${PATH}" >"$LAUNCHD_PLIST"

# Create named symlink so macOS System Settings shows "aidevops-auto-update"
# instead of the raw script name (t1260)
local bin_dir="$HOME/.aidevops/bin"
mkdir -p "$bin_dir"
local display_link="$bin_dir/aidevops-auto-update"
ln -sf "$script_path" "$display_link"

_generate_auto_update_plist "$display_link" "$interval_seconds" "${PATH}" >"$LAUNCHD_PLIST"

if launchctl load -w "$LAUNCHD_PLIST" 2>/dev/null; then
update_state "enable" "$(get_local_version)" "enabled"
Expand Down Expand Up @@ -861,7 +878,7 @@ ENVIRONMENT:
AIDEVOPS_SKILL_FRESHNESS_HOURS=24 Hours between skill checks (default: 24)

SCHEDULER BACKENDS:
macOS: launchd LaunchAgent (~/Library/LaunchAgents/com.aidevops.auto-update.plist)
macOS: launchd LaunchAgent (~/Library/LaunchAgents/com.aidevops.aidevops-auto-update.plist)
- Native macOS scheduler, survives reboots without cron
- Auto-migrates existing cron entries on first 'enable'
Linux: cron (crontab entry with # aidevops-auto-update marker)
Expand Down
14 changes: 4 additions & 10 deletions .agents/scripts/supervisor/cron.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Platform-aware: uses launchd on macOS, cron on Linux.
# Same CLI interface (cron install/uninstall/status) regardless of backend.
#
# On macOS: generates ~/Library/LaunchAgents/com.aidevops.supervisor-pulse.plist
# On macOS: generates ~/Library/LaunchAgents/com.aidevops.aidevops-supervisor-pulse.plist
# On Linux: installs a crontab entry (unchanged behaviour)

# Source launchd helpers (macOS backend)
Expand Down Expand Up @@ -103,7 +103,7 @@ _cmd_cron_launchd() {
"$batch_arg"

# Install (migrate handles the case where cron existed; install handles fresh)
if ! _launchd_is_loaded "com.aidevops.supervisor-pulse"; then
if ! _launchd_is_loaded "com.aidevops.aidevops-supervisor-pulse"; then
launchd_install_supervisor_pulse \
"$script_path" \
"$interval_seconds" \
Expand Down Expand Up @@ -167,20 +167,14 @@ _cmd_cron_linux() {
# Detect current PATH for cron environment (t1006)
local user_path="${PATH}"

# Detect GH_TOKEN from gh CLI if available (t1006)
local gh_token=""
if command -v gh &>/dev/null; then
gh_token=$(gh auth token 2>/dev/null || true)
fi
# GH_TOKEN is resolved at runtime by pulse.sh (t1260)
# Previously baked into crontab/plist as plaintext — no longer needed here

# Build cron command with environment variables
local env_vars=""
if [[ -n "$user_path" ]]; then
env_vars="PATH=${user_path}"
fi
if [[ -n "$gh_token" ]]; then
env_vars="${env_vars:+${env_vars} }GH_TOKEN=${gh_token}"
fi

local cron_cmd="*/${interval} * * * * ${env_vars:+${env_vars} }${script_path} pulse ${batch_arg} >> ${log_path} 2>&1 ${cron_marker}"

Expand Down
107 changes: 79 additions & 28 deletions .agents/scripts/supervisor/launchd.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
# Called by cron.sh when running on macOS.
#
# Three LaunchAgent plists managed:
# com.aidevops.supervisor-pulse - StartInterval:120 (every 2 min)
# com.aidevops.auto-update - StartInterval:600 (every 10 min)
# com.aidevops.todo-watcher - WatchPaths (replaces fswatch)
# com.aidevops.aidevops-supervisor-pulse - StartInterval:120 (every 2 min)
# com.aidevops.aidevops-auto-update - StartInterval:600 (every 10 min)
# com.aidevops.aidevops-todo-watcher - WatchPaths (replaces fswatch)
#
# Labels are prefixed with "aidevops-" so they appear grouped in
# macOS System Settings > Login Items & Extensions.
#
# Migration: auto-migrates existing cron entries to launchd on macOS.

Expand Down Expand Up @@ -35,7 +38,7 @@ _launchd_dir() {
#######################################
# Plist path for a given label
# Arguments:
# $1 - label (e.g., com.aidevops.supervisor-pulse)
# $1 - label (e.g., com.aidevops.aidevops-supervisor-pulse)
#######################################
_plist_path() {
local label="$1"
Expand Down Expand Up @@ -86,7 +89,7 @@ _launchd_unload() {
# $3 - log_path
# $4 - batch_arg (optional, e.g., "--batch my-batch")
# $5 - env_path (PATH value for launchd environment)
# $6 - gh_token (optional GH_TOKEN value)
# $6 - gh_token (deprecated — token now resolved at runtime by pulse.sh)
#######################################
_generate_supervisor_pulse_plist() {
local script_path="$1"
Expand All @@ -96,7 +99,7 @@ _generate_supervisor_pulse_plist() {
local env_path="${5:-/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin}"
local gh_token="${6:-}"

local label="com.aidevops.supervisor-pulse"
local label="com.aidevops.aidevops-supervisor-pulse"

# Build ProgramArguments array
local prog_args
Expand Down Expand Up @@ -167,7 +170,7 @@ _generate_auto_update_plist() {
local log_path="$3"
local env_path="${4:-/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin}"

local label="com.aidevops.auto-update"
local label="com.aidevops.aidevops-auto-update"

cat <<EOF
<?xml version="1.0" encoding="UTF-8"?>
Expand Down Expand Up @@ -220,7 +223,7 @@ _generate_todo_watcher_plist() {
local log_path="$4"
local env_path="${5:-/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin}"

local label="com.aidevops.todo-watcher"
local label="com.aidevops.aidevops-todo-watcher"

cat <<EOF
<?xml version="1.0" encoding="UTF-8"?>
Expand Down Expand Up @@ -273,20 +276,34 @@ launchd_install_supervisor_pulse() {
local log_path="$3"
local batch_arg="${4:-}"

local label="com.aidevops.supervisor-pulse"
local label="com.aidevops.aidevops-supervisor-pulse"
local plist_path
plist_path="$(_plist_path "$label")"
local launchd_dir
launchd_dir="$(_launchd_dir)"

mkdir -p "$launchd_dir"

# Detect PATH and GH_TOKEN for launchd environment
local env_path="${PATH}"
local gh_token=""
if command -v gh &>/dev/null; then
gh_token=$(gh auth token 2>/dev/null || true)
# Migrate from old label if present (t1260)
local old_label="com.aidevops.supervisor-pulse"
local old_plist
old_plist="$(_plist_path "$old_label")"
if _launchd_is_loaded "$old_label"; then
_launchd_unload "$old_plist" || true
log_info "Unloaded old LaunchAgent: $old_label"
fi
rm -f "$old_plist"

# Detect PATH for launchd environment
# GH_TOKEN is resolved at runtime by the pulse script (not baked into plist)
local env_path="${PATH}"

# Create named symlink so macOS System Settings shows "aidevops-supervisor-pulse"
# instead of the raw script name (t1260)
local bin_dir="$HOME/.aidevops/bin"
mkdir -p "$bin_dir"
local display_link="$bin_dir/aidevops-supervisor-pulse"
ln -sf "$script_path" "$display_link"

# Check if already loaded
if _launchd_is_loaded "$label"; then
Expand All @@ -295,14 +312,14 @@ launchd_install_supervisor_pulse() {
return 0
fi

# Generate and write plist
# Generate and write plist using symlink for display name (no GH_TOKEN — resolved at runtime)
_generate_supervisor_pulse_plist \
"$script_path" \
"$display_link" \
"$interval_seconds" \
"$log_path" \
"$batch_arg" \
"$env_path" \
"$gh_token" >"$plist_path"
"" >"$plist_path"

# Load into launchd
if _launchd_load "$plist_path"; then
Expand All @@ -321,7 +338,7 @@ launchd_install_supervisor_pulse() {
# Uninstall supervisor-pulse LaunchAgent on macOS
#######################################
launchd_uninstall_supervisor_pulse() {
local label="com.aidevops.supervisor-pulse"
local label="com.aidevops.aidevops-supervisor-pulse"
local plist_path
plist_path="$(_plist_path "$label")"

Expand All @@ -343,7 +360,7 @@ launchd_uninstall_supervisor_pulse() {
# Show status of supervisor-pulse LaunchAgent
#######################################
launchd_status_supervisor_pulse() {
local label="com.aidevops.supervisor-pulse"
local label="com.aidevops.aidevops-supervisor-pulse"
local plist_path
plist_path="$(_plist_path "$label")"

Expand Down Expand Up @@ -392,25 +409,42 @@ launchd_install_auto_update() {
local interval_seconds="${2:-600}"
local log_path="$3"

local label="com.aidevops.auto-update"
local label="com.aidevops.aidevops-auto-update"
local plist_path
plist_path="$(_plist_path "$label")"
local launchd_dir
launchd_dir="$(_launchd_dir)"

mkdir -p "$launchd_dir"

# Migrate from old label if present (t1260)
local old_label="com.aidevops.auto-update"
local old_plist
old_plist="$(_plist_path "$old_label")"
if _launchd_is_loaded "$old_label"; then
_launchd_unload "$old_plist" || true
log_info "Unloaded old LaunchAgent: $old_label"
fi
rm -f "$old_plist"

local env_path="${PATH}"

# Create named symlink so macOS System Settings shows "aidevops-auto-update"
# instead of the raw script name (t1260)
local bin_dir="$HOME/.aidevops/bin"
mkdir -p "$bin_dir"
local display_link="$bin_dir/aidevops-auto-update"
ln -sf "$script_path" "$display_link"

# Check if already loaded
if _launchd_is_loaded "$label"; then
log_warn "LaunchAgent $label already loaded. Unload first to change settings."
return 0
fi

# Generate and write plist
# Generate and write plist using symlink for display name
_generate_auto_update_plist \
"$script_path" \
"$display_link" \
"$interval_seconds" \
"$log_path" \
"$env_path" >"$plist_path"
Expand All @@ -432,7 +466,7 @@ launchd_install_auto_update() {
# Uninstall auto-update LaunchAgent on macOS
#######################################
launchd_uninstall_auto_update() {
local label="com.aidevops.auto-update"
local label="com.aidevops.aidevops-auto-update"
local plist_path
plist_path="$(_plist_path "$label")"

Expand All @@ -454,7 +488,7 @@ launchd_uninstall_auto_update() {
# Show status of auto-update LaunchAgent
#######################################
launchd_status_auto_update() {
local label="com.aidevops.auto-update"
local label="com.aidevops.aidevops-auto-update"
local plist_path
plist_path="$(_plist_path "$label")"

Expand Down Expand Up @@ -499,25 +533,42 @@ launchd_install_todo_watcher() {
local repo_path="$3"
local log_path="$4"

local label="com.aidevops.todo-watcher"
local label="com.aidevops.aidevops-todo-watcher"
local plist_path
plist_path="$(_plist_path "$label")"
local launchd_dir
launchd_dir="$(_launchd_dir)"

mkdir -p "$launchd_dir"

# Migrate from old label if present (t1260)
local old_label="com.aidevops.todo-watcher"
local old_plist
old_plist="$(_plist_path "$old_label")"
if _launchd_is_loaded "$old_label"; then
_launchd_unload "$old_plist" || true
log_info "Unloaded old LaunchAgent: $old_label"
fi
rm -f "$old_plist"

local env_path="${PATH}"

# Create named symlink so macOS System Settings shows "aidevops-todo-watcher"
# instead of the raw script name (t1260)
local bin_dir="$HOME/.aidevops/bin"
mkdir -p "$bin_dir"
local display_link="$bin_dir/aidevops-todo-watcher"
ln -sf "$script_path" "$display_link"

# Check if already loaded
if _launchd_is_loaded "$label"; then
log_warn "LaunchAgent $label already loaded."
return 0
fi

# Generate and write plist
# Generate and write plist using symlink for display name
_generate_todo_watcher_plist \
"$script_path" \
"$display_link" \
"$todo_path" \
"$repo_path" \
"$log_path" \
Expand All @@ -540,7 +591,7 @@ launchd_install_todo_watcher() {
# Uninstall todo-watcher LaunchAgent on macOS
#######################################
launchd_uninstall_todo_watcher() {
local label="com.aidevops.todo-watcher"
local label="com.aidevops.aidevops-todo-watcher"
local plist_path
plist_path="$(_plist_path "$label")"

Expand Down
18 changes: 18 additions & 0 deletions .agents/scripts/supervisor/pulse.sh
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,24 @@ cmd_pulse() {

ensure_db

# Resolve GH_TOKEN at runtime if not already set (t1260)
# Previously baked into the launchd plist as plaintext — now resolved
# from gh CLI keychain, gopass, or credential-helper at each pulse.
if [[ -z "${GH_TOKEN:-}" ]]; then
local _resolved_token=""
# Try gh CLI (reads from macOS Keychain when user is logged in)
if command -v gh &>/dev/null; then
_resolved_token=$(gh auth token 2>/dev/null || true)
fi
# Fallback: gopass encrypted store
if [[ -z "$_resolved_token" ]] && command -v gopass &>/dev/null; then
_resolved_token=$(gopass show -o "aidevops/GH_TOKEN" 2>/dev/null || true)
fi
if [[ -n "$_resolved_token" ]]; then
export GH_TOKEN="$_resolved_token"
fi
fi

# Acquire pulse dispatch lock to prevent concurrent pulses from
# independently dispatching workers and exceeding concurrency limits (t159)
if ! acquire_pulse_lock; then
Expand Down
Loading