@mohitkumawat/warmup-cli 1.2.15 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +37 -14
  2. package/dist/commands/default.d.ts +3 -1
  3. package/dist/commands/default.d.ts.map +1 -1
  4. package/dist/commands/default.js +25 -0
  5. package/dist/commands/default.js.map +1 -1
  6. package/dist/commands/doctor.d.ts +51 -0
  7. package/dist/commands/doctor.d.ts.map +1 -0
  8. package/dist/commands/doctor.js +219 -0
  9. package/dist/commands/doctor.js.map +1 -0
  10. package/dist/commands/execute.d.ts +24 -1
  11. package/dist/commands/execute.d.ts.map +1 -1
  12. package/dist/commands/execute.js +53 -21
  13. package/dist/commands/execute.js.map +1 -1
  14. package/dist/commands/logs.d.ts +12 -0
  15. package/dist/commands/logs.d.ts.map +1 -0
  16. package/dist/commands/logs.js +27 -0
  17. package/dist/commands/logs.js.map +1 -0
  18. package/dist/commands/menu.d.ts +52 -0
  19. package/dist/commands/menu.d.ts.map +1 -0
  20. package/dist/commands/menu.js +185 -0
  21. package/dist/commands/menu.js.map +1 -0
  22. package/dist/commands/setup.d.ts +7 -0
  23. package/dist/commands/setup.d.ts.map +1 -1
  24. package/dist/commands/setup.js +94 -5
  25. package/dist/commands/setup.js.map +1 -1
  26. package/dist/commands/status.d.ts +8 -1
  27. package/dist/commands/status.d.ts.map +1 -1
  28. package/dist/commands/status.js +89 -4
  29. package/dist/commands/status.js.map +1 -1
  30. package/dist/commands/test.d.ts.map +1 -1
  31. package/dist/commands/test.js +25 -4
  32. package/dist/commands/test.js.map +1 -1
  33. package/dist/commands/uninstall.d.ts.map +1 -1
  34. package/dist/commands/uninstall.js +15 -0
  35. package/dist/commands/uninstall.js.map +1 -1
  36. package/dist/config.d.ts +23 -1
  37. package/dist/config.d.ts.map +1 -1
  38. package/dist/config.js +38 -0
  39. package/dist/config.js.map +1 -1
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +32 -3
  42. package/dist/index.js.map +1 -1
  43. package/dist/notify.d.ts +14 -0
  44. package/dist/notify.d.ts.map +1 -0
  45. package/dist/notify.js +91 -0
  46. package/dist/notify.js.map +1 -0
  47. package/dist/runtime.d.ts +10 -0
  48. package/dist/runtime.d.ts.map +1 -1
  49. package/dist/runtime.js +39 -8
  50. package/dist/runtime.js.map +1 -1
  51. package/dist/scheduler/index.d.ts +17 -0
  52. package/dist/scheduler/index.d.ts.map +1 -1
  53. package/dist/scheduler/index.js +42 -0
  54. package/dist/scheduler/index.js.map +1 -1
  55. package/dist/scheduler/linux.d.ts.map +1 -1
  56. package/dist/scheduler/linux.js +3 -10
  57. package/dist/scheduler/linux.js.map +1 -1
  58. package/dist/scheduler/macos.d.ts +38 -0
  59. package/dist/scheduler/macos.d.ts.map +1 -1
  60. package/dist/scheduler/macos.js +158 -23
  61. package/dist/scheduler/macos.js.map +1 -1
  62. package/dist/scheduler/script.d.ts +18 -0
  63. package/dist/scheduler/script.d.ts.map +1 -0
  64. package/dist/scheduler/script.js +62 -0
  65. package/dist/scheduler/script.js.map +1 -0
  66. package/dist/scheduler/windows.js +1 -1
  67. package/dist/ui.d.ts +50 -4
  68. package/dist/ui.d.ts.map +1 -1
  69. package/dist/ui.js +250 -38
  70. package/dist/ui.js.map +1 -1
  71. package/dist/warmup.d.ts +52 -4
  72. package/dist/warmup.d.ts.map +1 -1
  73. package/dist/warmup.js +194 -79
  74. package/dist/warmup.js.map +1 -1
  75. package/package.json +3 -1
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <div align="center">
2
- <img src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Food/Hot%20Beverage.png" alt="WarmUp Logo" width="100" />
2
+ <img src="https://raw.githubusercontent.com/mohit-kumawat/warmup/main/assets/logo.png" alt="WarmUp Logo" width="100" />
3
3
 
4
4
  # ☕ WarmUp
5
5
 
@@ -14,23 +14,44 @@
14
14
  [![Stars](https://img.shields.io/github/stars/mohit-kumawat/warmup?style=for-the-badge&color=6C5CE7&logo=github)](https://github.com/mohit-kumawat/warmup)
15
15
 
16
16
  <br />
17
+
18
+ `npm install -g @mohitkumawat/warmup-cli`
19
+
20
+ <br />
17
21
  </div>
18
22
 
19
23
  ---
20
24
 
25
+ ```console
26
+ $ warmup
27
+
28
+ warmup v1.3.0
29
+
30
+ Schedule: 7:00 AM daily (America/New_York) active
31
+ Wake: scheduled (runs while asleep)
32
+ Last run: 7:00 AM ✔ success
33
+ Window: [████████████▓░░░░░░░░░░░░░░░░░] 45% elapsed
34
+ start now reset
35
+ 7:00 AM 9:15 AM 12:00 PM
36
+
37
+ ✔ Window resets in ~2h 45m. Full capacity at 12:00 PM.
38
+ ```
39
+
40
+ ---
41
+
21
42
  ## 🛑 The Problem
22
43
 
23
- Claude Pro and Max subscribers share a **5-hour rolling rate limit window** across Claude Code, Cowork, and claude.ai. When you start an intensive coding session, you burn through your allocation quickly and are forced to wait 2-3 hours for the window to reset.
44
+ Claude Pro and Max subscribers share a **5-hour rolling rate limit window** across Claude Code, Cowork, and claude.ai. When you start an intensive coding session, you burn through your messages fast then sit idle for 23 hours waiting for the window to reset.
24
45
 
25
46
  ## 🟢 The Solution
26
47
 
27
48
  **`warmup`** starts your rate limit window *before* you wake up by scheduling one tiny Claude Code ping (~10 tokens) at the exact right time.
28
49
 
29
- By the time you sit down to work, the window is perfectly timed to expire exactly when you need it, granting you a **100% fresh allocation** right in the middle of your workday.
50
+ By the time you sit down to work, the window is timed to **reset exactly when you'd otherwise hit the limit** — handing you **100% fresh capacity** in the middle of your workday.
30
51
 
31
52
  ### ⏱️ Side-by-Side Comparison
32
53
 
33
- | ❌ Without `warmup` | ✅ With `warmup` (2-hour exhaustion) |
54
+ | ❌ Without `warmup` | ✅ With `warmup` |
34
55
  | :--- | :--- |
35
56
  | **10:00 AM** → You start working, window starts. | **7:00 AM** → `warmup` sends one tiny ping (window starts). |
36
57
  | **12:00 PM** → **Rate limited!** 🛑 Must wait until 3:00 PM. | **10:00 AM** → You start working using the active window. |
@@ -39,6 +60,8 @@ By the time you sit down to work, the window is perfectly timed to expire exactl
39
60
 
40
61
  > ***You just saved over 3 hours of waiting.***
41
62
 
63
+ <sub>Times are illustrative — this example assumes ~2 hours of heavy use before you hit the limit.</sub>
64
+
42
65
  ---
43
66
 
44
67
  ## ⚡ Installation
@@ -53,13 +76,13 @@ npm install -g @mohitkumawat/warmup-cli
53
76
 
54
77
  ## 🚀 Quick Start
55
78
 
56
- Run the following command anywhere in your terminal:
79
+ Run `warmup` anywhere in your terminal:
57
80
 
58
81
  ```bash
59
82
  warmup
60
83
  ```
61
84
 
62
- *Fresh installs open a **guided onboarding wizard**.*
85
+ *First run? It opens a **guided setup wizard** — no flags to memorize.*
63
86
 
64
87
  ### 🔍 What happens behind the scenes?
65
88
 
@@ -68,10 +91,10 @@ warmup
68
91
  - 📊 Shows a detailed geometric preview of the scheduled timeline before explicitly asking for confirmation.
69
92
  - 💾 Stores config and logs purely locally in `~/.warmup/`.
70
93
 
71
- ### 🛡 What DOES NOT happen?
94
+ ### 🛡️ What DOES NOT happen?
72
95
 
73
96
  - ❌ Setup does **not** send a live Claude request or burn any limits.
74
- - ❌ `warmup` does **not** proxy your session, extract tokens, or phone home to any backend API. Privacy first.
97
+ - ❌ `warmup` does **not** proxy your session, extract tokens, or phone home to any server. Privacy first.
75
98
 
76
99
  ---
77
100
 
@@ -90,15 +113,15 @@ It then automatically calculates the **exact optimal pre-warm time** to ensure y
90
113
 
91
114
  **What if my computer is off or asleep at the scheduled pre-warm time?**
92
115
 
93
- `warmup` handles this automatically using native OS features. If your laptop was closed at your 5:00 AM pre-warm time, the pre-warm fires **immediately upon waking your machine** (e.g., at 8:00 AM).
116
+ **`warmup` wakes your machine a few minutes early and fires the ping while you're still asleep** — so your fresh window is ready before you sit down, not hours later when you open the lid. If the machine was fully powered off, it catches up on the next boot.
94
117
 
95
- It has built-in deduplication guards: it will *never* double-fire or accidentally consume a second rate-limit window, even if you restart your computer 10 times a day.
118
+ Either way, built-in deduplication guards mean it will *never* double-fire or burn a second rate-limit window even if you restart your computer 10 times a day.
96
119
 
97
- | OS | Background Mechanism |
120
+ | OS | Wake & Background Mechanism |
98
121
  | :--- | :--- |
99
- | 🍎 **macOS** | `launchd` via `RunAtLoad` |
100
- | 🐧 **Linux** | `systemd` timers via `Persistent=true` |
101
- | 🪟 **Windows** | `schtasks` via `StartWhenAvailable` |
122
+ | 🍎 **macOS** | `launchd` (`StartCalendarInterval` + `RunAtLoad`) plus a scheduled wake via `pmset repeat wakeorpoweron` |
123
+ | 🐧 **Linux** | `systemd` timers via `Persistent=true` + `WakeSystem=true` |
124
+ | 🪟 **Windows** | `schtasks` via `StartWhenAvailable` + `WakeToRun` |
102
125
 
103
126
  ---
104
127
 
@@ -12,8 +12,10 @@ interface DefaultCommandDependencies {
12
12
  readConfig: typeof readConfig;
13
13
  runStatus: () => Promise<void>;
14
14
  runFirstRunOnboarding: () => Promise<void>;
15
+ runInteractiveHome: () => Promise<void>;
16
+ isInteractive: () => boolean;
15
17
  }
16
18
  export declare function runFirstRunOnboarding(deps?: Partial<OnboardingDependencies>): Promise<void>;
17
- export declare function defaultCommand(deps?: Partial<DefaultCommandDependencies>): Promise<'status' | 'onboarding'>;
19
+ export declare function defaultCommand(deps?: Partial<DefaultCommandDependencies>): Promise<'status' | 'onboarding' | 'home'>;
18
20
  export {};
19
21
  //# sourceMappingURL=default.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"default.d.ts","sourceRoot":"","sources":["../../src/commands/default.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAOvC,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAGjE,UAAU,sBAAsB;IAC9B,iBAAiB,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1C,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,sBAAsB,EAAE,MAAM,IAAI,CAAC;IACnC,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D;AAED,UAAU,0BAA0B;IAClC,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,qBAAqB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5C;AAaD,wBAAsB,qBAAqB,CACzC,IAAI,GAAE,OAAO,CAAC,sBAAsB,CAAM,GACzC,OAAO,CAAC,IAAI,CAAC,CAkBf;AAED,wBAAsB,cAAc,CAClC,IAAI,GAAE,OAAO,CAAC,0BAA0B,CAAM,GAC7C,OAAO,CAAC,QAAQ,GAAG,YAAY,CAAC,CAYlC"}
1
+ {"version":3,"file":"default.d.ts","sourceRoot":"","sources":["../../src/commands/default.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAOvC,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAajE,UAAU,sBAAsB;IAC9B,iBAAiB,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1C,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,sBAAsB,EAAE,MAAM,IAAI,CAAC;IACnC,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D;AAED,UAAU,0BAA0B;IAClC,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,qBAAqB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,aAAa,EAAE,MAAM,OAAO,CAAC;CAC9B;AAaD,wBAAsB,qBAAqB,CACzC,IAAI,GAAE,OAAO,CAAC,sBAAsB,CAAM,GACzC,OAAO,CAAC,IAAI,CAAC,CAkBf;AAED,wBAAsB,cAAc,CAClC,IAAI,GAAE,OAAO,CAAC,0BAA0B,CAAM,GAC7C,OAAO,CAAC,QAAQ,GAAG,YAAY,GAAG,MAAM,CAAC,CA8B3C"}
@@ -10,6 +10,15 @@ const config_1 = require("../config");
10
10
  const ui_1 = require("../ui");
11
11
  const setup_1 = require("./setup");
12
12
  const status_1 = require("./status");
13
+ const menu_1 = require("./menu");
14
+ /**
15
+ * Bare `warmup` becomes an interactive hub only in a real terminal. Anywhere
16
+ * input/output is not a TTY (pipes, CI, the OS scheduler), it must stay a
17
+ * one-shot status print so it never blocks on a prompt.
18
+ */
19
+ function terminalIsInteractive() {
20
+ return Boolean(process.stdin.isTTY && process.stdout.isTTY);
21
+ }
13
22
  async function promptToStartSetup() {
14
23
  const { startSetup } = await inquirer_1.default.prompt([{
15
24
  type: 'confirm',
@@ -39,11 +48,27 @@ async function defaultCommand(deps = {}) {
39
48
  const loadConfig = deps.readConfig ?? config_1.readConfig;
40
49
  const runStatus = deps.runStatus ?? status_1.statusCommand;
41
50
  const runOnboarding = deps.runFirstRunOnboarding ?? runFirstRunOnboarding;
51
+ const runHome = deps.runInteractiveHome ?? menu_1.runInteractiveHome;
52
+ const isInteractive = deps.isInteractive ?? terminalIsInteractive;
42
53
  if (loadConfig()) {
54
+ // In a terminal, drop into the navigable hub; otherwise stay a scriptable
55
+ // one-shot status print.
56
+ if (isInteractive()) {
57
+ await runHome();
58
+ return 'home';
59
+ }
43
60
  await runStatus();
44
61
  return 'status';
45
62
  }
46
63
  await runOnboarding();
64
+ // If onboarding actually completed setup (config now exists) and we're in a
65
+ // terminal, drop straight into the hub — so a first-time user discovers it
66
+ // immediately instead of dead-ending on a success message. If setup was
67
+ // deferred (still no config), leave them at the deferred notice.
68
+ if (isInteractive() && loadConfig()) {
69
+ await runHome();
70
+ return 'home';
71
+ }
47
72
  return 'onboarding';
48
73
  }
49
74
  //# sourceMappingURL=default.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"default.js","sourceRoot":"","sources":["../../src/commands/default.ts"],"names":[],"mappings":";;;;;AAqCA,sDAoBC;AAED,wCAcC;AAzED,wDAAgC;AAChC,sCAAuC;AACvC,8BAKe;AACf,mCAAiE;AACjE,qCAAyC;AAiBzC,KAAK,UAAU,kBAAkB;IAC/B,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,kBAAQ,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,kBAAkB;YAC3B,OAAO,EAAE,IAAI;SACd,CAAC,CAAC,CAAC;IAEJ,OAAO,UAAU,CAAC;AACpB,CAAC;AAEM,KAAK,UAAU,qBAAqB,CACzC,OAAwC,EAAE;IAE1C,MAAM,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,IAAI,kBAAkB,CAAC;IACvE,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,IAAI,cAAS,CAAC;IAC7C,MAAM,iBAAiB,GAAG,IAAI,CAAC,kBAAkB,IAAI,uBAAkB,CAAC;IACxE,MAAM,qBAAqB,GAAG,IAAI,CAAC,sBAAsB,IAAI,2BAAsB,CAAC;IACpF,MAAM,iBAAiB,GAAG,IAAI,CAAC,kBAAkB,IAAI,uBAAkB,CAAC;IACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,oBAAY,CAAC;IAE/C,QAAQ,EAAE,CAAC;IACX,iBAAiB,EAAE,CAAC;IACpB,qBAAqB,EAAE,CAAC;IAExB,IAAI,CAAC,CAAC,MAAM,iBAAiB,EAAE,CAAC,EAAE,CAAC;QACjC,iBAAiB,EAAE,CAAC;QACpB,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;AACrC,CAAC;AAEM,KAAK,UAAU,cAAc,CAClC,OAA4C,EAAE;IAE9C,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,mBAAU,CAAC;IACjD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,sBAAa,CAAC;IAClD,MAAM,aAAa,GAAG,IAAI,CAAC,qBAAqB,IAAI,qBAAqB,CAAC;IAE1E,IAAI,UAAU,EAAE,EAAE,CAAC;QACjB,MAAM,SAAS,EAAE,CAAC;QAClB,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,aAAa,EAAE,CAAC;IACtB,OAAO,YAAY,CAAC;AACtB,CAAC"}
1
+ {"version":3,"file":"default.js","sourceRoot":"","sources":["../../src/commands/default.ts"],"names":[],"mappings":";;;;;AAiDA,sDAoBC;AAED,wCAgCC;AAvGD,wDAAgC;AAChC,sCAAuC;AACvC,8BAKe;AACf,mCAAiE;AACjE,qCAAyC;AACzC,iCAA4C;AAE5C;;;;GAIG;AACH,SAAS,qBAAqB;IAC5B,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC9D,CAAC;AAmBD,KAAK,UAAU,kBAAkB;IAC/B,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,kBAAQ,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,kBAAkB;YAC3B,OAAO,EAAE,IAAI;SACd,CAAC,CAAC,CAAC;IAEJ,OAAO,UAAU,CAAC;AACpB,CAAC;AAEM,KAAK,UAAU,qBAAqB,CACzC,OAAwC,EAAE;IAE1C,MAAM,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,IAAI,kBAAkB,CAAC;IACvE,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,IAAI,cAAS,CAAC;IAC7C,MAAM,iBAAiB,GAAG,IAAI,CAAC,kBAAkB,IAAI,uBAAkB,CAAC;IACxE,MAAM,qBAAqB,GAAG,IAAI,CAAC,sBAAsB,IAAI,2BAAsB,CAAC;IACpF,MAAM,iBAAiB,GAAG,IAAI,CAAC,kBAAkB,IAAI,uBAAkB,CAAC;IACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,oBAAY,CAAC;IAE/C,QAAQ,EAAE,CAAC;IACX,iBAAiB,EAAE,CAAC;IACpB,qBAAqB,EAAE,CAAC;IAExB,IAAI,CAAC,CAAC,MAAM,iBAAiB,EAAE,CAAC,EAAE,CAAC;QACjC,iBAAiB,EAAE,CAAC;QACpB,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;AACrC,CAAC;AAEM,KAAK,UAAU,cAAc,CAClC,OAA4C,EAAE;IAE9C,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,mBAAU,CAAC;IACjD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,sBAAa,CAAC;IAClD,MAAM,aAAa,GAAG,IAAI,CAAC,qBAAqB,IAAI,qBAAqB,CAAC;IAC1E,MAAM,OAAO,GAAG,IAAI,CAAC,kBAAkB,IAAI,yBAAkB,CAAC;IAC9D,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,qBAAqB,CAAC;IAElE,IAAI,UAAU,EAAE,EAAE,CAAC;QACjB,0EAA0E;QAC1E,yBAAyB;QACzB,IAAI,aAAa,EAAE,EAAE,CAAC;YACpB,MAAM,OAAO,EAAE,CAAC;YAChB,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,MAAM,SAAS,EAAE,CAAC;QAClB,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,aAAa,EAAE,CAAC;IAEtB,4EAA4E;IAC5E,2EAA2E;IAC3E,wEAAwE;IACxE,iEAAiE;IACjE,IAAI,aAAa,EAAE,IAAI,UAAU,EAAE,EAAE,CAAC;QACpC,MAAM,OAAO,EAAE,CAAC;QAChB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC"}
@@ -0,0 +1,51 @@
1
+ import { type WarmupConfig, type LogEntry } from '../config';
2
+ export type DiagnosticStatus = 'ok' | 'warn' | 'fail';
3
+ export interface DiagnosticCheck {
4
+ label: string;
5
+ status: DiagnosticStatus;
6
+ detail: string;
7
+ fix?: string;
8
+ }
9
+ /** Raw, already-resolved inputs for {@link collectDiagnostics}. */
10
+ export interface DiagnosticInput {
11
+ claudeInstalled: boolean;
12
+ claudeAuthenticated: boolean;
13
+ claudeVersion: string | null;
14
+ config: WarmupConfig | null;
15
+ platform: string;
16
+ scheduleInstalled: boolean;
17
+ wakeActive: boolean;
18
+ /** Whether the cached runtime node/claude paths still exist on disk. */
19
+ runtimeValid: boolean;
20
+ lastLog: LogEntry | null;
21
+ }
22
+ /**
23
+ * Turn resolved environment facts into an ordered list of health checks.
24
+ * Pure and synchronous: no I/O, no process spawning — so the exact status/fix
25
+ * routing is unit-testable. `doctorCommand` resolves the real facts and feeds
26
+ * them in. Checks short-circuit sensibly (no auth check if Claude is missing;
27
+ * no schedule checks if warmup isn't set up).
28
+ */
29
+ export declare function collectDiagnostics(input: DiagnosticInput): DiagnosticCheck[];
30
+ export interface DoctorDependencies {
31
+ isClaudeInstalled: (binary?: string) => boolean;
32
+ isClaudeAuthenticated: (binary?: string) => boolean;
33
+ getClaudeVersion: (binary?: string) => string | null;
34
+ readConfig: () => WarmupConfig | null;
35
+ detectPlatform: () => string;
36
+ isScheduleInstalled: () => boolean;
37
+ isWakeScheduleActive: () => boolean;
38
+ getLatestLog: () => LogEntry | null;
39
+ }
40
+ /**
41
+ * `warmup doctor` — a one-shot self-diagnostic (à la `claude doctor`): is Claude
42
+ * Code installed & authed, is the schedule registered/active, is a wake armed,
43
+ * did the last run succeed, are runtime paths cached — each with a concrete fix.
44
+ * Scriptable and non-blocking; safe in non-TTY contexts. `--json` emits the
45
+ * structured checks (no chrome) and sets a health-based exit code (non-zero
46
+ * when any check fails) so it composes into scripts and CI.
47
+ */
48
+ export declare function doctorCommand(options?: {
49
+ json?: boolean;
50
+ }, deps?: Partial<DoctorDependencies>): Promise<void>;
51
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,QAAQ,EACd,MAAM,WAAW,CAAC;AAanB,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,mEAAmE;AACnE,MAAM,WAAW,eAAe;IAC9B,eAAe,EAAE,OAAO,CAAC;IACzB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,wEAAwE;IACxE,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,eAAe,EAAE,CA+G5E;AAED,MAAM,WAAW,kBAAkB;IACjC,iBAAiB,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;IAChD,qBAAqB,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;IACpD,gBAAgB,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACrD,UAAU,EAAE,MAAM,YAAY,GAAG,IAAI,CAAC;IACtC,cAAc,EAAE,MAAM,MAAM,CAAC;IAC7B,mBAAmB,EAAE,MAAM,OAAO,CAAC;IACnC,oBAAoB,EAAE,MAAM,OAAO,CAAC;IACpC,YAAY,EAAE,MAAM,QAAQ,GAAG,IAAI,CAAC;CACrC;AAYD;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,EAChC,IAAI,GAAE,OAAO,CAAC,kBAAkB,CAAM,GACrC,OAAO,CAAC,IAAI,CAAC,CA+Cf"}
@@ -0,0 +1,219 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.collectDiagnostics = collectDiagnostics;
37
+ exports.doctorCommand = doctorCommand;
38
+ const fs = __importStar(require("fs"));
39
+ const config_1 = require("../config");
40
+ const warmup_1 = require("../warmup");
41
+ const scheduler_1 = require("../scheduler");
42
+ const ui_1 = require("../ui");
43
+ /**
44
+ * Turn resolved environment facts into an ordered list of health checks.
45
+ * Pure and synchronous: no I/O, no process spawning — so the exact status/fix
46
+ * routing is unit-testable. `doctorCommand` resolves the real facts and feeds
47
+ * them in. Checks short-circuit sensibly (no auth check if Claude is missing;
48
+ * no schedule checks if warmup isn't set up).
49
+ */
50
+ function collectDiagnostics(input) {
51
+ const checks = [];
52
+ checks.push(input.claudeInstalled
53
+ ? {
54
+ label: 'Claude Code',
55
+ status: 'ok',
56
+ detail: input.claudeVersion ? `installed (${input.claudeVersion})` : 'installed',
57
+ }
58
+ : {
59
+ label: 'Claude Code',
60
+ status: 'fail',
61
+ detail: 'not found in PATH',
62
+ fix: 'Install Claude Code: https://code.claude.com',
63
+ });
64
+ if (input.claudeInstalled) {
65
+ checks.push(input.claudeAuthenticated
66
+ ? { label: 'Authentication', status: 'ok', detail: 'logged in' }
67
+ : {
68
+ label: 'Authentication',
69
+ status: 'fail',
70
+ detail: 'not authenticated',
71
+ fix: 'Run `claude` once and complete login',
72
+ });
73
+ }
74
+ if (!input.config) {
75
+ checks.push({
76
+ label: 'Setup',
77
+ status: 'warn',
78
+ detail: 'not set up yet',
79
+ fix: 'Run `warmup` to start the guided setup',
80
+ });
81
+ return checks;
82
+ }
83
+ checks.push({
84
+ label: 'Setup',
85
+ status: 'ok',
86
+ detail: `pre-warm at ${input.config.schedule.time} (${input.config.schedule.timezone})`,
87
+ });
88
+ checks.push(input.scheduleInstalled
89
+ ? { label: 'Scheduled task', status: 'ok', detail: 'registered with the OS scheduler' }
90
+ : {
91
+ label: 'Scheduled task',
92
+ status: 'fail',
93
+ detail: 'not registered with the OS scheduler',
94
+ fix: 'Run `warmup setup` to (re)install it',
95
+ });
96
+ checks.push(input.config.schedule.enabled
97
+ ? { label: 'Schedule', status: 'ok', detail: 'active' }
98
+ : {
99
+ label: 'Schedule',
100
+ status: 'warn',
101
+ detail: 'paused',
102
+ fix: 'Run `warmup resume` to reactivate',
103
+ });
104
+ if (input.platform === 'macos') {
105
+ checks.push(input.wakeActive
106
+ ? { label: 'Wake', status: 'ok', detail: 'scheduled — runs while asleep' }
107
+ : {
108
+ label: 'Wake',
109
+ status: 'warn',
110
+ detail: "not scheduled — won't fire while the Mac is asleep",
111
+ fix: 'Run `warmup setup` to arm a system wake',
112
+ });
113
+ }
114
+ const last = input.lastLog;
115
+ if (!last) {
116
+ checks.push({ label: 'Last run', status: 'warn', detail: 'no runs recorded yet' });
117
+ }
118
+ else if (last.status === 'success' || last.status === 'boot-recovery') {
119
+ checks.push({ label: 'Last run', status: 'ok', detail: last.status });
120
+ }
121
+ else if (last.status === 'skipped') {
122
+ checks.push({
123
+ label: 'Last run',
124
+ status: 'warn',
125
+ detail: 'last scheduled window was missed (skipped)',
126
+ });
127
+ }
128
+ else {
129
+ checks.push({
130
+ label: 'Last run',
131
+ status: 'fail',
132
+ detail: 'last run failed',
133
+ fix: 'Run `warmup test` to retry and surface the error',
134
+ });
135
+ }
136
+ if (!input.config.runtime) {
137
+ checks.push({
138
+ label: 'Runtime',
139
+ status: 'warn',
140
+ detail: 'runtime paths not cached',
141
+ fix: 'Run `warmup update` to refresh them',
142
+ });
143
+ }
144
+ else if (input.runtimeValid) {
145
+ checks.push({ label: 'Runtime', status: 'ok', detail: 'node + claude paths resolved' });
146
+ }
147
+ else {
148
+ checks.push({
149
+ label: 'Runtime',
150
+ status: 'warn',
151
+ detail: 'cached node/claude path no longer exists',
152
+ fix: 'Run `warmup update` to refresh the paths the scheduler uses',
153
+ });
154
+ }
155
+ return checks;
156
+ }
157
+ /** Whether the cached runtime node + claude paths still exist on disk. */
158
+ function runtimePathsExist(runtime) {
159
+ if (!runtime)
160
+ return false;
161
+ try {
162
+ return fs.existsSync(runtime.nodePath) && fs.existsSync(runtime.claudePath);
163
+ }
164
+ catch {
165
+ return false;
166
+ }
167
+ }
168
+ /**
169
+ * `warmup doctor` — a one-shot self-diagnostic (à la `claude doctor`): is Claude
170
+ * Code installed & authed, is the schedule registered/active, is a wake armed,
171
+ * did the last run succeed, are runtime paths cached — each with a concrete fix.
172
+ * Scriptable and non-blocking; safe in non-TTY contexts. `--json` emits the
173
+ * structured checks (no chrome) and sets a health-based exit code (non-zero
174
+ * when any check fails) so it composes into scripts and CI.
175
+ */
176
+ async function doctorCommand(options = {}, deps = {}) {
177
+ const claudeInstalledFn = deps.isClaudeInstalled ?? warmup_1.isClaudeInstalled;
178
+ const claudeAuthedFn = deps.isClaudeAuthenticated ?? warmup_1.isClaudeAuthenticated;
179
+ const claudeVersionFn = deps.getClaudeVersion ?? warmup_1.getClaudeVersion;
180
+ const loadConfig = deps.readConfig ?? config_1.readConfig;
181
+ const platformFn = deps.detectPlatform ?? scheduler_1.detectPlatform;
182
+ const scheduleInstalledFn = deps.isScheduleInstalled ?? scheduler_1.isScheduleInstalled;
183
+ const wakeActiveFn = deps.isWakeScheduleActive ?? scheduler_1.isWakeScheduleActive;
184
+ const latestLogFn = deps.getLatestLog ?? config_1.getLatestLog;
185
+ const config = loadConfig();
186
+ const platform = platformFn();
187
+ // Probe the SAME binary the scheduler runs, not bare `claude`.
188
+ const claudeBinary = config?.runtime?.claudePath || 'claude';
189
+ const claudeInstalled = claudeInstalledFn(claudeBinary);
190
+ const input = {
191
+ claudeInstalled,
192
+ claudeAuthenticated: claudeInstalled ? claudeAuthedFn(claudeBinary) : false,
193
+ claudeVersion: claudeInstalled ? claudeVersionFn(claudeBinary) : null,
194
+ config,
195
+ platform,
196
+ scheduleInstalled: config ? scheduleInstalledFn() : false,
197
+ wakeActive: config && platform === 'macos' ? Boolean(config.wake) && wakeActiveFn() : false,
198
+ runtimeValid: config?.runtime ? runtimePathsExist(config.runtime) : false,
199
+ lastLog: config ? latestLogFn() : null,
200
+ };
201
+ const checks = collectDiagnostics(input);
202
+ if (options.json) {
203
+ const failures = checks.filter(c => c.status === 'fail').length;
204
+ const warnings = checks.filter(c => c.status === 'warn').length;
205
+ process.stdout.write(`${JSON.stringify({
206
+ healthy: failures === 0 && warnings === 0,
207
+ failures,
208
+ warnings,
209
+ checks,
210
+ }, null, 2)}\n`);
211
+ // Non-zero only on hard failures, so `warmup doctor --json` is usable as a
212
+ // health gate; warnings (e.g. paused, no wake) are advisory and exit 0.
213
+ process.exitCode = failures > 0 ? 1 : 0;
214
+ return;
215
+ }
216
+ (0, ui_1.printMiniLogo)();
217
+ (0, ui_1.printDiagnostics)(checks);
218
+ }
219
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDA,gDA+GC;AA+BD,sCAkDC;AAjPD,uCAAyB;AACzB,sCAKmB;AACnB,sCAImB;AACnB,4CAIsB;AACtB,8BAAwD;AAyBxD;;;;;;GAMG;AACH,SAAgB,kBAAkB,CAAC,KAAsB;IACvD,MAAM,MAAM,GAAsB,EAAE,CAAC;IAErC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe;QAC/B,CAAC,CAAC;YACE,KAAK,EAAE,aAAa;YACpB,MAAM,EAAE,IAAI;YACZ,MAAM,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,cAAc,KAAK,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC,WAAW;SACjF;QACH,CAAC,CAAC;YACE,KAAK,EAAE,aAAa;YACpB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,mBAAmB;YAC3B,GAAG,EAAE,8CAA8C;SACpD,CAAC,CAAC;IAEP,IAAI,KAAK,CAAC,eAAe,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,mBAAmB;YACnC,CAAC,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE;YAChE,CAAC,CAAC;gBACE,KAAK,EAAE,gBAAgB;gBACvB,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,mBAAmB;gBAC3B,GAAG,EAAE,sCAAsC;aAC5C,CAAC,CAAC;IACT,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QAClB,MAAM,CAAC,IAAI,CAAC;YACV,KAAK,EAAE,OAAO;YACd,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,gBAAgB;YACxB,GAAG,EAAE,wCAAwC;SAC9C,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,CAAC,IAAI,CAAC;QACV,KAAK,EAAE,OAAO;QACd,MAAM,EAAE,IAAI;QACZ,MAAM,EAAE,eAAe,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,GAAG;KACxF,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB;QACjC,CAAC,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,kCAAkC,EAAE;QACvF,CAAC,CAAC;YACE,KAAK,EAAE,gBAAgB;YACvB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,sCAAsC;YAC9C,GAAG,EAAE,sCAAsC;SAC5C,CAAC,CAAC;IAEP,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO;QACvC,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE;QACvD,CAAC,CAAC;YACE,KAAK,EAAE,UAAU;YACjB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,QAAQ;YAChB,GAAG,EAAE,mCAAmC;SACzC,CAAC,CAAC;IAEP,IAAI,KAAK,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU;YAC1B,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,+BAA+B,EAAE;YAC1E,CAAC,CAAC;gBACE,KAAK,EAAE,MAAM;gBACb,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,oDAAoD;gBAC5D,GAAG,EAAE,yCAAyC;aAC/C,CAAC,CAAC;IACT,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC;IAC3B,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC,CAAC;IACrF,CAAC;SAAM,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,KAAK,eAAe,EAAE,CAAC;QACxE,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IACxE,CAAC;SAAM,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC;YACV,KAAK,EAAE,UAAU;YACjB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,4CAA4C;SACrD,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC;YACV,KAAK,EAAE,UAAU;YACjB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,iBAAiB;YACzB,GAAG,EAAE,kDAAkD;SACxD,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC;YACV,KAAK,EAAE,SAAS;YAChB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,0BAA0B;YAClC,GAAG,EAAE,qCAAqC;SAC3C,CAAC,CAAC;IACL,CAAC;SAAM,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,8BAA8B,EAAE,CAAC,CAAC;IAC1F,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC;YACV,KAAK,EAAE,SAAS;YAChB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,0CAA0C;YAClD,GAAG,EAAE,6DAA6D;SACnE,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAaD,0EAA0E;AAC1E,SAAS,iBAAiB,CAAC,OAAgC;IACzD,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAC3B,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC9E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,UAAU,aAAa,CACjC,UAA8B,EAAE,EAChC,OAAoC,EAAE;IAEtC,MAAM,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,IAAI,0BAAiB,CAAC;IACtE,MAAM,cAAc,GAAG,IAAI,CAAC,qBAAqB,IAAI,8BAAqB,CAAC;IAC3E,MAAM,eAAe,GAAG,IAAI,CAAC,gBAAgB,IAAI,yBAAgB,CAAC;IAClE,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,mBAAU,CAAC;IACjD,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,IAAI,0BAAc,CAAC;IACzD,MAAM,mBAAmB,GAAG,IAAI,CAAC,mBAAmB,IAAI,+BAAmB,CAAC;IAC5E,MAAM,YAAY,GAAG,IAAI,CAAC,oBAAoB,IAAI,gCAAoB,CAAC;IACvE,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,IAAI,qBAAY,CAAC;IAEtD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAC;IAC9B,+DAA+D;IAC/D,MAAM,YAAY,GAAG,MAAM,EAAE,OAAO,EAAE,UAAU,IAAI,QAAQ,CAAC;IAC7D,MAAM,eAAe,GAAG,iBAAiB,CAAC,YAAY,CAAC,CAAC;IAExD,MAAM,KAAK,GAAoB;QAC7B,eAAe;QACf,mBAAmB,EAAE,eAAe,CAAC,CAAC,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,KAAK;QAC3E,aAAa,EAAE,eAAe,CAAC,CAAC,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI;QACrE,MAAM;QACN,QAAQ;QACR,iBAAiB,EAAE,MAAM,CAAC,CAAC,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAC,KAAK;QACzD,UAAU,EAAE,MAAM,IAAI,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,YAAY,EAAE,CAAC,CAAC,CAAC,KAAK;QAC3F,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK;QACzE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI;KACvC,CAAC;IAEF,MAAM,MAAM,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAEzC,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;QAChE,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;QAChE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC;YACrC,OAAO,EAAE,QAAQ,KAAK,CAAC,IAAI,QAAQ,KAAK,CAAC;YACzC,QAAQ;YACR,QAAQ;YACR,MAAM;SACP,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACjB,2EAA2E;QAC3E,wEAAwE;QACxE,OAAO,CAAC,QAAQ,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxC,OAAO;IACT,CAAC;IAED,IAAA,kBAAa,GAAE,CAAC;IAChB,IAAA,qBAAgB,EAAC,MAAM,CAAC,CAAC;AAC3B,CAAC"}
@@ -1,6 +1,29 @@
1
+ import { readConfig, writeLog } from '../config';
2
+ import { executeWarmup, shouldBootRecover, shouldLogSkip, shouldRunNearSchedule } from '../warmup';
3
+ import { notify } from '../notify';
4
+ import { animateBootRecovery, animateBootRecoverySuccess } from '../ui';
5
+ /**
6
+ * Injectable seams for {@link executeCommand}. Production wires the real
7
+ * implementations; tests substitute the clock, the pre-warm, the notifier, and
8
+ * the logger so the branch routing can be exercised deterministically without
9
+ * spawning Claude or firing OS notifications. Mirrors the dependency-object
10
+ * pattern used by `defaultCommand` / `runFirstRunOnboarding`.
11
+ */
12
+ export interface ExecuteCommandDependencies {
13
+ now: Date;
14
+ readConfig: typeof readConfig;
15
+ shouldBootRecover: typeof shouldBootRecover;
16
+ shouldRunNearSchedule: typeof shouldRunNearSchedule;
17
+ shouldLogSkip: typeof shouldLogSkip;
18
+ executeWarmup: typeof executeWarmup;
19
+ notify: typeof notify;
20
+ writeLog: typeof writeLog;
21
+ animateBootRecovery: typeof animateBootRecovery;
22
+ animateBootRecoverySuccess: typeof animateBootRecoverySuccess;
23
+ }
1
24
  /**
2
25
  * Internal command called by the OS scheduler (launchd/systemd/schtasks).
3
26
  * Not user-facing. Determines whether this is a normal run or boot-recovery.
4
27
  */
5
- export declare function executeCommand(): Promise<void>;
28
+ export declare function executeCommand(deps?: Partial<ExecuteCommandDependencies>): Promise<void>;
6
29
  //# sourceMappingURL=execute.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../src/commands/execute.ts"],"names":[],"mappings":"AAOA;;;GAGG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CA4CpD"}
1
+ {"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../src/commands/execute.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EAEV,QAAQ,EAET,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACnG,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC3B,MAAM,OAAO,CAAC;AAWf;;;;;;GAMG;AACH,MAAM,WAAW,0BAA0B;IACzC,GAAG,EAAE,IAAI,CAAC;IACV,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,qBAAqB,EAAE,OAAO,qBAAqB,CAAC;IACpD,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,MAAM,EAAE,OAAO,MAAM,CAAC;IACtB,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAC1B,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,0BAA0B,EAAE,OAAO,0BAA0B,CAAC;CAC/D;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,IAAI,GAAE,OAAO,CAAC,0BAA0B,CAAM,GAC7C,OAAO,CAAC,IAAI,CAAC,CAmEf"}
@@ -3,34 +3,50 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.executeCommand = executeCommand;
4
4
  const config_1 = require("../config");
5
5
  const warmup_1 = require("../warmup");
6
+ const notify_1 = require("../notify");
6
7
  const ui_1 = require("../ui");
8
+ function formatNow(timezone, now) {
9
+ return now.toLocaleTimeString('en-US', {
10
+ timeZone: timezone,
11
+ hour: 'numeric',
12
+ minute: '2-digit',
13
+ hour12: true,
14
+ });
15
+ }
7
16
  /**
8
17
  * Internal command called by the OS scheduler (launchd/systemd/schtasks).
9
18
  * Not user-facing. Determines whether this is a normal run or boot-recovery.
10
19
  */
11
- async function executeCommand() {
12
- const config = (0, config_1.readConfig)();
20
+ async function executeCommand(deps = {}) {
21
+ const now = deps.now ?? new Date();
22
+ const loadConfig = deps.readConfig ?? config_1.readConfig;
23
+ const checkBootRecover = deps.shouldBootRecover ?? warmup_1.shouldBootRecover;
24
+ const checkRunNearSchedule = deps.shouldRunNearSchedule ?? warmup_1.shouldRunNearSchedule;
25
+ const checkLogSkip = deps.shouldLogSkip ?? warmup_1.shouldLogSkip;
26
+ const runWarmup = deps.executeWarmup ?? warmup_1.executeWarmup;
27
+ const sendNotification = deps.notify ?? notify_1.notify;
28
+ const recordLog = deps.writeLog ?? config_1.writeLog;
29
+ const showBootRecovery = deps.animateBootRecovery ?? ui_1.animateBootRecovery;
30
+ const showBootRecoverySuccess = deps.animateBootRecoverySuccess ?? ui_1.animateBootRecoverySuccess;
31
+ const config = loadConfig();
13
32
  if (!config || !config.schedule.enabled) {
14
33
  process.exit(0);
15
34
  return;
16
35
  }
17
- const isBootRecovery = (0, warmup_1.shouldBootRecover)(config);
36
+ const [h, m] = config.schedule.time.split(':').map(Number);
37
+ const scheduledTime = (0, config_1.formatHour)(h, m);
38
+ const isBootRecovery = checkBootRecover(config, now);
18
39
  if (isBootRecovery) {
19
- const [h, m] = config.schedule.time.split(':').map(Number);
20
- const scheduledTime = (0, config_1.formatHour)(h, m);
21
- const now = new Date();
22
- const actualTime = now.toLocaleTimeString('en-US', {
23
- timeZone: config.schedule.timezone,
24
- hour: 'numeric',
25
- minute: '2-digit',
26
- hour12: true,
27
- });
40
+ const actualTime = formatNow(config.schedule.timezone, now);
28
41
  // These animations only show if running in an interactive terminal
29
42
  if (process.stdout.isTTY) {
30
- await (0, ui_1.animateBootRecovery)(scheduledTime, actualTime);
43
+ await showBootRecovery(scheduledTime, actualTime);
44
+ }
45
+ const result = runWarmup(true);
46
+ if (!result.success) {
47
+ sendNotification('warmup pre-warm failed', result.message);
31
48
  }
32
- const result = (0, warmup_1.executeWarmup)(true);
33
- if (result.success && process.stdout.isTTY) {
49
+ else if (process.stdout.isTTY) {
34
50
  // Boot-recovery reset = NOW + 5 hours, not scheduled time + 5
35
51
  const resetTime = new Date(now.getTime() + 5 * 60 * 60 * 1000)
36
52
  .toLocaleTimeString('en-US', {
@@ -39,14 +55,30 @@ async function executeCommand() {
39
55
  minute: '2-digit',
40
56
  hour12: true,
41
57
  });
42
- await (0, ui_1.animateBootRecoverySuccess)(resetTime);
58
+ await showBootRecoverySuccess(resetTime);
59
+ }
60
+ }
61
+ else if (checkRunNearSchedule(config, now)) {
62
+ // Normal scheduled execution (within tolerance, not a recent duplicate).
63
+ const result = runWarmup(false);
64
+ if (!result.success) {
65
+ sendNotification('warmup pre-warm failed', result.message);
43
66
  }
44
67
  }
45
- else if ((0, config_1.isNearScheduledTime)(config.schedule.time, config.schedule.timezone)) {
46
- // Normal scheduled execution only fire if we're near the scheduled time
47
- (0, warmup_1.executeWarmup)(false);
68
+ else if (checkLogSkip(config, now)) {
69
+ // The window was missed and it is too late to recover (e.g. the Mac was
70
+ // opened hours after the scheduled time). Record it once so the miss is
71
+ // visible in `warmup status` instead of vanishing silently.
72
+ const entry = {
73
+ timestamp: now.toISOString(),
74
+ status: 'skipped',
75
+ message: `Pre-warm window missed — opened too late to pre-warm (scheduled ${scheduledTime})`,
76
+ scheduledTime: config.schedule.time,
77
+ actualTime: formatNow(config.schedule.timezone, now),
78
+ };
79
+ recordLog(entry, config.schedule.timezone);
80
+ sendNotification('warmup missed today’s window', `Opened too late to pre-warm (scheduled ${scheduledTime}).`);
48
81
  }
49
- // If neither boot-recovery nor near scheduled time, do nothing.
50
- // This prevents RunAtLoad / reboot from firing pointless pre-warms.
82
+ // Otherwise (e.g. a login well before the scheduled time) do nothing.
51
83
  }
52
84
  //# sourceMappingURL=execute.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"execute.js","sourceRoot":"","sources":["../../src/commands/execute.ts"],"names":[],"mappings":";;AAWA,wCA4CC;AAvDD,sCAAwE;AACxE,sCAA6D;AAC7D,8BAGe;AAEf;;;GAGG;AACI,KAAK,UAAU,cAAc;IAClC,MAAM,MAAM,GAAG,IAAA,mBAAU,GAAE,CAAC;IAC5B,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;QACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAChB,OAAO;IACT,CAAC;IAED,MAAM,cAAc,GAAG,IAAA,0BAAiB,EAAC,MAAM,CAAC,CAAC;IAEjD,IAAI,cAAc,EAAE,CAAC;QACnB,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC3D,MAAM,aAAa,GAAG,IAAA,mBAAU,EAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACvC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,UAAU,GAAG,GAAG,CAAC,kBAAkB,CAAC,OAAO,EAAE;YACjD,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ;YAClC,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;QAEH,mEAAmE;QACnE,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACzB,MAAM,IAAA,wBAAmB,EAAC,aAAa,EAAE,UAAU,CAAC,CAAC;QACvD,CAAC;QAED,MAAM,MAAM,GAAG,IAAA,sBAAa,EAAC,IAAI,CAAC,CAAC;QAEnC,IAAI,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAC3C,8DAA8D;YAC9D,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;iBAC3D,kBAAkB,CAAC,OAAO,EAAE;gBAC3B,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ;gBAClC,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,SAAS;gBACjB,MAAM,EAAE,IAAI;aACb,CAAC,CAAC;YACL,MAAM,IAAA,+BAA0B,EAAC,SAAS,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;SAAM,IAAI,IAAA,4BAAmB,EAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/E,0EAA0E;QAC1E,IAAA,sBAAa,EAAC,KAAK,CAAC,CAAC;IACvB,CAAC;IACD,gEAAgE;IAChE,oEAAoE;AACtE,CAAC"}
1
+ {"version":3,"file":"execute.js","sourceRoot":"","sources":["../../src/commands/execute.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export interface LogsCommandOptions {
2
+ limit?: number;
3
+ json?: boolean;
4
+ }
5
+ /**
6
+ * `warmup logs` — show recent pre-warm run history (newest first). Also reused
7
+ * by the interactive hub's "View recent runs". Scriptable: `--json` emits the
8
+ * raw entries (no chrome) for piping; otherwise a pretty list. In a non-TTY
9
+ * context it just prints and exits.
10
+ */
11
+ export declare function logsCommand(options?: LogsCommandOptions): Promise<void>;
12
+ //# sourceMappingURL=logs.d.ts.map