Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 45 additions & 1 deletion catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"schemaVersion": 1,
"providerId": "official",
"name": "PI-Desktop Official Plugins",
"updatedAt": "2026-09-14T04:38:39Z",
"updatedAt": "2026-09-17T16:33:01Z",
"homepage": "https://github.com/vastsa/pi-desktop-plugins",
"plugins": [
{
Expand Down Expand Up @@ -347,6 +347,50 @@
}
]
},
{
"id": "io.github.muzimu217.usage-dashboard",
"name": "Usage Dashboard",
"description": "A native PI-Desktop usage dashboard built on the official read-only usage contract (pi.usage.listTurns): 365-day activity heatmap, model-share donut, daily trend lines, top sessions and streaks, in a light and dark panel.",
"i18n": {
"en": {
"name": "Usage Dashboard",
"description": "A native PI-Desktop usage dashboard built on the official read-only usage contract (pi.usage.listTurns): 365-day activity heatmap, model-share donut, daily trend lines, top sessions and streaks, in a light and dark panel.",
"safetyNotes": "Read-only aggregation over the host's official usage interface (pi.usage.listTurns). Counts tokens per turn only \u2014 never reads message text, tool arguments, project paths or credentials; keeps at most an 8-character session id prefix; writes nothing outside its own plugin settings; makes no network requests. Covers PI-Desktop's native usage only and reads no other tool's files."
},
"zh-CN": {
"name": "\u539f\u751f\u7528\u91cf\u770b\u677f",
"description": "\u57fa\u4e8e PI-Desktop \u5b98\u65b9\u53ea\u8bfb\u7528\u91cf\u63a5\u53e3\uff08pi.usage.listTurns\uff09\u6784\u5efa\u7684\u539f\u751f\u7528\u91cf\u770b\u677f\uff1a365 \u5929\u6d3b\u52a8\u70ed\u529b\u56fe\u3001\u6a21\u578b\u5360\u6bd4\u73af\u5f62\u56fe\u3001\u6bcf\u65e5\u8d8b\u52bf\u6298\u7ebf\u3001Top \u4f1a\u8bdd\u4e0e\u8fde\u7eed\u4f7f\u7528\u5929\u6570\uff0c\u6df1\u6d45\u4e24\u5957\u4e3b\u9898\u3002",
"safetyNotes": "\u53ea\u8bfb\u805a\u5408\u5bbf\u4e3b\u5b98\u65b9\u7528\u91cf\u63a5\u53e3\uff08pi.usage.listTurns\uff09\u8fd4\u56de\u7684\u8ba1\u6570\uff1a\u4e0d\u8bfb\u53d6\u6d88\u606f\u6b63\u6587\u3001\u5de5\u5177\u53c2\u6570\u3001\u9879\u76ee\u8def\u5f84\u6216\u51ed\u636e\uff0c\u6700\u591a\u4fdd\u7559 8 \u4f4d session id \u524d\u7f00\uff0c\u9664\u81ea\u8eab\u63d2\u4ef6\u8bbe\u7f6e\u5916\u4e0d\u5199\u4efb\u4f55\u4f4d\u7f6e\uff0c\u4e0d\u53d1\u8d77\u7f51\u7edc\u8bf7\u6c42\u3002\u4ec5\u8986\u76d6 PI-Desktop \u539f\u751f\u7528\u91cf\uff0c\u4e0d\u8bfb\u53d6\u5176\u4ed6\u5de5\u5177\u7684\u4efb\u4f55\u6587\u4ef6\u3002"
}
},
"author": "muzimu217",
"categories": [
"productivity",
"developer-tools"
],
"verified": true,
"downloads": 0,
"homepage": "https://github.com/vastsa/pi-desktop-plugins/tree/main/plugins/io.github.muzimu217.usage-dashboard",
"repository": "https://github.com/vastsa/pi-desktop-plugins",
"readmeMarkdown": "# Usage Dashboard\uff08\u539f\u751f\u7528\u91cf\u770b\u677f\uff09\n\nA native PI-Desktop usage dashboard. It is the first consumer of the host's\nofficial read-only usage contract `pi.usage.listTurns` (PI-Desktop host\nPR #503) and renders that fact stream as a local board: metric cards, a\n365-day activity heatmap, a model-share donut, per-model daily trend lines,\ntop sessions and activity streaks \u2014 in a light and a dark palette.\n\n\u539f\u751f\u7528\u91cf\u770b\u677f\uff1aPI-Desktop \u5b98\u65b9\u53ea\u8bfb\u7528\u91cf\u63a5\u53e3 `pi.usage.listTurns`\uff08\u5bbf\u4e3b PR #503\uff09\n\u7684\u9996\u4e2a\u6d88\u8d39\u8005\u3002\u628a\u5b98\u65b9\u4e8b\u5b9e\u9762\u6e32\u67d3\u6210\u672c\u5730\u770b\u677f\uff1a\u6307\u6807\u5361\u3001365 \u5929\u6d3b\u52a8\u70ed\u529b\u56fe\u3001\u6a21\u578b\u5360\u6bd4\n\u73af\u5f62\u56fe\u3001\u5206\u6a21\u578b\u6bcf\u65e5\u8d8b\u52bf\u7ebf\u3001Top \u4f1a\u8bdd\u4e0e\u8fde\u7eed\u4f7f\u7528\u5929\u6570\uff0c\u6df1\u6d45\u4e24\u5957\u4e3b\u9898\u3002\n\n## Scope and differentiation / \u5b9a\u4f4d\u4e0e\u5dee\u5f02\u5316\n\n- **Official contract only.** All data comes from `pi.usage.listTurns`. The\n plugin never reads transcript files, the host database, or any other AI\n tool's local data. It covers PI-Desktop's native usage only.\n \u53ea\u7528\u6b63\u5f0f\u5951\u7ea6\uff1a\u6570\u636e\u5168\u90e8\u6765\u81ea `pi.usage.listTurns`\uff0c\u4e0d\u76f4\u8bfb\u8f6c\u5f55\u672c\u3001\u5bbf\u4e3b\u6570\u636e\u5e93\u6216\n \u5176\u4ed6\u5de5\u5177\u7684\u672c\u5730\u6587\u4ef6\uff1b\u4ec5\u8986\u76d6 PI-Desktop \u539f\u751f\u7528\u91cf\u3002\n- Differs from `pi.token-insights`, which aggregates multiple tools and reads\n host records directly: this plugin is a thin, permission-minimal renderer of\n the official fact interface.\n \u4e0e pi.token-insights\uff08\u591a\u5de5\u5177\u805a\u5408\u3001\u76f4\u8bfb\u5bbf\u4e3b\u5e93\uff09\u5dee\u5f02\u5316\uff1a\u672c\u63d2\u4ef6\u662f\u6b63\u5f0f\u4e8b\u5b9e\u63a5\u53e3\n \u4e4b\u4e0a\u7684\u8584\u6e32\u67d3\u5c42\uff0c\u6743\u9650\u6700\u5c0f\u5316\u3002\n\n## Data channel / \u6570\u636e\u901a\u9053\n\nThe panel bridge is read-only, so the plugin process does all host access\n(the pattern `pi.token-insights` established):\n\n1. `onLoad` (and the `usageDashboard.open` command) walks the full history:\n windows of 365 days walked backwards from now (`toMs = previous fromMs \u2212 1`),\n each window paged by cursor until `nextCursor = null`; the first fully empty\n window ends the walk; a `turnId` set dedupes across window seams.\n2. The deduped turns go through the pure aggregator `lib/aggregate.js`\n (`aggregateCube(turns, nowMs)`, clock injected for testability).\n3. The cube is published via `pi.plugin.setSettings({ usageCube, scanState })`;\n the panel polls plugin settings and renders. Range switching (7/30/90/365\n days) is a renderer-side filter over the cube's daily rows \u2014 no second walk.\n\n## Accounting / \u805a\u5408\u53e3\u5f84\uff08v3.1\uff09\n\n- **Headline tokens = `inputTokens + outputTokens`.** Cache read, cache write\n and reasoning tokens appear only in a diagnostics line and never enter the\n headline. headline \u4ec5\u8ba1\u8f93\u5165 + \u8f93\u51fa\uff1b\u7f13\u5b58\u8bfb/\u5199\u4e0e\u63a8\u7406 token \u53ea\u8fdb\u8bca\u65ad\u884c\u3002\n- Cards: total tokens, turn count, session count (deduped `sessionId`),\n peak day (max daily headline + its date), current/longest streak.\n- Daily keys are **local-calendar days** built from `endedAt`, so the heatmap\n is not shifted by timezone conversion.\n- Streaks (v1, simple): a day is active when its headline > 0; the current\n streak anchors on today and falls back to yesterday; any one-day gap breaks\n a run. \u53e3\u5f84\u6700\u7b80\u5b9e\u73b0\uff0c\u89c1 `lib/aggregate.js` \u6ce8\u91ca\u3002\n- The panel recomputes everything that *can* be derived from daily rows for\n the selected range (totals, turns, peak, model shares, trend). Session count,\n the diagnostics line and the Top-sessions list are all-history figures and\n labelled as such \u2014 a session spans days, so it cannot be counted from daily\n rows without lying.\n\n## Privacy / \u9690\u79c1\n\nRead-only aggregation over the official interface. Counts only \u2014 never message\ntext, tool arguments, project paths or credentials. At most an 8-character\nsession id prefix is kept. Nothing is written outside this plugin's own\nsettings, and no network request is made. Every number stays on this device.\n\n\u53ea\u8bfb\u805a\u5408\u3001\u4ec5\u8ba1\u6570\uff1a\u4e0d\u8bfb\u53d6\u6d88\u606f\u6b63\u6587/\u5de5\u5177\u53c2\u6570/\u9879\u76ee\u8def\u5f84/\u51ed\u636e\uff0c\u6700\u591a\u4fdd\u7559 8 \u4f4d\nsession id \u524d\u7f00\uff1b\u9664\u81ea\u8eab\u63d2\u4ef6\u8bbe\u7f6e\u5916\u4e0d\u5199\u4efb\u4f55\u4f4d\u7f6e\uff0c\u4e0d\u53d1\u8d77\u7f51\u7edc\u8bf7\u6c42\u3002\n\n## Permissions / \u6743\u9650\n\n- `ui.panel` \u2014 the dashboard panel\n- `usage.read` \u2014 read aggregate token usage through the host's official interface\n\n## Panel\n\n- 7/30/90/365-day range switch (renderer-side filtering), language follows the\n system with an EN/\u4e2d\u6587 override, theme follows `prefers-color-scheme` with a\n light/dark override (both persisted locally).\n- Charts are hand-rolled SVG: week-column heatmap with month axis and legend,\n donut with center total and a two-column legend, daily trend with per-model\n toggleable lines.\n- Accessibility: `sr-only` data tables twin the heatmap and the trend chart,\n the donut carries an aria-label summarizing the shares, and all controls are\n focusable buttons.\n\n## Host requirement / \u5bbf\u4e3b\u8981\u6c42\n\n`pi.usage.listTurns` is provided by PI-Desktop (host PR #503). On builds\nwithout it the panel shows an explicit \"host interface not available\" state\ninstead of an empty board. \u9762\u677f\u5bf9\u672a\u63d0\u4f9b\u8be5\u63a5\u53e3\u7684\u65e7\u7248\u672c\u5bbf\u4e3b\u663e\u793a\u660e\u786e\u63d0\u793a\uff0c\n\u800c\u4e0d\u662f\u4f2a\u88c5\u6210\u201c\u6ca1\u6709\u7528\u91cf\u201d\u3002\n\n## Development\n\n```bash\nnode --check plugins/io.github.muzimu217.usage-dashboard/main.js\nnode --check plugins/io.github.muzimu217.usage-dashboard/lib/aggregate.js\nnode --test tests/usage-dashboard.test.mjs\n```\n",
"safetyNotes": "Read-only aggregation over the host's official usage interface (pi.usage.listTurns). Counts tokens per turn only \u2014 never reads message text, tool arguments, project paths or credentials; keeps at most an 8-character session id prefix; writes nothing outside its own plugin settings; makes no network requests. Covers PI-Desktop's native usage only and reads no other tool's files.",
"versions": [
{
"version": "0.1.0",
"publishedAt": "2026-09-17T16:33:01Z",
"changelog": "Release 0.1.0: first release. Aggregates the full PI-Desktop usage history through the official pi.usage.listTurns contract (365-day windows walked backwards, cursor pagination, turnId dedupe) into a local dashboard: metric cards, 365-day heatmap, model donut, per-model trend lines, top sessions and streaks. Headline tokens are input + output; cache and reasoning tokens stay in a diagnostics line. 0.1.0\uff1a\u9996\u4e2a\u7248\u672c\u3002\u901a\u8fc7\u5b98\u65b9 pi.usage.listTurns \u63a5\u53e3\uff08\u6309 365 \u5929\u7a97\u53e3\u56de\u8d70 + \u6e38\u6807\u7ffb\u9875 + turnId \u53bb\u91cd\uff09\u805a\u5408\u5168\u90e8\u539f\u751f\u7528\u91cf\u5386\u53f2\uff0c\u5448\u73b0\u6307\u6807\u5361\u3001\u70ed\u529b\u56fe\u3001\u6a21\u578b\u73af\u5f62\u56fe\u3001\u5206\u6a21\u578b\u8d8b\u52bf\u7ebf\u3001Top \u4f1a\u8bdd\u4e0e\u8fde\u7eed\u5929\u6570\uff1bheadline \u4ec5\u8ba1\u8f93\u5165+\u8f93\u51fa\uff0c\u7f13\u5b58\u4e0e\u63a8\u7406 token \u5355\u5217\u8bca\u65ad\u884c\u3002",
"minPiDesktop": ">=0.14.3",
"shasum": "ac03b7748e9ae4f51a58ba55c68921ad26cd4fefe5b323a965311e8640e00941",
"url": "packages/io.github.muzimu217.usage-dashboard-0.1.0.piplug",
"sizeBytes": 89310,
"permissions": [
"ui.panel",
"usage.read"
],
"fs": {}
}
]
},
{
"id": "pi.bianqian",
"name": "\u4fbf\u7b7e",
Expand Down
Binary file not shown.
99 changes: 99 additions & 0 deletions plugins/io.github.muzimu217.usage-dashboard/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Usage Dashboard(原生用量看板)

A native PI-Desktop usage dashboard. It is the first consumer of the host's
official read-only usage contract `pi.usage.listTurns` (PI-Desktop host
PR #503) and renders that fact stream as a local board: metric cards, a
365-day activity heatmap, a model-share donut, per-model daily trend lines,
top sessions and activity streaks — in a light and a dark palette.

原生用量看板:PI-Desktop 官方只读用量接口 `pi.usage.listTurns`(宿主 PR #503)
的首个消费者。把官方事实面渲染成本地看板:指标卡、365 天活动热力图、模型占比
环形图、分模型每日趋势线、Top 会话与连续使用天数,深浅两套主题。

## Scope and differentiation / 定位与差异化

- **Official contract only.** All data comes from `pi.usage.listTurns`. The
plugin never reads transcript files, the host database, or any other AI
tool's local data. It covers PI-Desktop's native usage only.
只用正式契约:数据全部来自 `pi.usage.listTurns`,不直读转录本、宿主数据库或
其他工具的本地文件;仅覆盖 PI-Desktop 原生用量。
- Differs from `pi.token-insights`, which aggregates multiple tools and reads
host records directly: this plugin is a thin, permission-minimal renderer of
the official fact interface.
与 pi.token-insights(多工具聚合、直读宿主库)差异化:本插件是正式事实接口
之上的薄渲染层,权限最小化。

## Data channel / 数据通道

The panel bridge is read-only, so the plugin process does all host access
(the pattern `pi.token-insights` established):

1. `onLoad` (and the `usageDashboard.open` command) walks the full history:
windows of 365 days walked backwards from now (`toMs = previous fromMs − 1`),
each window paged by cursor until `nextCursor = null`; the first fully empty
window ends the walk; a `turnId` set dedupes across window seams.
2. The deduped turns go through the pure aggregator `lib/aggregate.js`
(`aggregateCube(turns, nowMs)`, clock injected for testability).
3. The cube is published via `pi.plugin.setSettings({ usageCube, scanState })`;
the panel polls plugin settings and renders. Range switching (7/30/90/365
days) is a renderer-side filter over the cube's daily rows — no second walk.

## Accounting / 聚合口径(v3.1)

- **Headline tokens = `inputTokens + outputTokens`.** Cache read, cache write
and reasoning tokens appear only in a diagnostics line and never enter the
headline. headline 仅计输入 + 输出;缓存读/写与推理 token 只进诊断行。
- Cards: total tokens, turn count, session count (deduped `sessionId`),
peak day (max daily headline + its date), current/longest streak.
- Daily keys are **local-calendar days** built from `endedAt`, so the heatmap
is not shifted by timezone conversion.
- Streaks (v1, simple): a day is active when its headline > 0; the current
streak anchors on today and falls back to yesterday; any one-day gap breaks
a run. 口径最简实现,见 `lib/aggregate.js` 注释。
- The panel recomputes everything that *can* be derived from daily rows for
the selected range (totals, turns, peak, model shares, trend). Session count,
the diagnostics line and the Top-sessions list are all-history figures and
labelled as such — a session spans days, so it cannot be counted from daily
rows without lying.

## Privacy / 隐私

Read-only aggregation over the official interface. Counts only — never message
text, tool arguments, project paths or credentials. At most an 8-character
session id prefix is kept. Nothing is written outside this plugin's own
settings, and no network request is made. Every number stays on this device.

只读聚合、仅计数:不读取消息正文/工具参数/项目路径/凭据,最多保留 8 位
session id 前缀;除自身插件设置外不写任何位置,不发起网络请求。

## Permissions / 权限

- `ui.panel` — the dashboard panel
- `usage.read` — read aggregate token usage through the host's official interface

## Panel

- 7/30/90/365-day range switch (renderer-side filtering), language follows the
system with an EN/中文 override, theme follows `prefers-color-scheme` with a
light/dark override (both persisted locally).
- Charts are hand-rolled SVG: week-column heatmap with month axis and legend,
donut with center total and a two-column legend, daily trend with per-model
toggleable lines.
- Accessibility: `sr-only` data tables twin the heatmap and the trend chart,
the donut carries an aria-label summarizing the shares, and all controls are
focusable buttons.

## Host requirement / 宿主要求

`pi.usage.listTurns` is provided by PI-Desktop (host PR #503). On builds
without it the panel shows an explicit "host interface not available" state
instead of an empty board. 面板对未提供该接口的旧版本宿主显示明确提示,
而不是伪装成“没有用量”。

## Development

```bash
node --check plugins/io.github.muzimu217.usage-dashboard/main.js
node --check plugins/io.github.muzimu217.usage-dashboard/lib/aggregate.js
node --test tests/usage-dashboard.test.mjs
```
Loading
Loading