modelstat 0.1.3 → 0.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.
- package/README.md +10 -10
- package/dist/cli.mjs +231 -124
- package/dist/cli.mjs.map +1 -1
- package/package.json +6 -6
- package/scripts/postinstall.mjs +7 -7
- package/vendor/ModelstatTray.app/Contents/MacOS/modelstat-tray +0 -0
- package/vendor/tray-mac/Package.swift +1 -1
- package/vendor/tray-mac/Sources/ModelstatTray/main.swift +8 -8
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "modelstat",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "modelstat
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "modelstat daemon — reads local AI-tool usage and ships tokenised events to modelstat.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/cli.mjs",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"tsup": "^8.3.5",
|
|
30
30
|
"tsx": "^4.19.2",
|
|
31
31
|
"typescript": "^5.7.3",
|
|
32
|
+
"@modelstat/daemon-core": "0.0.0",
|
|
32
33
|
"@modelstat/core": "0.0.0",
|
|
33
|
-
"@modelstat/parsers": "0.0.0"
|
|
34
|
-
"@modelstat/companion-core": "0.0.0"
|
|
34
|
+
"@modelstat/parsers": "0.0.0"
|
|
35
35
|
},
|
|
36
36
|
"engines": {
|
|
37
37
|
"node": ">=20.18.0"
|
|
@@ -52,13 +52,13 @@
|
|
|
52
52
|
"cursor",
|
|
53
53
|
"tokens",
|
|
54
54
|
"analytics",
|
|
55
|
-
"
|
|
55
|
+
"daemon",
|
|
56
56
|
"cli"
|
|
57
57
|
],
|
|
58
58
|
"repository": {
|
|
59
59
|
"type": "git",
|
|
60
60
|
"url": "git+https://github.com/modelstat/modelstat.git",
|
|
61
|
-
"directory": "apps/
|
|
61
|
+
"directory": "apps/daemon"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
64
|
"dev:connect": "tsx ./src/cli.ts connect",
|
package/scripts/postinstall.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Post-install hook for the @modelstat/
|
|
3
|
+
* Post-install hook for the @modelstat/daemon CLI.
|
|
4
4
|
*
|
|
5
5
|
* Runs after `npm install -g modelstat` (or `npx modelstat` cache
|
|
6
6
|
* population) and downloads the bundled summariser GGUF (~2.7 GB)
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* install that doesn't want long-running side-effects).
|
|
21
21
|
*
|
|
22
22
|
* In all skip cases the model still downloads lazily on first
|
|
23
|
-
* `modelstat scan` — the
|
|
23
|
+
* `modelstat scan` — the daemon always preflights the summariser
|
|
24
24
|
* before producing any segments (see src/pipeline.ts), so users in
|
|
25
25
|
* skip-mode just see the download then, instead of now.
|
|
26
26
|
*/
|
|
@@ -69,7 +69,7 @@ async function main() {
|
|
|
69
69
|
|
|
70
70
|
// ── TTY-only path: pre-download the heavy summariser model ───────
|
|
71
71
|
// The 2.7 GB GGUF is the only thing we want to skip in non-TTY
|
|
72
|
-
// installs (CI, npm cache, packagers). The
|
|
72
|
+
// installs (CI, npm cache, packagers). The daemon always preflights
|
|
73
73
|
// the summariser before producing segments (see src/pipeline.ts),
|
|
74
74
|
// so users in skip-mode just see the download then, instead of now.
|
|
75
75
|
const skipModelDownload =
|
|
@@ -84,14 +84,14 @@ async function main() {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
// Resolve the helper from the workspace package the bundle uses.
|
|
87
|
-
//
|
|
87
|
+
// daemon-core is bundled into dist/cli.mjs by tsup, but we need
|
|
88
88
|
// the helper as a standalone import here (postinstall runs against
|
|
89
89
|
// the unbundled source layout).
|
|
90
90
|
let ensureLlamaModel;
|
|
91
91
|
let defaultLlamaConfig;
|
|
92
92
|
try {
|
|
93
93
|
({ ensureLlamaModel, defaultLlamaConfig } = await import(
|
|
94
|
-
"@modelstat/
|
|
94
|
+
"@modelstat/daemon-core/node"
|
|
95
95
|
));
|
|
96
96
|
} catch (err) {
|
|
97
97
|
console.warn(
|
|
@@ -126,7 +126,7 @@ async function main() {
|
|
|
126
126
|
);
|
|
127
127
|
}
|
|
128
128
|
console.log(
|
|
129
|
-
"[modelstat] all set — your dashboard already has the new
|
|
129
|
+
"[modelstat] all set — your dashboard already has the new daemon running",
|
|
130
130
|
);
|
|
131
131
|
}
|
|
132
132
|
|
|
@@ -136,7 +136,7 @@ async function rebootServiceIfInstalled() {
|
|
|
136
136
|
homedir(),
|
|
137
137
|
"Library",
|
|
138
138
|
"LaunchAgents",
|
|
139
|
-
"ai.modelstat.
|
|
139
|
+
"ai.modelstat.daemon.plist",
|
|
140
140
|
);
|
|
141
141
|
const systemdUnit = join(
|
|
142
142
|
process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"),
|
|
Binary file
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// swift build -c release (executable at .build/release/modelstat-tray)
|
|
12
12
|
// ./build-app.sh (wraps the binary in ModelstatTray.app)
|
|
13
13
|
//
|
|
14
|
-
// Wired into the macOS install path in apps/
|
|
14
|
+
// Wired into the macOS install path in apps/daemon/src/service.ts —
|
|
15
15
|
// the launchd plist launches THIS instead of the headless daemon; the
|
|
16
16
|
// tray then spawns `modelstat start` as a child so there's still only
|
|
17
17
|
// one process managing the pipeline.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// ModelstatTray — macOS menu-bar
|
|
1
|
+
// ModelstatTray — macOS menu-bar daemon for the modelstat agent.
|
|
2
2
|
//
|
|
3
3
|
// What it does:
|
|
4
4
|
// · puts a "◉" status item in the menu bar
|
|
@@ -66,7 +66,7 @@ struct AgentStats: Decodable {
|
|
|
66
66
|
struct DeviceInfo: Decodable {
|
|
67
67
|
let hostname: String?
|
|
68
68
|
let os_family: String?
|
|
69
|
-
let
|
|
69
|
+
let daemon_status: String?
|
|
70
70
|
let last_seen_at: String?
|
|
71
71
|
}
|
|
72
72
|
|
|
@@ -85,7 +85,7 @@ struct LocalStatus: Decodable {
|
|
|
85
85
|
let message: String?
|
|
86
86
|
let queue_size: Int?
|
|
87
87
|
let last_event_at: String?
|
|
88
|
-
let
|
|
88
|
+
let daemon_version: String?
|
|
89
89
|
let stats: LocalStatsCounters?
|
|
90
90
|
}
|
|
91
91
|
|
|
@@ -246,13 +246,13 @@ final class TrayController: NSObject {
|
|
|
246
246
|
//
|
|
247
247
|
// The tray no longer spawns `start --force` blindly. Blind --force
|
|
248
248
|
// SIGTERMs whatever live daemon owns the singleton lock (see
|
|
249
|
-
// apps/
|
|
249
|
+
// apps/daemon/src/lock.ts), so two briefly-coexisting trays
|
|
250
250
|
// (kickstart -k racing a reinstall, KeepAlive respawn overlap) had
|
|
251
251
|
// their daemons kill each other in a loop — observed 2026-06-12
|
|
252
252
|
// ending with zero daemons and nothing restarting them. Instead,
|
|
253
253
|
// every (re)start funnels through ensureDaemon(), which asks the CLI
|
|
254
254
|
// `_daemon-health` (decision logic + tests live in
|
|
255
|
-
// apps/
|
|
255
|
+
// apps/daemon/src/supervise.ts):
|
|
256
256
|
// adopt → a live, heartbeating daemon owns the lock — leave it.
|
|
257
257
|
// spawn → no live owner — plain `start` (a dead owner's stale
|
|
258
258
|
// lock is reclaimed without --force by lock.ts).
|
|
@@ -482,10 +482,10 @@ final class TrayController: NSObject {
|
|
|
482
482
|
}
|
|
483
483
|
|
|
484
484
|
// Live agent phase comes from the local heartbeat mirror.
|
|
485
|
-
// Falls back to the device-view's reported
|
|
485
|
+
// Falls back to the device-view's reported daemon_status. If
|
|
486
486
|
// both are missing we say "running" rather than "starting" so
|
|
487
487
|
// the menu doesn't lie about the daemon's state.
|
|
488
|
-
let phase = local?.status ?? s.device?.
|
|
488
|
+
let phase = local?.status ?? s.device?.daemon_status ?? "running"
|
|
489
489
|
let phaseMsg = local?.message
|
|
490
490
|
// Pulse the leading dot while the agent is actively working so the
|
|
491
491
|
// menu reads as alive even on the rare beat where the numbers don't
|
|
@@ -503,7 +503,7 @@ final class TrayController: NSObject {
|
|
|
503
503
|
// numbers, plus point the menu items at the dashboard. Keep the
|
|
504
504
|
// reassuring "Claimed ✓" and append the agent version when the
|
|
505
505
|
// local snapshot carries it.
|
|
506
|
-
if let v = local?.
|
|
506
|
+
if let v = local?.daemon_version, !v.isEmpty {
|
|
507
507
|
setInfo(deviceMI, "Claimed ✓ · \(v)")
|
|
508
508
|
} else {
|
|
509
509
|
setInfo(deviceMI, "Claimed ✓ — synced to your account")
|