今日已更新 240 条资讯 | 累计 23823 条内容
关于我们

标签:#launchd

找到 3 篇相关文章

AI 资讯

Automating a Daily Morning Health Check for Your Claude Code Setup with launchd

In my previous post, Monitoring Claude Code hook watchdogs with launchd , I set up liveness monitoring for hooks — and immediately ran into the next question: a healthy hook means nothing if the product behind it is down. What I really wanted was a single page I could skim in five minutes every morning and know that everything is fine. That page is daily-brief.sh . launchd runs it three times a day (8:00, 10:30, and at login), and it compiles production HTTP probes, hook latency p95, launchd exit codes, 7-day API costs broken down by model, and per-project git status into one Markdown file appended to Obsidian. The problem: checking five places by hand every morning The more you automate, the higher the risk that something breaks silently. I used to open all of these manually every morning: The Vercel dashboard (production liveness) launchctl logs (scheduled job failures) Claude Code cost usage git status for each project The hook latency JSONL Just opening them took 3–5 minutes. Two incidents slipped through unnoticed (2026-06-11: GitHub Scout silently going blank, and a server configuration error in the autolike license API). Consolidating everything into one automatically delivered page makes missing things physically impossible. Overall design: 3 triggers → 1 Markdown file → append to Obsidian launchd ├─ StartCalendarInterval: 8:00 ├─ StartCalendarInterval: 10:30 └─ RunAtLoad: true(ログイン時) ↓ ~/.claude/scripts/daily-brief.sh ↓ ~/.claude/logs/daily-brief-YYYYMMDD.md ← 正本ログ ~/.claude/logs/daily-brief-latest.md ← 最新コピー ~/Desktop/Daily Brief/today-brief-YYYYMMDD.md ~/Documents/claude-obsidian/wiki/briefs/daily/today-brief-YYYYMMDD.md Even when the second run fires at 10:30, the marker <!-- daily-brief YYYYMMDD --> prevents duplicate appends (details below). The script opens like this: #!/usr/bin/env bash # launchd で毎日 8:00 / 10:30 / ログイン時 実行(再実行してもマーカーで二重追記しない)。 # 注意: Desktop / ~/Documents(vault) は TCC 保護領域 → plist は /bin/bash 直起動(FDA付与済み)。 # /bin/zsh 経由だと FDA 未付与で書き込

2026-07-25 原文 →
AI 资讯

It Can Die in Its Sleep — Self-Healing launchd Jobs with Multi-Slot Firing and a Done-Marker

My previous piece, " Making a launchd Job Unload Itself ," built a job that runs exactly once and then unloads itself. This time it's the mirror image: a pattern designed around the assumption that the job will die mid-run — it fires several slots a day and delivers "retry until it succeeds, then quit immediately on success." Every morning I hand Claude Code the task of updating my Obsidian Vault, and it kept dying partway through — killed by macOS sleep, no network on wake, or a claude timeout. Instead of trying to prevent every failure perfectly, I decided "it can die overnight as long as it's done by the time I wake up" was the more realistic goal, and I redesigned around that. The problem: "started but didn't finish" piles up silently There are three ways a launchd job fails to run to completion. Lid-close sleep (on battery) — caffeinate -s only works on AC power. On battery, the job freezes the instant you close the lid, and gets reaped by timeout after wake. No network — right after wake, WiFi isn't connected yet. git push and claude's API calls time out. claude timeout — an ingest that chews through 28 hours of conversation logs doesn't fit in a single slot and times out (this happened three days in a row, 2026-06-11 to 13). All three can look like "the job started, exit code 0," so you notice late. Overall design: 4 slots + a done-marker The fix is simple: stuff multiple StartCalendarInterval entries into the plist, and at the top of the script check "if today's run already succeeded, exit 0 immediately." <!-- com.shun.vault-auto-ingest.plist (StartCalendarInterval excerpt) --> <key> StartCalendarInterval </key> <array> <dict> <key> Hour </key><integer> 4 </integer><key> Minute </key><integer> 55 </integer> </dict> <dict> <key> Hour </key><integer> 8 </integer><key> Minute </key><integer> 20 </integer> </dict> <dict> <key> Hour </key><integer> 10 </integer><key> Minute </key><integer> 45 </integer> </dict> <dict> <key> Hour </key><integer> 12 </integer><key>

2026-07-19 原文 →
AI 资讯

Planting a Future Breaking Change Today: A launchd Timer Job That Deletes Itself When Done

This is a follow-up to my earlier post, " Automating a config migration with a one-shot launchd job ." Some breaking changes come with a known expiration date, and you can prepare for them long before they land. This time the external event was the end-of-life of Fable 5 (2026-07-07), and I'll walk through how I designed a launchd job you set up today, that fires only on the target day, and that removes itself once it's done. The whole thing started with the thought, "manually fixing this on the shutdown day is going to be annoying." But I also didn't want to run a script every morning that needlessly rewrites JSON. What I landed on was a three-part set: a date gate, a jq rewrite with a backup, and self-unload. The problem: on the day I learn about a deprecation, I want to plant a job that "only runs on the target day" Right now, ~/.claude/settings.json looks like this: { "model" : "claude-fable-5[1m]" , ... } The moment I learned Fable 5 would end on 2026-07-07, creating a calendar reminder to manually rewrite this "model" felt too flimsy — I'll forget. On the other hand, making "a daemon that checks the date every time it boots" is overkill. What I wanted was a job I could set once and leave alone, that runs when the day arrives, and then disappears. launchd can fire at a specified time via StartCalendarInterval . But you can't express "just once at 9:00 on 7/7"; you need a combination of recurring and date-fixed slots. Specifying multiple slots and absorbing the redundancy with idempotency is the standard trick on macOS launchd. The implementation: the three-part set Here's the full ~/.claude/scripts/model-transition-0707.sh (comments omitted). #!/bin/bash set -uo pipefail SETTINGS = " $HOME /.claude/settings.json" LOG = " $HOME /.claude/logs/model-transition.log" PLIST = " $HOME /Library/LaunchAgents/com.shun.model-transition-0707.plist" log () { echo "[ $( date '+%F %T' ) ] $* " >> " $LOG " ; } # ① 日付ゲート if [ " $( date +%Y%m%d ) " -lt 20260707 ] ; then log "ski

2026-07-11 原文 →