@juejin-opensource/jusage 0.1.1-beta.8

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.
Files changed (36) hide show
  1. package/README.md +72 -0
  2. package/bin/jusage.js +2 -0
  3. package/dist/daemon.d.ts +12 -0
  4. package/dist/daemon.js +68 -0
  5. package/dist/dashboard/assets/antigravity-color-DgAQAdn3.svg +1 -0
  6. package/dist/dashboard/assets/click-idle-6pQvdt8N.png +0 -0
  7. package/dist/dashboard/assets/dashboard-CUazuH2T.js +55 -0
  8. package/dist/dashboard/assets/dashboard-QA2DxQpC.css +1 -0
  9. package/dist/dashboard/assets/download-BzFSswoC.js +1 -0
  10. package/dist/dashboard/assets/env-BKC5UKtr.js +1 -0
  11. package/dist/dashboard/assets/hawking-idle-C6M3ZvDm.png +0 -0
  12. package/dist/dashboard/assets/hermesagent-DUfTwIy7.svg +1 -0
  13. package/dist/dashboard/assets/index-1sV0Fo6m.js +143 -0
  14. package/dist/dashboard/assets/index-C1eSlCqF.css +1 -0
  15. package/dist/dashboard/assets/index-DcW6PDH-.js +1 -0
  16. package/dist/dashboard/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  17. package/dist/dashboard/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  18. package/dist/dashboard/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  19. package/dist/dashboard/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  20. package/dist/dashboard/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  21. package/dist/dashboard/assets/qoder-color-CCjsVbDP.svg +1 -0
  22. package/dist/dashboard/assets/rank-DedRCNMw.js +16 -0
  23. package/dist/dashboard/assets/settings-jDmOMdkE.js +1 -0
  24. package/dist/dashboard/assets/yoyo-idle-DmXh5tLT.png +0 -0
  25. package/dist/dashboard/brand-logos/claude-code.svg +1 -0
  26. package/dist/dashboard/brand-logos/codex.svg +15 -0
  27. package/dist/dashboard/index.html +13 -0
  28. package/dist/index.d.ts +10 -0
  29. package/dist/index.js +486 -0
  30. package/dist/service-macos.d.ts +7 -0
  31. package/dist/service-macos.js +97 -0
  32. package/dist/service-windows.d.ts +6 -0
  33. package/dist/service-windows.js +50 -0
  34. package/dist/service.d.ts +3 -0
  35. package/dist/service.js +109 -0
  36. package/package.json +51 -0
package/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # @juejin-opensource/jusage
2
+
3
+ 本地优先的 AI Agent Token 用量看板:采集 Claude / Codex / Cursor 用量,启动本地面板,可选上报掘金云端。
4
+
5
+ ## 安装
6
+
7
+ 需要 Node.js >= 20。
8
+
9
+ ```bash
10
+ npm i -g @juejin-opensource/jusage
11
+ ```
12
+
13
+ ## 快速开始
14
+
15
+ 推荐用后台服务启动(常驻,支持开机自启,macOS / Windows):
16
+
17
+ ```bash
18
+ jusage service start
19
+ # 面板: http://127.0.0.1:8452
20
+ ```
21
+
22
+ 首次启动会写入数据目录 `~/.ai-usage/`,尝试注册 Claude / Codex Hook,并同步本地用量。
23
+
24
+ 常用管理:
25
+
26
+ ```bash
27
+ jusage service status
28
+ jusage service stop
29
+ jusage status # 查看面板与同步状态
30
+ ```
31
+
32
+ 需要前台跑(当前终端占用、方便看日志)时再用:
33
+
34
+ ```bash
35
+ jusage start # 或直接 jusage
36
+ # Ctrl+C 停止
37
+ ```
38
+
39
+ ## 命令
40
+
41
+ | 命令 | 说明 |
42
+ |------|------|
43
+ | `jusage service <action>` | **推荐** 后台服务与开机自启;`action`: `start` \| `stop` \| `status` |
44
+ | `jusage` / `jusage start` | 前台启动本地面板与同步 |
45
+ | `jusage stop` | 停止当前进程内的前台服务 |
46
+ | `jusage status` | 查看 CLI / 面板当前状态 |
47
+ | `jusage sync` | 手动同步本地用量数据 |
48
+ | `jusage upload` | 上报数据到云端 |
49
+ | `jusage help` | 显示帮助 |
50
+
51
+ ## 常用选项
52
+
53
+ ```bash
54
+ jusage service start
55
+ jusage start --port 8452
56
+ jusage sync --source=claude # claude | codex | cursor | all
57
+ jusage upload --force # 忽略云端同步开关,强制上报
58
+ jusage upload --reconcile # 全量对账后上报
59
+ ```
60
+
61
+ ## 数据与同步
62
+
63
+ - 数据目录:`~/.ai-usage/`
64
+ - Claude / Codex:优先 Hook 触发 `jusage sync`;失败时回退定时轮询
65
+ - Cursor:轮询模式(无 Hook)
66
+ - 云端同步:面板「设置」可配置 Server 地址、Token 与开关;也可手动 `jusage upload`
67
+
68
+ 调试日志:`~/.ai-usage/logs/notify.log`、`~/.ai-usage/logs/sync.log`。
69
+
70
+ ## License
71
+
72
+ MIT
package/bin/jusage.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import '../dist/index.js';
@@ -0,0 +1,12 @@
1
+ import { clearPid, getRunningOwner, getRunningPid, isPidAlive, pidFilePath, readPid, readRuntimeOwner, writePid, writeRuntimeOwner } from '@juejin-opensource/jusage-core';
2
+ export { clearPid, getRunningOwner, getRunningPid, isPidAlive, pidFilePath, readPid, readRuntimeOwner, writePid, writeRuntimeOwner, };
3
+ export declare function daemonLogPath(dataDir?: string): string;
4
+ export declare function ensureDaemonLogDir(dataDir?: string): Promise<string>;
5
+ /** Soft then hard kill. Returns true if the process is gone. */
6
+ export declare function stopPid(pid: number, timeoutMs?: number): Promise<boolean>;
7
+ export declare function waitForPid(dataDir?: string, timeoutMs?: number): Promise<number | null>;
8
+ export declare function resolveServiceCommand(cliBinPath: string): {
9
+ nodePath: string;
10
+ args: string[];
11
+ commandLine: string;
12
+ };
package/dist/daemon.js ADDED
@@ -0,0 +1,68 @@
1
+ import { mkdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { DEFAULT_DATA_DIR, logsDir, clearPid, getRunningOwner, getRunningPid, isPidAlive, pidFilePath, readPid, readRuntimeOwner, writePid, writeRuntimeOwner, } from '@juejin-opensource/jusage-core';
4
+ export { clearPid, getRunningOwner, getRunningPid, isPidAlive, pidFilePath, readPid, readRuntimeOwner, writePid, writeRuntimeOwner, };
5
+ export function daemonLogPath(dataDir = DEFAULT_DATA_DIR) {
6
+ return join(logsDir(dataDir), 'daemon.log');
7
+ }
8
+ export async function ensureDaemonLogDir(dataDir = DEFAULT_DATA_DIR) {
9
+ const dir = logsDir(dataDir);
10
+ await mkdir(dir, { recursive: true });
11
+ return dir;
12
+ }
13
+ /** Soft then hard kill. Returns true if the process is gone. */
14
+ export async function stopPid(pid, timeoutMs = 5000) {
15
+ if (!isPidAlive(pid))
16
+ return true;
17
+ try {
18
+ process.kill(pid, 'SIGTERM');
19
+ }
20
+ catch {
21
+ return !isPidAlive(pid);
22
+ }
23
+ const deadline = Date.now() + timeoutMs;
24
+ while (Date.now() < deadline) {
25
+ if (!isPidAlive(pid))
26
+ return true;
27
+ await new Promise((r) => setTimeout(r, 100));
28
+ }
29
+ if (!isPidAlive(pid))
30
+ return true;
31
+ try {
32
+ if (process.platform === 'win32') {
33
+ const { spawnSync } = await import('node:child_process');
34
+ spawnSync('taskkill', ['/PID', String(pid), '/T', '/F'], { stdio: 'ignore' });
35
+ }
36
+ else {
37
+ process.kill(pid, 'SIGKILL');
38
+ }
39
+ }
40
+ catch {
41
+ // ignore
42
+ }
43
+ await new Promise((r) => setTimeout(r, 200));
44
+ return !isPidAlive(pid);
45
+ }
46
+ export async function waitForPid(dataDir = DEFAULT_DATA_DIR, timeoutMs = 15_000) {
47
+ const deadline = Date.now() + timeoutMs;
48
+ while (Date.now() < deadline) {
49
+ const pid = await getRunningPid(dataDir);
50
+ if (pid != null)
51
+ return pid;
52
+ await new Promise((r) => setTimeout(r, 200));
53
+ }
54
+ return getRunningPid(dataDir);
55
+ }
56
+ export function resolveServiceCommand(cliBinPath) {
57
+ const nodePath = process.execPath;
58
+ const args = [cliBinPath, 'start'];
59
+ const commandLine = process.platform === 'win32'
60
+ ? `"${nodePath}" "${cliBinPath}" start`
61
+ : `${shellQuote(nodePath)} ${shellQuote(cliBinPath)} start`;
62
+ return { nodePath, args, commandLine };
63
+ }
64
+ function shellQuote(value) {
65
+ if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value))
66
+ return value;
67
+ return `'${value.replace(/'/g, `'\\''`)}'`;
68
+ }
@@ -0,0 +1 @@
1
+ <svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Antigravity</title><mask height="23" id="lobe-icons-antigravity-0-_R_0_" maskUnits="userSpaceOnUse" width="24" x="0" y="1"><path d="M21.751 22.607c1.34 1.005 3.35.335 1.508-1.508C17.73 15.74 18.904 1 12.037 1 5.17 1 6.342 15.74.815 21.1c-2.01 2.009.167 2.511 1.507 1.506 5.192-3.517 4.857-9.714 9.715-9.714 4.857 0 4.522 6.197 9.714 9.715z" fill="#fff"></path></mask><g mask="url(#lobe-icons-antigravity-0-_R_0_)"><g filter="url(#lobe-icons-antigravity-1-_R_0_)"><path d="M-1.018-3.992c-.408 3.591 2.686 6.89 6.91 7.37 4.225.48 7.98-2.043 8.387-5.633.408-3.59-2.686-6.89-6.91-7.37-4.225-.479-7.98 2.043-8.387 5.633z" fill="#FFE432"></path></g><g filter="url(#lobe-icons-antigravity-2-_R_0_)"><path d="M15.269 7.747c1.058 4.557 5.691 7.374 10.348 6.293 4.657-1.082 7.575-5.653 6.516-10.21-1.058-4.556-5.691-7.374-10.348-6.292-4.657 1.082-7.575 5.653-6.516 10.21z" fill="#FC413D"></path></g><g filter="url(#lobe-icons-antigravity-3-_R_0_)"><path d="M-12.443 10.804c1.338 4.703 7.36 7.11 13.453 5.378 6.092-1.733 9.947-6.95 8.61-11.652C8.282-.173 2.26-2.58-3.833-.848-9.925.884-13.78 6.1-12.443 10.804z" fill="#00B95C"></path></g><g filter="url(#lobe-icons-antigravity-4-_R_0_)"><path d="M-12.443 10.804c1.338 4.703 7.36 7.11 13.453 5.378 6.092-1.733 9.947-6.95 8.61-11.652C8.282-.173 2.26-2.58-3.833-.848-9.925.884-13.78 6.1-12.443 10.804z" fill="#00B95C"></path></g><g filter="url(#lobe-icons-antigravity-5-_R_0_)"><path d="M-7.608 14.703c3.352 3.424 9.126 3.208 12.896-.483 3.77-3.69 4.108-9.459.756-12.883C2.69-2.087-3.083-1.871-6.853 1.82c-3.77 3.69-4.108 9.458-.755 12.883z" fill="#00B95C"></path></g><g filter="url(#lobe-icons-antigravity-6-_R_0_)"><path d="M9.932 27.617c1.04 4.482 5.384 7.303 9.7 6.3 4.316-1.002 6.971-5.448 5.93-9.93-1.04-4.483-5.384-7.304-9.7-6.301-4.316 1.002-6.971 5.448-5.93 9.93z" fill="#3186FF"></path></g><g filter="url(#lobe-icons-antigravity-7-_R_0_)"><path d="M2.572-8.185C.392-3.329 2.778 2.472 7.9 4.771c5.122 2.3 11.042.227 13.222-4.63 2.18-4.855-.205-10.656-5.327-12.955-5.122-2.3-11.042-.227-13.222 4.63z" fill="#FBBC04"></path></g><g filter="url(#lobe-icons-antigravity-8-_R_0_)"><path d="M-3.267 38.686c-5.277-2.072 3.742-19.117 5.984-24.83 2.243-5.712 8.34-8.664 13.616-6.592 5.278 2.071 11.533 13.482 9.29 19.195-2.242 5.713-23.613 14.298-28.89 12.227z" fill="#3186FF"></path></g><g filter="url(#lobe-icons-antigravity-9-_R_0_)"><path d="M28.71 17.471c-1.413 1.649-5.1.808-8.236-1.878-3.135-2.687-4.531-6.201-3.118-7.85 1.412-1.649 5.1-.808 8.235 1.878s4.532 6.2 3.119 7.85z" fill="#749BFF"></path></g><g filter="url(#lobe-icons-antigravity-10-_R_0_)"><path d="M18.163 9.077c5.81 3.93 12.502 4.19 14.946.577 2.443-3.612-.287-9.727-6.098-13.658-5.81-3.931-12.502-4.19-14.946-.577-2.443 3.612.287 9.727 6.098 13.658z" fill="#FC413D"></path></g><g filter="url(#lobe-icons-antigravity-11-_R_0_)"><path d="M-.915 2.684c-1.44 3.473-.97 6.967 1.05 7.804 2.02.837 4.824-1.3 6.264-4.772 1.44-3.473.97-6.967-1.05-7.804-2.02-.837-4.824 1.3-6.264 4.772z" fill="#FFEE48"></path></g></g><defs><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="17.587" id="lobe-icons-antigravity-1-_R_0_" width="19.838" x="-3.288" y="-11.917"><feFlood flood-opacity="0" result="BackgroundImageFix"></feFlood><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"></feBlend><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="1.117"></feGaussianBlur></filter><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="38.565" id="lobe-icons-antigravity-2-_R_0_" width="38.9" x="4.251" y="-13.493"><feFlood flood-opacity="0" result="BackgroundImageFix"></feFlood><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"></feBlend><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="5.4"></feGaussianBlur></filter><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="36.517" id="lobe-icons-antigravity-3-_R_0_" width="40.955" x="-21.889" y="-10.592"><feFlood flood-opacity="0" result="BackgroundImageFix"></feFlood><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"></feBlend><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="4.591"></feGaussianBlur></filter><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="36.517" id="lobe-icons-antigravity-4-_R_0_" width="40.955" x="-21.889" y="-10.592"><feFlood flood-opacity="0" result="BackgroundImageFix"></feFlood><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"></feBlend><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="4.591"></feGaussianBlur></filter><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="36.595" id="lobe-icons-antigravity-5-_R_0_" width="36.632" x="-19.099" y="-10.278"><feFlood flood-opacity="0" result="BackgroundImageFix"></feFlood><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"></feBlend><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="4.591"></feGaussianBlur></filter><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="34.087" id="lobe-icons-antigravity-6-_R_0_" width="33.533" x=".981" y="8.758"><feFlood flood-opacity="0" result="BackgroundImageFix"></feFlood><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"></feBlend><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="4.363"></feGaussianBlur></filter><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="35.276" id="lobe-icons-antigravity-7-_R_0_" width="35.978" x="-6.143" y="-21.659"><feFlood flood-opacity="0" result="BackgroundImageFix"></feFlood><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"></feBlend><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="3.954"></feGaussianBlur></filter><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="46.523" id="lobe-icons-antigravity-8-_R_0_" width="45.114" x="-11.96" y="-.46"><feFlood flood-opacity="0" result="BackgroundImageFix"></feFlood><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"></feBlend><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="3.531"></feGaussianBlur></filter><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="24.054" id="lobe-icons-antigravity-9-_R_0_" width="25.094" x="10.485" y=".58"><feFlood flood-opacity="0" result="BackgroundImageFix"></feFlood><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"></feBlend><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="3.159"></feGaussianBlur></filter><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="30.007" id="lobe-icons-antigravity-10-_R_0_" width="33.508" x="5.833" y="-12.467"><feFlood flood-opacity="0" result="BackgroundImageFix"></feFlood><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"></feBlend><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="2.669"></feGaussianBlur></filter><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="26.151" id="lobe-icons-antigravity-11-_R_0_" width="22.194" x="-8.355" y="-8.876"><feFlood flood-opacity="0" result="BackgroundImageFix"></feFlood><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"></feBlend><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="3.303"></feGaussianBlur></filter></defs></svg>