awakebook
WIP(現在進行中)
Work In Progress
このプロジェクトで現在進行中の作業と、過去のスナップショットを記録する。
現在の状況
フェーズ: MVP+永続化/Customタイマー+多言語(9言語) 完成 / Codex QA ラウンド1 対応済み・再検証待ち
- M0〜M7(MVP)+ M8 一部(設定永続化・Custom タイマー)+ 多言語対応9言語 まで実装、実アプリ手動確認済み。
- Codex QA ループ導入済み(
.codex-review/+AGENTS.md)。ラウンド1で F-001/F-002(両方低)を対応・push 済み → Codex の再検証待ち。 - コア機構(pmset disablesleep)はライブ検証済み。受け入れ基準 §11 を満たす。
- 多言語:
Localization.swiftの手書きテーブル方式(システム言語追従、英語フォールバック)。英/日/繁/簡/韓/西/葡/仏/独。--dump-localizationで網羅性確認可。 - 全ソース:
Package.swift/Sources/awakebook/{main,AppDelegate,MenuBarController,SleepController,AutoOffTimer,BatteryMonitor,PowerStateObserver,Settings,Localization}.swift/Scripts/{install,uninstall}-sudoers.sh。 - sudoers ルール
/etc/sudoers.d/awakebook配置済み(このマシン)。release バイナリ.build/release/awakebook(約196K)。
次にやること
- Codex に再検証を依頼(F-001 訳語のネイティブ視点での自然さ、F-002 アイコンの見た目)。新 findings が来たら
codex-qa-loop(「findings 処理して」)で回す。 - M8 残(任意):
.appバンドル化+Info.plist(LSUIElement=YES)+署名/notarization、ログイン項目登録(SMAppService.mainApp)、(opt) 蓋閉じ輝度0。 - バンドルアイコン設定(.app 化とセット。F-002 の根本対応)。
既知の小課題
- カウントダウン表記が1時間境界で "1:00"→"59:59" と意味が切り替わる(後で精緻化候補)。
- 低電力モードのライブ発火確認は未実施(ロジック検証のみ)。
- 用語ゆれ: メニューは「スリープさせない」、README/エラー文は「スリープ抑止」。統一は将来検討。
メモ / 注意(ハマりどころ)
pmset -gの値区切りはタブ。SleepDisabled は 0 のとき行が出ないこともあれば、一度設定後は\t\t0で残ることもある → 値で判定(パーサ対応済み)。- macOS の
/bin/bashは 3.2。シェルスクリプトで全角文字直前の$VARは${VAR}と波括弧で囲う(誤解釈する)。 - 実機トグル検証は disablesleep を一時的に 1 にするので、必ず 0 に戻すこと。
確定事項
- アプリ正式名称は
awakebook(仕様書のlid-awakeは仮称。リポジトリ名に合わせて確定)。バンドル ID / 表示名 / sudoers ファイル名はこれに揃える。
過去のWIPアーカイブ
(新しい「現在の状況」を書く前に、古いものをここに追記でアーカイブする。新しいものが上)
2026-06-30 21:50 時点のスナップショット
- フェーズ: MVP+永続化/Customタイマー+多言語(9言語) 完成・実機確認済み / 残りは M8 配布系(任意)。
- Codex QA はまだ未導入だった(→ 本セッションで導入し、ラウンド1対応まで完了)。
2026-06-30 20:52 時点のスナップショット
- フェーズ: MVP(M0〜M7)完成 / 残りは M8(オプション・配布)。M8 一部(設定永続化・Custom タイマー・フロア5–50%拡張)完了。
- 全機能実装済みだが実アプリでの手動 GUI 動作確認は未実施の段階だった(→ 本セッション末で確認完了)。
ROADMAP(計画)
ロードマップ
lid-awake ビルド仕様(~/Downloads/引き続き.md)の M0〜M8 をマイルストーンとして配置。
今週
- M0 環境: メニューバーアプリ雛形(SwiftPM)、
.accessoryで Dock 非表示、空のNSStatusItem表示 — 2026-06-30 完了 - M1 コア:
SleepControllerの有効化/解除/読み戻し、Keep Awakeトグル + 状態表示 — 2026-06-30 完了(ライブ動作確認は M2 のルール配置後) - M2 権限:
install-sudoers.sh+visudo -cf検証、NOPASSWD トグルをライブ確認 — 2026-06-30 完了
今月
- M3 タイマー: オートオフ(Off/30分/1時間/2時間)+カウントダウン表示、満了で自動解除 — 2026-06-30 完了(ライブ満了確認は sudoers 配置後)。
Custom…は未実装 - M4 バッテリー:
BatteryMonitor(IOKit.ps)、フロア割れで自動解除、Battery floor ▸+残量表示 — 2026-06-30 完了(フロアは10/15/20/25/30%プリセット) - M5 低電力モード:
PowerStateObserverで連動自動解除、状態行に注記 — 2026-06-30 完了 - M6 安全網: 終了時/SIGTERM時に必ず 0 復帰、起動時の状態同期 — 2026-06-30 完了(SIGTERM 経路をE2E検証)
- M7 仕上げ: README(セットアップ/使い方)、受け入れ基準 §11 チェック完了 — 2026-06-30 完了(MVP 完成)
今四半期
- M8 設定永続化: バッテリーフロア・Custom タイマー分数を UserDefaults suite に保存 — 2026-06-30 完了
- M8 Custom タイマー: 任意分数入力、フロアを 5–50% に拡張 — 2026-06-30 完了
- M8 (opt): 蓋閉じで内蔵ディスプレイ輝度0(Wedge 相当)、ログイン項目登録、動作ログ
- M8 配布:
.appバンドル化+Info.plist(LSUIElement)+Developer ID 署名/notarization
いつか
- 権限を
SMAppService.daemon(特権ヘルパー)方式へ移行
DECISIONS(意思決定)
意思決定記録
このプロジェクトで下した重要な意思決定を記録する。 最新が上に来る。
2026-06-30 — ビルドは SwiftPM、Dock 非表示は .accessory で
- 決定: ビルドは Xcode プロジェクトではなく SwiftPM 実行可能パッケージ(
Package.swift)で行う。仕様書の「Xcode が楽」案からの変更。 - 理由:
swift buildで CLI から再現ビルド・検証でき、AI 駆動の開発フローに乗せやすい。.pbxprojの手書き/差分管理は壊れやすい。Package.swift は Xcode でもそのまま開ける。 - Dock 非表示:
Info.plistのLSUIElement=YESの代わりに、起動時NSApp.setActivationPolicy(.accessory)で実行時に等価の挙動を得る。 - 言語モード: 当面 Swift 5 モード(
swiftLanguageModes: [.v5])。Swift 6 の strict concurrency は後で対応。 - 将来: 配布時は executable を
.appバンドル化し、Info.plist にLSUIElement=YESを入れた上で Developer ID 署名+notarization する。
2026-06-30 — アプリ正式名称は awakebook
- 決定: アプリ名は
awakebookに確定。仕様書(引き続き.md)のlid-awakeは仮称で、リポジトリ名に合わせた。 - 影響: バンドル ID / 表示名 / sudoers ファイル名(
/etc/sudoers.d/awakebook)/ Xcode プロジェクト名をこれに揃える。
2026-06-30 — コア機構は pmset disablesleep、caffeinate は不採用
- 決定: スリープ抑止のコアは
sudo pmset -a disablesleep 1/0(カーネルのSleepDisabledフラグ)に一本化する。 - 理由:
caffeinateおよび caffeinate 系アプリ(KeepingYouAwake 等)は macOS の power assertion を使うため、アイドルタイマーは止められても 蓋を閉じたときのハードウェア的スリープトリガーを上書きできない。SleepDisabledフラグはIOPMrootDomainがスリープ拒否権として認識し lid-close を跨いで持続する(Apple Silicon でも有効)。 - 補足:
SleepDisabledは解除するか再起動するまで残る(永続化はされない=再起動でクリアされるので安全側)。
2026-06-30 — UI の唯一の真実は pmset -g の読み戻し
- 決定: メニューバーのアイコン/状態表示は、内部フラグではなく毎回
pmset -g | grep SleepDisabledの実値を読み戻して反映する。 - 理由: 「メニューバーが嘘をつかない」を最優先。タイマー満了・バッテリーフロア割れ・低電力モード・手動 OFF・終了 のすべての復帰経路を
SleepController.disable()に集約し、状態の単一情報源をカーネルの実フラグにするため。
2026-06-30 — 権限は sudoers NOPASSWD(MVP)、将来 SMAppService へ
- 決定: MVP では
/etc/sudoers.d/lid-awakeに指定2コマンド(pmset -a disablesleep 0/1)だけを NOPASSWD で許可する。install-sudoers.shで配置し、必ずvisudo -cfで構文検証する。 - 理由: GUI アプリはパスワードを入力できない。sudoers はコマンド+引数を完全一致で照合するため、アプリが叩く引数と一字一句揃える。壊れた sudoers は sudo 全体を壊すので検証必須。
- 将来: 配布を考えるなら
SMAppService.daemon(macOS 13+)の特権ヘルパーへ移行。SMJobBlessは非推奨なので使わない。 - 配布制約: sudoers / pmset がサンドボックス外のため App Store 配布は不可。配るなら Developer ID 署名+notarization で直配。
DEVLOG(作業ログ)
開発日誌
このプロジェクトでの作業を時系列で記録する。 最新のエントリが上に来る。
2026-07-01 12:12 — Codex QA ループ導入 + findings 対応(F-001/F-002)
- 多言語対応を含むレビューを Codex に依頼するため、
codex-qa-loopスキルで足場を生成:.codex-review/{findings,response}.md+AGENTS.md(Codex 契約セクション+「今回の重点: 多言語対応のレビュー」=各言語で起動して翻訳・繁簡取り違え・レイアウト崩れ・言語判定・書式崩れを見る観点、-AppleLanguages/--dump-localizationの使い方)。 - Codex が2件起票(両方 低)、両方対応:
- F-001: Custom ダイアログのタイトルが es/pt/fr/de で「Mac 本体の自動シャットダウン設定」に読める("apagado automático" 等)→ 全9言語で「スリープ抑止を続ける時間」と分かる表現に統一(
Keep awake for (分)系、keepAwake の訳語と整合)。 - F-002: Custom ダイアログに無関係な青いフォルダ風アイコン(.app バンドル未作成でアプリアイコンが無いため)→ アラートに
moon.zzz(48pt) を設定。エラーアラート(.warning)は caution アイコンが妥当なので対象外。バンドルアイコンは M8 で対応予定。 - 検証: release ビルド・
--dump-localization(新タイトル+欠落0)・常駐起動 OK。アイコンの見た目はヘッドレスで未確認(実ダイアログで要確認)。
- F-001: Custom ダイアログのタイトルが es/pt/fr/de で「Mac 本体の自動シャットダウン設定」に読める("apagado automático" 等)→ 全9言語で「スリープ抑止を続ける時間」と分かる表現に統一(
- 文言判断: メニューの Keep Awake 日本語は候補(スリープ防止/抑止/起こしておく)を提示の上、「スリープさせない」を維持(現状維持)。
- 気づき: メニューは「スリープさせない」だが README/エラー文は「スリープ抑止」を使用。用語の統一は将来検討の余地あり(今回は未着手)。
2026-06-30 21:50 — 多言語対応(9言語)
- メニュー UI 全文字列を9言語対応: English / 日本語 / 繁體中文(zh-Hant) / 简体中文(zh-Hans) / 한국어 / Español / Português / Français / Deutsch。
- 方式:
.appバンドル外でも確実に動くよう、リソースバンドルに依存しない手書きローカライズテーブルLocalization.swift(enum L+LocKey+[LocKey: [Lang: String]])。Locale.preferredLanguagesから解決し未対応は英語フォールバック。言語変更は再起動で反映。 - 対象: メニュー項目 / タイマープリセット・Custom / ステータス行 / バッテリー行 / Custom ダイアログ / エラーアラート /
SleepController.Failureのメッセージ。SleepDisabled(カーネルフラグ名)と数値%は非翻訳。 - ついでの改善: tick 時のステータス行更新が
hasPrefix("Status:")前提で壊れていたため、updateStatusLine(on: true)呼び出しに変更(pmset は叩かない)。 - 検証:
--dump-localization診断フラグを追加。欠落 0(23キー×9言語)、全言語で書式文字列(%d/%@)の描画が正常。言語判定ロジックを18ケース(zh-Hant/Hans 各種、順序付き優先、フォールバック)でテスト全 PASS。通常起動・release ビルド確認。 - 補足: システム設定を変えずに
awakebook -AppleLanguages '(fr)'等で任意言語をプレビュー可能(README に追記)。
2026-06-30 20:52 — セッション総括(仕様受領 → MVP+α 完成・実機動作確認)
- このセッションで
~/Downloads/引き続き.md(lid-awake仮称仕様)を起点に、awakebook を ゼロから M0〜M7(MVP)+ M8 一部 まで一気に実装。 - 流れ: 仕様を
.devnotes/へ流し込み → 名称確定 → M0 雛形(SwiftPM) → M1 コア → M2 sudoers(ライブ検証) → M3 タイマー → M4 バッテリー → M5 低電力 → M6 安全網(SIGTERM E2E) → M7 README/受け入れ基準 → M8 設定永続化+Custom タイマー。 - 途中で実バグ2件を発見・修正: ①
pmset -gタブ区切りでパーサが値1を読めず → 空白/タブ両対応に修正、②install-sudoers.sh の bash3.2 全角変数バグ →${VAR}化。 - ユーザーが実アプリを起動し手動で全機能の動作を確認 → 正常動作(「動いたーーー」)。
- 現状: 実用上「蓋を閉じて Claude Code を走らせ続ける」が使える状態。残りは M8 の配布系(
.appバンドル化+署名/notarization、ログイン項目登録)と任意の輝度0。 - コミット履歴: c84dd5e → e331348(計10コミット、すべて push 済み)。
2026-06-30 — M8(一部): 設定の永続化 + Custom タイマー
- 設定の永続化:
Settings(UserDefaults(suiteName: "com.awakebook"))を追加。.appバンドル外の bare executable でも確実に保存されるよう専用 suite を使用。- バッテリーフロアを保存・起動時復元(
BatteryMonitor(floorPercent: Settings.batteryFloor)、selectFloorで保存)。 - Custom タイマーの最終入力分数を保存(次回入力ダイアログの初期値)。
- バッテリーフロアを保存・起動時復元(
- Custom タイマー: Timer submenu に
Custom…を追加。NSAlert+NSTextFieldで 1〜1440 分を入力 → keepAwake ON+カウントダウン開始。プリセットに無い値のときは Custom にチェック+残り時間をラベル表示。 - フロア範囲拡張: 仕様の 5–50% に合わせ、プリセットを
[5,10,15,20,25,30,40,50]に拡張。 - 検証:
swift build成功・常駐起動。UserDefaults suite のラウンドトリップ単体テスト PASS。README のメニュー表を更新(Custom、フロア5–50%、再起動後保持)。 - 残 M8:
.appバンドル化+署名/notarization、ログイン項目登録、(opt) 蓋閉じ輝度0。
2026-06-30 — M7 仕上げ: README + 受け入れ基準チェック(MVP 完成)
- README に「必要環境 / セットアップ(
swift build -c release+install-sudoers.sh)/ 使い方(メニュー各項目)/ 注意」を記入。README 内の旧称lid-awakeの名残も整理(正式名awakebook)。 - release ビルドを README 手順どおり確認(
.build/release/awakebook、約140K)。起動・常駐・終了・SIGTERM 復帰を確認。 - 受け入れ基準(仕様 §11)チェック:
- ✅ コア機構(pmset disablesleep)をライブ検証済み。
- ✅
menuWillOpenで毎回pmset -gを読み戻し、表示が実値と一致。 - ✅ タイマー満了/フロア割れ/低電力/手動OFF/終了 が全て
disableKeepAwake()・終了クリーンアップに集約。SIGTERM 経路は E2E 検証済み。 - ✅ install/uninstall スクリプトとも
visudo -cf検証付き。配置はparsed OK確認済み。
- これで MVP(M0〜M7)完成。残りは M8(
.appバンドル化/署名・notarization、ログイン項目、Custom タイマー、設定永続化、蓋閉じ輝度0 など)。
2026-06-30 — M5/M6 実装: 低電力モード連動 + 安全網
- M5(低電力モード連動):
PowerStateObserverを実装。.NSProcessInfoPowerStateDidChangeを監視し、off→on のエッジでonEnteredLowPowerModeを発火。MenuBarController.handleLowPowerMode()で keepAwake が ON のときだけdisableKeepAwake()。状態行に[低電力モード]を併記。 - M6(安全網):
- 終了時:
AppDelegate.applicationWillTerminate→MenuBarController.cleanupOnTerminate()でsetDisableSleep(false)(メニューの Quit 経路)。 - 異常終了:
main.swiftに SIGINT/SIGTERM のDispatchSourceハンドラを追加し、kill されてもsetDisableSleep(false)してからexit(0)。 - 起動時同期:
syncOnLaunch()で実フラグと UI を同期。起動直後すでに低電力モードかつ ON なら念のため解除。前回 ON のまま落ちた値(同一ブート内のみ残存)は UI に反映(再起動で自動クリア)。
- 終了時:
- 検証:
swift build成功。M6 をエンドツーエンドで確認 — disablesleep=1 でアプリ起動(低電力 off なので 1 維持=正しい)→ SIGTERM 終了 →pmset -gが 0 に復帰。 - 補足: 低電力モードのライブ発火は実機で再現しづらいためロジック検証に留めた(解除経路は M2 で実証済みの共通経路)。
2026-06-30 — M4 実装: バッテリー監視
BatteryMonitorを実装。IOKit.ps(IOPSCopyPowerSourcesInfo/IOPSCopyPowerSourcesList/IOPSGetPowerSourceDescription)で残量%を取得。- 変化検知は
IOPSNotificationCreateRunLoopSourceを main RunLoop(.commonModes)に登録。取りこぼし対策に 60 秒ポーリングを併用。 - フロア割れは復帰するまで一度だけ通知する
hasBreachedラッチ付き。MenuBarController.handleFloorBreached()で keepAwake が ON のときだけdisableKeepAwake()(M2 で実証済みの共通解除経路)。 - メニューに
Battery floor ▸(10/15/20/25/30%、既定20%)とBattery: NN%(フロア NN%)表示を追加。 - 検証:
swift build成功・常駐起動。readBatteryPercent()単体テストで実機 100%(pmset と一致)を確認。フロア割れ→解除の経路は M2 の共通経路を流用。 - 補足: 仕様のフロア設定範囲は 5–50% だが、まずは 10–30% のプリセットを提供(後で拡張可)。
2026-06-30 — M2 ライブ検証完了+パーサのバグ修正
- ユーザーが
sudo bash Scripts/install-sudoers.shを実行。/etc/sudoers.d/awakebook(root:wheel / 0440)配置成功、visudo -cfも通過。 - ライブ検証成功:
sudo -n /usr/bin/pmset -a disablesleep 1/0がパスワードなしで通り、pmset -gのSleepDisabledが 1/0 に追従。NOPASSWD・コア機構がエンドツーエンドで動作確認。 - バグ発見&修正: 実機の
pmset -gは値区切りがタブ(SleepDisabled\t\t1)。isSleepDisabled()は半角スペースで split していたため 値1を true 判定できていなかった。値1を実機で読めなかったため未検出だった。- 修正:
split(whereSeparator: { $0.isWhitespace })で空白・タブ両対応。パース部をparseSleepDisabled(from:)に純粋関数として分離。 - 検証: タブ/スペース×0/1・行なし・空文字・他行混在の7ケース全 PASS。さらに実機 flag=1 の生出力を捕獲してパーサに通し true 判定を確認(テスト後 0 に復帰)。
- 修正:
- スクリプト修正: macOS の
/bin/bash3.2 は全角文字直前の$VARを誤解釈する。install-sudoers.sh の最終 echo を${VAR}波括弧に修正(配置自体は元から成功、cosmetic な行のみ)。
2026-06-30 — M3 実装: オートオフタイマー
AutoOffTimerを実装。start(duration:)で 1秒刻みのTimer(.commonモード登録でメニュー操作中も止まらない)を回し、onTickで残り秒数、onExpireで満了を通知。MenuBarControllerにTimersubmenu(Off(無期限) / 30分 / 1時間 / 2時間)を追加。時間を選ぶと keepAwake を ON にしてカウントダウン開始、満了でdisableKeepAwake()(手動 OFF・自動解除と同じ共通経路)。- メニューバーのタイトルに残り時間を併記(
updateCountdownTitle/ tick で更新)。状態行にも「(残り H:MM)」を表示。 - 表示形式
formatCountdown: 1時間以上 "H:MM"、未満 "M:SS"。既知の小課題: 1時間境界で "1:00"(1h)→"59:59"(59m59s) と意味が切り替わる。後で精緻化候補。 - 検証:
swift build成功、常駐起動・正常終了。タイマーのライブ満了確認は keepAwake の ON が必要=sudoers 配置後にまとめて確認。 - 未実装(M3 スコープ外):
Custom…(任意時間入力)。後続で追加候補。
2026-06-30 — M1/M2 実装: SleepController + sudoers スクリプト
- M1(コア):
SleepControllerを実装。setDisableSleep(_:)→sudo -n /usr/bin/pmset -a disablesleep 1/0。-n(非対話)で sudoers 未設定時に GUI がパスワード待ちでフリーズせず即失敗する。isSleepDisabled()→pmset -gのSleepDisabled行を読み戻し。MenuBarControllerにKeep Awakeトグル+Status: SleepDisabled=x表示を追加。メニューを開くたび(menuWillOpen)実値を読み戻すので表示が嘘をつかない。切り替え失敗時はNSAlertで install-sudoers.sh の案内を出す。
- M2(権限):
Scripts/install-sudoers.sh/uninstall-sudoers.shを作成。一時ファイルに書いてvisudo -cfで検証 →install -m 0440 -o root -g wheelで/etc/sudoers.d/awakebookに配置 → 再検証。対象ユーザーはSUDO_USERから特定。 - 発見(非自明):
pmset -gはSleepDisabledが 0 のとき行ごと出力されない(1 のときだけSleepDisabled 1が出る)。よって「行が無ければ false」で判定するのが正しい。isSleepDisabled()はこの挙動に対応済み。 - 検証:
swift build成功、新メニューでの常駐起動・正常終了を確認。pmset -g読み戻し(sudo 不要)も動作確認。 - 未検証(要ユーザー操作): ライブのトグル成功確認は sudoers 配置が前提で、配置にはパスワードが要る。
sudo bash Scripts/install-sudoers.sh実行後に NOPASSWD トグルを検証する。
2026-06-30 — M0 完了: メニューバーアプリ雛形
- ビルド方式を SwiftPM 実行可能パッケージに決定(仕様の「Xcode プロジェクト」案から変更)。
swift buildで CLI 再現ビルド・検証でき、git にクリーンに乗るため。詳細は DECISIONS.md。 - Dock アイコン非表示は
LSUIElementの代わりにNSApp.setActivationPolicy(.accessory)(実行時の等価設定)で実現。.appバンドル化は後続で対応。 - ファイル構成:
Package.swift/Sources/awakebook/{main,AppDelegate,MenuBarController}.swift。.gitignoreに Swift/Xcode 系を追加。 MenuBarControllerはmoon.zzzシンボルのNSStatusItem+Quitのみ(M0 雛形)。- 検証:
swift build成功(Swift 6.3.2 / 言語モード 5 / macOS .v13)。バイナリ起動 → メニューバー常駐を確認 → 正常終了。 - 次は M1(
SleepControllerの有効化/解除/読み戻し)。
2026-06-30 — ビルド仕様を受領、開発の足場を確定
~/Downloads/引き続き.md(lid-awake仮称のビルド仕様書)を受領。本プロジェクトawakebookの実装対象として採用。- アプリ概要: 蓋を閉じた状態・バッテリー駆動・外部ディスプレイなしでも Mac を起こし続ける macOS メニューバーアプリ(Sleepless / Amphetamine 相当の自作版)。狙いは Claude Code のリモートセッションや長時間ジョブを蓋閉じで走らせ続けること。
- コア機構を
sudo pmset -a disablesleep 1/0(カーネルのSleepDisabledフラグ)に確定。caffeinate系は power assertion で蓋閉じスリープを止められないため不採用。詳細は DECISIONS.md。 - 仕様を
.devnotes/(DEVLOG / WIP / DECISIONS / ROADMAP)と CLAUDE.md / README に流し込み、M0〜M8 のマイルストーンを ROADMAP に配置。 - 次の一手は M0(Xcode メニューバーアプリ雛形 +
LSUIElement+ 空のNSStatusItem表示)。
最近のコミット
- 058828e .appバンドル生成スクリプトを追加(Applicationsから起動可能に) 2026/7/2
- 3a62c32 .devnotes更新: Codex QA導入とfindings F-001/F-002対応を記録 2026/7/1
- 7a128c8 Codex QA findings F-001/F-002 対応 2026/7/1
- 1d84dab メニューUIを9言語に多言語対応 2026/6/30
- efac66d .devnotes更新: セッション総括と現在状況の整理(MVP+α完成・実機確認済み) 2026/6/30
- e331348 M8一部: 設定の永続化とCustomタイマーを実装 2026/6/30
- 2a81444 M7: READMEのセットアップ/使い方を整備し受け入れ基準を確認(MVP完成) 2026/6/30
- f5a978b M5/M6: 低電力モード連動と安全網を実装 2026/6/30
- 8157341 M4: バッテリー監視とフロア割れ自動解除を実装 2026/6/30
- d6bba56 M2ライブ検証完了とSleepDisabledパーサのタブ対応修正 2026/6/30
README
awakebook
概要
蓋を閉じた状態・バッテリー駆動・外部ディスプレイなしでも Mac を起こし続ける macOS メニューバーアプリ。 Claude Code のリモートセッションや長時間ジョブを、MacBook の蓋を閉じたまま・電源につながず走らせ続けるためのツール。
「一晩中スリープしない」「バッテリーを使い切る」事故を防ぐため、安全装置(オートオフタイマー・バッテリー下限・低電力モード連動)付きで実装している。Sleepless / Amphetamine 相当を自作するプロジェクト。
しくみ
- コアは
sudo pmset -a disablesleep 1/0(カーネルのSleepDisabledフラグ)。蓋を閉じてもスリープしなくなる。 caffeinate系(power assertion)は蓋閉じスリープを止められないため使わない。- メニューバーの表示は常に
pmset -gの実値を読み戻して反映する(表示が嘘をつかない)。 SleepDisabledは再起動でクリアされる(永続化されない)ため、戻し忘れても再起動すれば安全側。
必要環境
- macOS 13 以降(Apple Silicon / Intel)
- Xcode コマンドラインツールまたは Xcode(
swiftが使えること)
セットアップ
1. ビルド
git clone https://github.com/ichirokisanuki/awakebook.git
cd awakebook
swift build -c release
ビルド成果物: .build/release/awakebook
2. sudoers ルールの配置(必須)
GUI アプリはパスワードを入力できないため、pmset の切り替え 2 コマンドだけをパスワードなしで許可する sudoers ルールを置く。visudo -cf 検証付きで安全に配置される。
sudo bash Scripts/install-sudoers.sh
実行すると /etc/sudoers.d/awakebook(root:wheel / 0440)が配置され、最後に確認用コマンドが表示される。許可されるのは次の 2 つだけ:
/usr/bin/pmset -a disablesleep 0
/usr/bin/pmset -a disablesleep 1
ルールを外すとき:
sudo bash Scripts/uninstall-sudoers.sh
3. 起動
.build/release/awakebook
メニューバーに月アイコンが出れば常駐成功(Dock アイコンは出ない)。ログイン項目への登録は今後対応予定。
使い方
メニューバーの月アイコンをクリックすると以下のメニューが出る。
| 項目 | 説明 |
|---|---|
| Keep Awake | スリープ抑止の ON/OFF トグル。ON 中はアイコンが塗りつぶしになる |
| Timer ▸ | オートオフ(Off=無期限 / 30分 / 1時間 / 2時間 / Custom…=任意の分数)。時間を選ぶと自動的に Keep Awake が ON になり、満了で自動解除。ON 中はアイコン横に残り時間を併記 |
| Battery floor ▸ | バッテリー下限(5〜50%、既定20%)。残量がこれを下回ると自動解除。設定は再起動後も保持される |
| Status: SleepDisabled = x | pmset -g の実値(タイマー作動中は残り時間、低電力モード中は [低電力モード] も表示) |
| Battery: NN% | 現在の残量とフロア設定 |
| Quit | 終了(終了時に必ずスリープ抑止を解除する) |
次のいずれでも確実に解除される: タイマー満了 / バッテリーフロア割れ / 低電力モード突入 / 手動 OFF / アプリ終了(Quit・kill 含む)。
多言語対応
メニュー UI は macOS のシステム言語に追従して以下9言語で表示される(未対応言語は英語にフォールバック)。
English / 日本語 / 繁體中文 / 简体中文 / 한국어 / Español / Português / Français / Deutsch
システム設定を変えずに特定言語を試したいときは、起動時に言語を指定できる:
.build/release/awakebook -AppleLanguages '(fr)' # フランス語で起動
.build/release/awakebook -AppleLanguages '(zh-Hant)' # 繁体字で起動
翻訳は Sources/awakebook/Localization.swift の1テーブルに集約している。網羅性と書式の確認は awakebook --dump-localization で行える。
注意
- 蓋を閉じると排気が落ちる。重い持続負荷を蓋閉じで回すのは要注意(熱)。
- 充電が一定以下になると
pmsetの設定に関係なく macOS がハードウェアレベルで強制スリープする(回避不可)。バッテリーフロアはその手前で安全に止めるためのもの。 - App Store 配布は不可(sudoers / pmset がサンドボックス外)。配るなら Developer ID 署名+notarization で直配する。