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
|
+
[](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.
|
|
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": {
|
|
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": {
|
|
26
|
-
|
|
27
|
-
|
|
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
|
}
|