clauddy 1.29.0 → 2.0.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/preload.js DELETED
@@ -1,43 +0,0 @@
1
- const { contextBridge, ipcRenderer } = require('electron')
2
-
3
- contextBridge.exposeInMainWorld('api', {
4
- watchPetPointer: (enabled) => ipcRenderer.send('pet-pointer-watch', enabled),
5
- onPetPointer: (cb) => ipcRenderer.on('pet-pointer', (_e, point) => cb(point)),
6
- setReminder: (provider, enabled) => ipcRenderer.send('set-reminder', provider, enabled),
7
- onReminders: (cb) => ipcRenderer.on('reminders', (_e, state) => cb(state)),
8
- onReminderDue: (cb) => ipcRenderer.on('reminder-due', (_e, event) => cb(event)),
9
- onUsage: (cb) => ipcRenderer.on('usage', (_e, data) => cb(data)),
10
- onError: (cb) => ipcRenderer.on('usage-error', (_e, msg) => cb(msg)),
11
- onConfig: (cb) => ipcRenderer.on('config', (_e, cfg) => cb(cfg)),
12
- saveConfig: (patch) => ipcRenderer.send('save-config', patch),
13
- resize: (w, h, animate) => ipcRenderer.send('resize', w, h, animate),
14
- openUsage: (provider) => ipcRenderer.send('open-usage', provider),
15
- codexDetect: () => ipcRenderer.send('codex-detect'),
16
- onCodexDetected: (cb) => ipcRenderer.on('codex-detected', (_e, r) => cb(r)),
17
- codexEnable: (on) => ipcRenderer.send('codex-enable', on),
18
- onCodex: (cb) => ipcRenderer.on('codex', (_e, c) => cb(c)),
19
- cursorDetect: () => ipcRenderer.send('cursor-detect'),
20
- onCursorDetected: (cb) => ipcRenderer.on('cursor-detected', (_e, r) => cb(r)),
21
- cursorEnable: (on) => ipcRenderer.send('cursor-enable', on),
22
- onCursor: (cb) => ipcRenderer.on('cursor', (_e, c) => cb(c)),
23
- onRealUsage: (cb) => ipcRenderer.on('real-usage', (_e, u) => cb(u)),
24
- onAuthState: (cb) => ipcRenderer.on('auth-state', (_e, s) => cb(s)),
25
- onProfile: (cb) => ipcRenderer.on('profile', (_e, p) => cb(p)),
26
- onAuthPending: (cb) => ipcRenderer.on('auth-pending', () => cb()),
27
- onAuthResult: (cb) => ipcRenderer.on('auth-result', (_e, r) => cb(r)),
28
- authStart: () => ipcRenderer.send('auth-start'),
29
- authCode: (code) => ipcRenderer.send('auth-code', code),
30
- authLogout: () => ipcRenderer.send('auth-logout'),
31
- onAccounts: (cb) => ipcRenderer.on('accounts', (_e, a) => cb(a)),
32
- accountSwitch: (id) => ipcRenderer.send('accounts-switch', id),
33
- accountAdd: () => ipcRenderer.send('accounts-add'),
34
- accountCancelAdd: () => ipcRenderer.send('accounts-cancel-add'),
35
- accountRemove: (id) => ipcRenderer.send('accounts-remove', id),
36
- onDebugState: (cb) => ipcRenderer.on('debug-state', (_e, s) => cb(s)),
37
- onVersion: (cb) => ipcRenderer.on('version', (_e, v) => cb(v)),
38
- onUpdateStatus: (cb) => ipcRenderer.on('update-status', (_e, s) => cb(s)),
39
- onPopoverAnchor: (cb) => ipcRenderer.on('popover-anchor', (_e, x) => cb(x)),
40
- checkUpdates: () => ipcRenderer.send('check-updates'),
41
- doUpdate: () => ipcRenderer.send('do-update'),
42
- quit: () => ipcRenderer.send('quit'),
43
- })
package/reminders.js DELETED
@@ -1,54 +0,0 @@
1
- // One-shot reset reminders, independent of automatic threshold alerts.
2
- function createReminders({ load, save, now = Date.now }) {
3
- let pending = []
4
- let delivered = {}
5
- const valid = (r) =>
6
- r &&
7
- ['claude', 'codex', 'cursor'].includes(r.provider) &&
8
- typeof r.key === 'string' &&
9
- typeof r.label === 'string' &&
10
- Number.isFinite(r.at) &&
11
- Number.isFinite(r.pct)
12
- try {
13
- const data = load()
14
- pending = Array.isArray(data?.pending) ? data.pending.filter(valid) : []
15
- delivered = data?.delivered || {}
16
- } catch {}
17
- function persist(nextPending, nextDelivered = delivered) {
18
- const recent = Object.fromEntries(
19
- Object.entries(nextDelivered).filter(([, at]) => Number.isFinite(at) && now() - at < 3600000),
20
- )
21
- save({ pending: nextPending, delivered: recent })
22
- pending = nextPending
23
- delivered = recent
24
- }
25
- return {
26
- list: () => pending.map((r) => ({ ...r })),
27
- arm(reminder) {
28
- // Cursor's budget is a billing cycle, so a reset can be a month out
29
- if (!valid(reminder) || reminder.at <= now() || reminder.at > now() + 32 * 86400000)
30
- return false
31
- persist([...pending.filter((r) => r.key !== reminder.key), { ...reminder }])
32
- return true
33
- },
34
- cancel(key) {
35
- const found = pending.some((r) => r.key === key)
36
- if (found) persist(pending.filter((r) => r.key !== key))
37
- },
38
- takeDue() {
39
- const due = pending.filter((r) => r.at <= now())
40
- if (!due.length) return []
41
- const nextDelivered = { ...delivered }
42
- for (const r of due) nextDelivered[r.key] = now()
43
- // Consume before delivering: restart must not replay it.
44
- persist(
45
- pending.filter((r) => r.at > now()),
46
- nextDelivered,
47
- )
48
- return due
49
- },
50
- recentlyDelivered: (key) => Number.isFinite(delivered[key]) && now() - delivered[key] < 3600000,
51
- }
52
- }
53
-
54
- module.exports = { createReminders }
package/renderer/burn.js DELETED
@@ -1,98 +0,0 @@
1
- // ---- burn-rate projection ----
2
- // The panel knows where you are (82%) and when the window resets (1h 12m); it
3
- // can also say where you're headed — fit a slope through recent usage and
4
- // project the crossing of 100%. If the reset lands first there's nothing to
5
- // worry about, which is worth saying out loud rather than leaving blank.
6
- //
7
- // The slope is fitted over session *tokens*, not the account %. The % is the
8
- // number we ultimately care about, but it arrives as a whole number every ~5
9
- // min: over a short window the whole signal is a single 16 → 17 step, which
10
- // makes the fitted pace wrong by multiples. Local-log tokens step too — one
11
- // jump per assistant turn, with plateaus in between — but in increments some
12
- // 10-20x finer, so they carry a far steadier slope. The account % still anchors
13
- // it: pct/tokens converts tokens/ms into %/ms and re-calibrates on every poll,
14
- // so the projection stays tied to the real number.
15
- //
16
- // Loaded both as a plain <script> by the renderer (exposing `Burn`) and via
17
- // require() by the tests — hence the dual export at the bottom.
18
- ;(() => {
19
- const WINDOW_MS = 45 * 60 * 1000 // only fit recent samples — pace changes
20
- const MIN_SAMPLES = 4
21
- const MIN_SPAN_MS = 5 * 60 * 1000 // shorter than a % fit affords, and steadier
22
- const SAMPLE_EVERY_MS = 30 * 1000 // usage polls every few seconds; thin it out
23
-
24
- // `now` is injectable so tests can drive the trail without sleeping
25
- function createBurnTracker() {
26
- let trail = []
27
-
28
- function note(tokens, active, now = Date.now()) {
29
- if (!active || !(tokens > 0)) {
30
- trail = [] // no session, no trend
31
- return
32
- }
33
- // session tokens only climb within a window — a drop means it rolled
34
- // over, and the fresh sample starts the new trail rather than being
35
- // throttled away
36
- let last = trail[trail.length - 1]
37
- if (last && tokens < last.tokens) {
38
- trail = []
39
- last = undefined
40
- }
41
- if (last && now - last.t < SAMPLE_EVERY_MS) return
42
- trail.push({ t: now, tokens })
43
- trail = trail.filter((s) => s.t >= now - WINDOW_MS)
44
- }
45
-
46
- // least-squares slope in tokens per ms, or null when there isn't enough to say
47
- function slope() {
48
- if (trail.length < MIN_SAMPLES) return null
49
- const span = trail[trail.length - 1].t - trail[0].t
50
- if (span < MIN_SPAN_MS) return null
51
- const n = trail.length
52
- const t0 = trail[0].t
53
- let sx = 0
54
- let sy = 0
55
- let sxy = 0
56
- let sxx = 0
57
- for (const s of trail) {
58
- const x = s.t - t0
59
- sx += x
60
- sy += s.tokens
61
- sxy += x * s.tokens
62
- sxx += x * x
63
- }
64
- const denom = n * sxx - sx * sx
65
- if (denom === 0) return null
66
- return (n * sxy - sx * sy) / denom
67
- }
68
-
69
- // → { kind: 'eta', ms } when you'd run out first, { kind: 'safe' } when the
70
- // reset beats you there, or null when we can't tell yet.
71
- function project(pct, resetMs, tokens) {
72
- if (!(pct > 0) || !(tokens > 0)) return null // no anchor to convert tokens → %
73
- const tokPerMs = slope()
74
- if (tokPerMs == null || tokPerMs <= 0) return null // idle or flat
75
- const pctPerMs = tokPerMs * (pct / tokens)
76
- const eta = (100 - pct) / pctPerMs
77
- if (!Number.isFinite(eta) || eta <= 0) return null
78
- if (resetMs != null && eta >= resetMs) return { kind: 'safe' }
79
- return { kind: 'eta', ms: eta }
80
- }
81
-
82
- return {
83
- note,
84
- slope,
85
- project,
86
- reset: () => {
87
- trail = []
88
- },
89
- get trail() {
90
- return trail
91
- },
92
- }
93
- }
94
-
95
- const api = { createBurnTracker, WINDOW_MS, MIN_SAMPLES, MIN_SPAN_MS, SAMPLE_EVERY_MS }
96
- if (typeof module === 'object' && module.exports) module.exports = api
97
- else globalThis.Burn = api
98
- })()
@@ -1,52 +0,0 @@
1
- // A quiet transition detector. Startup and reconnects establish a baseline;
2
- // inactivity is a pause, never proof that an agent finished its task.
3
- ;((root) => {
4
- function createActivityTracker({ now = Date.now, cooldown = 30000 } = {}) {
5
- const seen = new Map()
6
- let lastCue = -Infinity
7
- return {
8
- forget(provider) {
9
- seen.delete(provider)
10
- },
11
- observe(provider, data) {
12
- if (!data) {
13
- seen.delete(provider)
14
- return null
15
- }
16
- const next = { active: !!data.active, activity: data.activity || 'working' }
17
- const previous = seen.get(provider)
18
- seen.set(provider, next)
19
- if (!previous) return null
20
- const changed =
21
- next.active !== previous.active || (next.active && next.activity !== previous.activity)
22
- if (!changed || now() - lastCue < cooldown) return null
23
- lastCue = now()
24
- return { provider, activity: next.active ? next.activity : 'paused' }
25
- },
26
- }
27
- }
28
- // Activity freshness is separate from quota freshness: Codex can work while
29
- // its last rate-limit reading is old. Ignore abandoned activity snapshots.
30
- const FRESH_MS = 60000
31
- const SLEEP_MS = 300000 // main's default sleepThresholdMs; Codex and Cursor have no flag of their own
32
- // each source is its last payload, or null while it isn't connected
33
- function currentActivity({ claude = null, codex = null, cursor = null } = {}, now = Date.now()) {
34
- const fresh = (at) => Number.isFinite(at) && now - at <= FRESH_MS
35
- const providers = []
36
- if (claude?.active && fresh(claude.ts)) providers.push('claude')
37
- if (codex?.active && fresh(codex.lastSeen)) providers.push('codex')
38
- if (cursor?.active && fresh(cursor.lastSeen)) providers.push('cursor')
39
- // Claude's and Cursor's logs say *what* they're doing; Codex's don't, and
40
- // two workers share one scene
41
- const solo = providers.length === 1 ? { claude, cursor }[providers[0]] : null
42
- const activity = solo?.activity || 'working'
43
- const idle = (s) => s && !!s.lastSeen && now - s.lastSeen >= SLEEP_MS
44
- const asleep = [claude && !!claude.sleeping, idle(codex), idle(cursor)].filter(
45
- (v) => v !== null && v !== undefined,
46
- )
47
- return { providers, activity, sleeping: asleep.length > 0 && asleep.every(Boolean) }
48
- }
49
- const api = { createActivityTracker, currentActivity }
50
- if (typeof module === 'object' && module.exports) module.exports = api
51
- else root.Companion = api
52
- })(globalThis)