tibodog 0.1.0 β†’ 0.1.2

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.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # πŸ• tibodog
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/tibodog)](https://www.npmjs.com/package/tibodog)
4
+
3
5
  > 티보가 λ¦¬μ…‹ν•˜λ©΄ μ§–λŠ” 개.
4
6
  > A dog that barks when Tibo resets the limits.
5
7
 
@@ -33,9 +35,6 @@ run; on Linux and Windows tibodog still watches and barks in the terminal.
33
35
  npx tibodog --test # μœ„μ ― λ„μš°κ³  λ°”λ‘œ ν•œλ²ˆ 짖어보기 / bring up the widget and bark once
34
36
  ```
35
37
 
36
- > 아직 npm에 올리기 μ „μ΄μ—μš”. κ·Έλ™μ•ˆμ€ `npx tibodog` λŒ€μ‹  `npx github:matthewkim-11/tibodog`을 μ“°μ„Έμš”.
37
- > Not on npm yet: use `npx github:matthewkim-11/tibodog` in place of `npx tibodog` for now.
38
-
39
38
  The first run builds the widget into `~/.tibodog` (under a minute). After that it starts instantly and keeps
40
39
  running even when you close the terminal. Then pick a way to read X:
41
40
 
@@ -70,9 +70,11 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
70
70
  private var panel: NotchPanel?
71
71
  private var geometry: NotchGeometry?
72
72
  private var observers: [Any] = []
73
+ private var signalSources: [DispatchSourceSignal] = []
73
74
 
74
75
  func applicationDidFinishLaunching(_ notification: Notification) {
75
76
  NSApp.setActivationPolicy(.accessory)
77
+ quitOnSignals()
76
78
  let store = UsageStore.shared
77
79
  store.start()
78
80
  rebuildPanel()
@@ -139,6 +141,17 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
139
141
  return out
140
142
  }
141
143
 
144
+ /// `pkill`, Ctrl-C or a logout quit like the menu's Quit, so the Now Playing helper stops with us.
145
+ private func quitOnSignals() {
146
+ for sig in [SIGTERM, SIGINT, SIGHUP] {
147
+ signal(sig, SIG_IGN)
148
+ let source = DispatchSource.makeSignalSource(signal: sig, queue: .main)
149
+ source.setEventHandler { NSApp.terminate(nil) }
150
+ source.resume()
151
+ signalSources.append(source)
152
+ }
153
+ }
154
+
142
155
  func applicationWillTerminate(_ notification: Notification) {
143
156
  NowPlayingMonitor.shared.stop()
144
157
  }
@@ -77,6 +77,21 @@ final class NowPlayingMonitor: ObservableObject {
77
77
  return nil
78
78
  }
79
79
 
80
+ /// Runs the adapter (same pid, via exec) with a tiny watcher beside it that ends the adapter once
81
+ /// TiboDog is gone, so a crash or `kill -9` never leaves a perl process streaming to nobody.
82
+ static let watchdog = #"""
83
+ my $app = getppid(); my $adapter = $$;
84
+ if (fork() == 0) {
85
+ open STDOUT, '>', '/dev/null'; open STDIN, '<', '/dev/null';
86
+ while (1) {
87
+ sleep 1;
88
+ exit 0 unless kill 0, $adapter;
89
+ if (!kill 0, $app) { kill 'TERM', $adapter; exit 0 }
90
+ }
91
+ }
92
+ exec '/usr/bin/perl', @ARGV or die "exec: $!";
93
+ """#
94
+
80
95
  // MARK: Lifecycle
81
96
 
82
97
  func start() {
@@ -100,7 +115,7 @@ final class NowPlayingMonitor: ObservableObject {
100
115
  }
101
116
  let proc = Process()
102
117
  proc.executableURL = URL(fileURLWithPath: "/usr/bin/perl")
103
- proc.arguments = [paths.script.path, paths.framework.path, "stream", "--debounce=150"]
118
+ proc.arguments = ["-e", Self.watchdog, paths.script.path, paths.framework.path, "stream", "--debounce=150"]
104
119
  let p = Pipe()
105
120
  proc.standardOutput = p
106
121
  proc.standardError = FileHandle.nullDevice
@@ -77,6 +77,16 @@ final class ResetTracker: ObservableObject {
77
77
  guard !redeeming else { return }
78
78
  redeeming = true
79
79
  lastOutcome = nil
80
+ if UsageStore.mock {
81
+ // Demos and screenshots: look like Codex said "reset", never spend a real credit.
82
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.9) {
83
+ self.redeeming = false
84
+ self.lastOutcome = "reset"
85
+ self.credits = max(0, (self.credits ?? 1) - 1)
86
+ UsageStore.shared.mockReset()
87
+ }
88
+ return
89
+ }
80
90
  let key = UUID().uuidString
81
91
  Task.detached(priority: .userInitiated) {
82
92
  var outcome = (try? CodexUsageAPI.consumeResetCredit(idempotencyKey: key)) ?? "error"
@@ -1,4 +1,5 @@
1
1
  import Foundation
2
+ import SwiftUI
2
3
  import Combine
3
4
  import ServiceManagement
4
5
 
@@ -180,6 +181,18 @@ final class UsageStore: ObservableObject {
180
181
  return fresh
181
182
  }
182
183
 
184
+ /// TIBODOG_MOCK=1: what spending a reset credit looks like, for demos (both windows start over).
185
+ func mockReset() {
186
+ guard Self.mock || Self.mockUsageOnly else { return }
187
+ withAnimation(.easeOut(duration: 0.6)) {
188
+ usage?.fiveHour?.utilization = 0
189
+ usage?.fiveHour?.resetsAt = Date().addingTimeInterval(5 * 3600)
190
+ usage?.sevenDay?.utilization = 0
191
+ usage?.sevenDay?.resetsAt = Date().addingTimeInterval(7 * 86400)
192
+ }
193
+ lastUpdated = Date()
194
+ }
195
+
183
196
  /// Short text for the menu bar item, e.g. "42% Β· 18%".
184
197
  var menuBarTitle: String {
185
198
  guard let u = usage else { return errorMessage == nil ? "…" : "!" }
package/package.json CHANGED
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "tibodog",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "A notch widget (Claude Code/Codex usage, Now Playing) with a dog that barks when Tibo (@thsottiaux) resets the Codex limits on X",
5
5
  "type": "module",
6
- "bin": { "tibodog": "bin/tibodog.js" },
6
+ "bin": {
7
+ "tibodog": "bin/tibodog.js"
8
+ },
7
9
  "files": [
8
10
  "bin",
9
11
  "src",
@@ -22,9 +24,25 @@
22
24
  "start": "node bin/tibodog.js",
23
25
  "test": "node --test test/*.test.js"
24
26
  },
25
- "engines": { "node": ">=18" },
26
- "keywords": ["codex", "claude-code", "usage", "notch", "macos", "rate-limit", "reset", "dog", "x", "twitter"],
27
- "repository": { "type": "git", "url": "git+https://github.com/matthewkim-11/tibodog.git" },
27
+ "engines": {
28
+ "node": ">=18"
29
+ },
30
+ "keywords": [
31
+ "codex",
32
+ "claude-code",
33
+ "usage",
34
+ "notch",
35
+ "macos",
36
+ "rate-limit",
37
+ "reset",
38
+ "dog",
39
+ "x",
40
+ "twitter"
41
+ ],
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "git+https://github.com/matthewkim-11/tibodog.git"
45
+ },
28
46
  "homepage": "https://github.com/matthewkim-11/tibodog",
29
47
  "license": "MIT"
30
48
  }