clauddy 1.0.0 → 1.2.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 +15 -14
- package/auth.js +6 -1
- package/bin/clauddy.js +55 -8
- package/main.js +14 -2
- package/package.json +1 -1
- package/usage.js +2 -1
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ The token is saved locally (see [Data & privacy](#data--privacy)) and refreshed
|
|
|
46
46
|
</tr>
|
|
47
47
|
</table>
|
|
48
48
|
|
|
49
|
-
Plus a welcome **wave** on launch. You can
|
|
49
|
+
Plus a welcome **wave** on launch. You can [poke the pet from the terminal](#play-with-the-pet) too.
|
|
50
50
|
|
|
51
51
|
## Install
|
|
52
52
|
|
|
@@ -100,22 +100,25 @@ Settings saved from the UI live in `~/.claude-usage-monitor/config.json`, so you
|
|
|
100
100
|
}
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
-
##
|
|
103
|
+
## Play with the pet
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
With the widget running, poke it from the terminal — just for fun:
|
|
106
106
|
|
|
107
107
|
```bash
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
./pet auto # ↩️ release control, back to real usage data
|
|
108
|
+
bunx clauddy poke # 💕 squish + hearts
|
|
109
|
+
bunx clauddy celebrate # 🎉 jump + confetti
|
|
110
|
+
bunx clauddy fire # 🔥 on fire
|
|
111
|
+
bunx clauddy sleeping # 😴 blue zzz
|
|
112
|
+
bunx clauddy working # 🍴 eats token coins
|
|
113
|
+
bunx clauddy tired # 🥵 maxed out
|
|
114
|
+
bunx clauddy idle # 🙂 calm
|
|
115
|
+
bunx clauddy auto # ↩️ back to your real usage
|
|
117
116
|
```
|
|
118
117
|
|
|
118
|
+
Each state is written to the data dir the running widget watches, so it reacts
|
|
119
|
+
live. (Installed globally? Drop the `bunx`: `clauddy poke`. Working on the repo?
|
|
120
|
+
`./pet <state>` does the same.)
|
|
121
|
+
|
|
119
122
|
## How it works
|
|
120
123
|
|
|
121
124
|
- **`main.js`** — Electron main process: frameless, transparent, always-on-top window; polls usage; fires macOS notifications; watches `config.json` and `debug.json`.
|
|
@@ -150,5 +153,3 @@ your PRs.
|
|
|
150
153
|
|
|
151
154
|
- `feat:` → minor, `fix:` → patch, `feat!:`/`BREAKING CHANGE` → major.
|
|
152
155
|
- `docs:`/`chore:`/`ci:` etc. don't trigger a release.
|
|
153
|
-
- Needs an **`NPM_TOKEN`** repo secret (an npm automation token); `GITHUB_TOKEN`
|
|
154
|
-
is automatic.
|
package/auth.js
CHANGED
|
@@ -11,7 +11,12 @@ const TOKEN_URL = 'https://platform.claude.com/v1/oauth/token'
|
|
|
11
11
|
const USAGE_URL = 'https://api.anthropic.com/api/oauth/usage'
|
|
12
12
|
const SCOPE = 'org:create_api_key user:profile user:inference'
|
|
13
13
|
const UA = 'claude-cli/2.1.181 (external, cli)'
|
|
14
|
-
|
|
14
|
+
// when CLAUDE_CONFIG_DIR is set (e.g. via direnv for multi-account setups),
|
|
15
|
+
// keep the widget's data alongside that account's Claude config
|
|
16
|
+
const DATA_DIR = process.env.CLAUDE_CONFIG_DIR
|
|
17
|
+
? path.join(process.env.CLAUDE_CONFIG_DIR, 'usage-monitor')
|
|
18
|
+
: path.join(os.homedir(), '.claude-usage-monitor')
|
|
19
|
+
const TOKEN_PATH = path.join(DATA_DIR, 'auth.json')
|
|
15
20
|
|
|
16
21
|
const b64url = (buf) =>
|
|
17
22
|
buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
|
package/bin/clauddy.js
CHANGED
|
@@ -1,12 +1,59 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
|
|
3
|
+
// `clauddy` → launch the widget (spawns Electron on main.js)
|
|
4
|
+
// `clauddy <state>` → poke the running widget for fun/demo (writes the state
|
|
5
|
+
// to the data dir the app watches; the app reacts live)
|
|
6
6
|
const path = require('node:path')
|
|
7
|
-
const
|
|
7
|
+
const fs = require('node:fs')
|
|
8
|
+
const os = require('node:os')
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
// same data dir the app uses (respects CLAUDE_CONFIG_DIR for multi-account)
|
|
11
|
+
const dataDir = process.env.CLAUDE_CONFIG_DIR
|
|
12
|
+
? path.join(process.env.CLAUDE_CONFIG_DIR, 'usage-monitor')
|
|
13
|
+
: path.join(os.homedir(), '.claude-usage-monitor')
|
|
14
|
+
|
|
15
|
+
const STATES = [
|
|
16
|
+
'fire',
|
|
17
|
+
'sleeping',
|
|
18
|
+
'working',
|
|
19
|
+
'tired',
|
|
20
|
+
'idle',
|
|
21
|
+
'poke',
|
|
22
|
+
'celebrate',
|
|
23
|
+
'auto',
|
|
24
|
+
'clear',
|
|
25
|
+
]
|
|
26
|
+
const arg = process.argv[2]
|
|
27
|
+
|
|
28
|
+
if (!arg) {
|
|
29
|
+
// no argument → launch the app
|
|
30
|
+
const { spawn } = require('node:child_process')
|
|
31
|
+
const electron = require('electron')
|
|
32
|
+
const child = spawn(electron, [path.join(__dirname, '..', 'main.js')], { stdio: 'inherit' })
|
|
33
|
+
child.on('close', (code) => process.exit(code ?? 0))
|
|
34
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
35
|
+
console.log(
|
|
36
|
+
[
|
|
37
|
+
'clauddy — a cute desktop pet that tracks your Claude Code usage',
|
|
38
|
+
'',
|
|
39
|
+
'Usage:',
|
|
40
|
+
' clauddy launch the widget',
|
|
41
|
+
' clauddy <state> poke the running widget (just for fun)',
|
|
42
|
+
'',
|
|
43
|
+
'States: fire, sleeping, working, tired, idle, poke, celebrate, auto',
|
|
44
|
+
'(the widget must be running for a state to show)',
|
|
45
|
+
].join('\n'),
|
|
46
|
+
)
|
|
47
|
+
} else if (STATES.includes(arg)) {
|
|
48
|
+
// write the state for the running app to pick up
|
|
49
|
+
fs.mkdirSync(dataDir, { recursive: true })
|
|
50
|
+
fs.writeFileSync(
|
|
51
|
+
path.join(dataDir, 'debug.json'),
|
|
52
|
+
`${JSON.stringify({ state: arg, t: Date.now() })}\n`,
|
|
53
|
+
)
|
|
54
|
+
console.log(`clauddy → ${arg} (the running widget will react)`)
|
|
55
|
+
} else {
|
|
56
|
+
console.error(`clauddy: unknown command "${arg}"`)
|
|
57
|
+
console.error('try: fire, sleeping, working, tired, idle, poke, celebrate, auto — or --help')
|
|
58
|
+
process.exit(1)
|
|
59
|
+
}
|
package/main.js
CHANGED
|
@@ -5,8 +5,20 @@ const os = require('node:os')
|
|
|
5
5
|
const { getUsage } = require('./usage')
|
|
6
6
|
const auth = require('./auth')
|
|
7
7
|
|
|
8
|
-
// data dir: kept outside the project folder so moving the repo doesn't break it
|
|
9
|
-
|
|
8
|
+
// data dir: kept outside the project folder so moving the repo doesn't break it.
|
|
9
|
+
// when CLAUDE_CONFIG_DIR is set (e.g. via direnv for multi-account setups), nest
|
|
10
|
+
// the widget's data under that account's Claude config dir so each account gets
|
|
11
|
+
// its own auth.json, config.json, and Electron userData.
|
|
12
|
+
const DATA_DIR = process.env.CLAUDE_CONFIG_DIR
|
|
13
|
+
? path.join(process.env.CLAUDE_CONFIG_DIR, 'usage-monitor')
|
|
14
|
+
: path.join(os.homedir(), '.claude-usage-monitor')
|
|
15
|
+
// when isolating per-account, also pin Electron's userData under DATA_DIR so two
|
|
16
|
+
// widgets can run in parallel without fighting over cookies/cache. left untouched
|
|
17
|
+
// in the default case so existing installs keep their window position etc.
|
|
18
|
+
if (process.env.CLAUDE_CONFIG_DIR) {
|
|
19
|
+
fs.mkdirSync(DATA_DIR, { recursive: true })
|
|
20
|
+
app.setPath('userData', path.join(DATA_DIR, 'electron'))
|
|
21
|
+
}
|
|
10
22
|
// external config: edit without rebuilding the .app
|
|
11
23
|
const EXTERNAL_CONFIG = path.join(DATA_DIR, 'config.json')
|
|
12
24
|
// debug channel: `./pet <state>` writes here to force a state (dev only)
|
package/package.json
CHANGED
package/usage.js
CHANGED
|
@@ -2,7 +2,8 @@ const fs = require('node:fs')
|
|
|
2
2
|
const path = require('node:path')
|
|
3
3
|
const os = require('node:os')
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const CLAUDE_DIR = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude')
|
|
6
|
+
const PROJECTS_DIR = path.join(CLAUDE_DIR, 'projects')
|
|
6
7
|
|
|
7
8
|
function labelFor(model) {
|
|
8
9
|
if (!model) return 'desconhecido'
|