Add container-native scheduled task system with timezone support
Introduces a cron-based scheduler that lets Claude set up recurring and one-time tasks inside containers. Tasks run as separate Claude Code agents and persist across container recreation via the named volume. New files: - container/triple-c-scheduler: CLI for add/remove/enable/disable/list/logs/run/notifications - container/triple-c-task-runner: cron wrapper with flock, logging, notifications, auto-cleanup Key changes: - Dockerfile: add cron package and COPY both scripts - entrypoint.sh: timezone setup, cron daemon, crontab restore, env saving - container.rs: init=true for zombie reaping, TZ env, scheduler instructions, timezone recreation check - image.rs: embed scheduler scripts in build context - app_settings.rs + types.ts: timezone field - settings_commands.rs: detect_host_timezone via iana-time-zone crate - SettingsPanel.tsx: timezone input with auto-detection Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -113,6 +113,59 @@ if [ -S /var/run/docker.sock ]; then
|
||||
usermod -aG "$DOCKER_GROUP" claude
|
||||
fi
|
||||
|
||||
# ── Timezone setup ───────────────────────────────────────────────────────────
|
||||
if [ -n "${TZ:-}" ]; then
|
||||
if [ -f "/usr/share/zoneinfo/$TZ" ]; then
|
||||
ln -sf "/usr/share/zoneinfo/$TZ" /etc/localtime
|
||||
echo "$TZ" > /etc/timezone
|
||||
echo "entrypoint: timezone set to $TZ"
|
||||
else
|
||||
echo "entrypoint: warning — timezone '$TZ' not found in /usr/share/zoneinfo"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Scheduler setup ─────────────────────────────────────────────────────────
|
||||
SCHEDULER_DIR="/home/claude/.claude/scheduler"
|
||||
mkdir -p "$SCHEDULER_DIR/tasks" "$SCHEDULER_DIR/logs" "$SCHEDULER_DIR/notifications"
|
||||
chown -R claude:claude "$SCHEDULER_DIR"
|
||||
|
||||
# Start cron daemon (runs as root, executes jobs per user crontab)
|
||||
cron
|
||||
|
||||
# Save environment variables for cron jobs (cron runs with a minimal env)
|
||||
ENV_FILE="$SCHEDULER_DIR/.env"
|
||||
: > "$ENV_FILE"
|
||||
env | while IFS='=' read -r key value; do
|
||||
case "$key" in
|
||||
ANTHROPIC_*|AWS_*|CLAUDE_CODE_*|PATH|HOME|LANG|TZ|COLORTERM)
|
||||
# Escape single quotes in value and write as KEY='VALUE'
|
||||
escaped_value=$(printf '%s' "$value" | sed "s/'/'\\\\''/g")
|
||||
printf "%s='%s'\n" "$key" "$escaped_value" >> "$ENV_FILE"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
chown claude:claude "$ENV_FILE"
|
||||
chmod 600 "$ENV_FILE"
|
||||
|
||||
# Restore crontab from persisted task JSON files (survives container recreation)
|
||||
if ls "$SCHEDULER_DIR/tasks/"*.json >/dev/null 2>&1; then
|
||||
CRON_TMP=$(mktemp)
|
||||
echo "# Triple-C scheduled tasks — managed by triple-c-scheduler" > "$CRON_TMP"
|
||||
echo "# Do not edit manually; changes will be overwritten." >> "$CRON_TMP"
|
||||
echo "" >> "$CRON_TMP"
|
||||
for task_file in "$SCHEDULER_DIR/tasks/"*.json; do
|
||||
[ -f "$task_file" ] || continue
|
||||
enabled=$(jq -r '.enabled' "$task_file")
|
||||
[ "$enabled" = "true" ] || continue
|
||||
schedule=$(jq -r '.schedule' "$task_file")
|
||||
id=$(jq -r '.id' "$task_file")
|
||||
echo "$schedule /usr/local/bin/triple-c-task-runner $id" >> "$CRON_TMP"
|
||||
done
|
||||
crontab -u claude "$CRON_TMP" 2>/dev/null || true
|
||||
rm -f "$CRON_TMP"
|
||||
echo "entrypoint: restored crontab from persisted tasks"
|
||||
fi
|
||||
|
||||
# ── Stay alive as claude ─────────────────────────────────────────────────────
|
||||
echo "Triple-C container ready."
|
||||
exec su -s /bin/bash claude -c "exec sleep infinity"
|
||||
|
||||
Reference in New Issue
Block a user