diff --git a/plugins/yanhy2000/mcode-usage-monitor/.minimax-plugin/plugin.json b/plugins/yanhy2000/mcode-usage-monitor/.minimax-plugin/plugin.json index bc1f7f0..ae5ed3d 100644 --- a/plugins/yanhy2000/mcode-usage-monitor/.minimax-plugin/plugin.json +++ b/plugins/yanhy2000/mcode-usage-monitor/.minimax-plugin/plugin.json @@ -2,16 +2,20 @@ "schemaVersion": 1, "name": "mcode-usage-monitor", "displayName": "Token 用量看板", - "version": "1.2.0", + "version": "1.3.0", "description": "统计本机 MiniMax Code 会话的 token 消耗,可按模型和会话筛选。", "author": "yanhy2000", "icon": "icon.png", "category": "Business", "exampleQueries": [ "打开 Token 用量看板", - "看看 MiniMax Code 最近 24 小时的 token 用量" + "查一下这个对话用了多少 token", + "统计最近 7 天的 token 消耗与缓存命中率", + "看看历史上哪个模型用得最多" ], "apps": [], "mcpServers": [], - "skills": [] + "skills": [ + "skills/usage-query/SKILL.md" + ] } diff --git a/plugins/yanhy2000/mcode-usage-monitor/README.md b/plugins/yanhy2000/mcode-usage-monitor/README.md index e346115..afd3642 100644 --- a/plugins/yanhy2000/mcode-usage-monitor/README.md +++ b/plugins/yanhy2000/mcode-usage-monitor/README.md @@ -4,7 +4,7 @@ English | [简体中文](README.zh-CN.md) Watch Token usage from your local MiniMax Code runtime in near real time: consumption over time, output speed, cache hit rate, per-model comparison, per-project breakdown, tool-call stats, and recent requests. Filter by time range, model, and session. -Author: [yanhy2000](https://github.com/yanhy2000) · Version: `1.2.0` +Author: [yanhy2000](https://github.com/yanhy2000) · Version: `1.3.0`  @@ -26,9 +26,11 @@ Do not leave out the hidden `.minimax-plugin` directory; the plugin needs it to Then restart a version of MiniMax Code that supports MiniApps, confirm that the plugin is enabled, and open "Token 用量看板" or ask the Agent to open it. If MiniMax Code uses a custom data directory (`MINIMAX_DATA_DIR`), put it under `plugins/` there instead. -The page opens on the last 24 hours. You can switch between today (since local midnight), 1 hour, 12 hours, 24 hours, 7 days, 30 days, and all time, or enter a custom whole-hour range (1–8760 hours; only the most recent entry is kept). Model and session filtering is multi-select, and the session list only lists sessions with usage inside the selected range. The per-project breakdown groups usage by session workspace directory, and tool-call stats come from the tool calls recorded per request; both follow the current filters. Every card can be collapsed or expanded, and a collapsed card can be dragged to reorder (expanded cards cannot). The "重置布局" button at the top restores all cards to expanded and the default order without touching the filters. Filters, time range, refresh interval, theme, card collapse state, and card order are remembered between visits. The page refreshes every 10 seconds by default (5 s / 10 s / 30 s / manual). +The page opens on the last 24 hours. You can switch between today (since local midnight), 1 hour, 12 hours, 24 hours, 7 days, 30 days, and all time, or enter a custom whole-hour range (1–8760 hours; only the most recent entry is kept). Model and session filtering is multi-select, and the two lists constrain each other: the model list only shows models that appear in the selected sessions, and the session list only shows sessions that used the selected models; a side shows its full range list only when the other side is set to all. The select-all action is dimmed only when everything is selected, invert is always available, and selecting nothing is allowed (the page then shows zero data). The per-project breakdown groups usage by session workspace directory, and tool-call stats come from the tool calls recorded per request; both follow the current filters. Every card can be collapsed or expanded. Clicking "编辑布局" enters edit mode: the KPI tiles at the top and the cards below (collapsed or not) can be reordered by dragging their ⠿ handle and removed with ✕. "保存布局" exits and remembers the arrangement, "取消保存" discards the changes, and "重置布局" restores everything shown in the default order — none of these touch the filters. Filters, time range, refresh interval, theme, and the visibility and order of tiles and cards are remembered between visits. The page refreshes every 10 seconds by default (5 s / 10 s / 30 s / manual). -This app requires **Python 3.8+** on the local machine to read the local database. It uses only the standard library, so no `pip install` is needed. No API key or other configuration is required. +This app requires **Python 3.8+** on the local machine to read the local database. It uses only the standard library, so no `pip install` is needed. The bundled SQLite must have the JSON1 extension enabled (the default for SQLite 3.38+ and all standard Python builds; the page reports the exact cause if it is missing). No API key or other configuration is required. + +The package also ships one Agent skill (`skills/usage-query/`). When a usage question comes up in a conversation — "how many tokens did this chat use?", "which model do I use most?" — the Agent can run the bundled data backend (`miniapp/node/api.py`) directly and answer, without opening the dashboard first. That path is the same read-only snapshot as the page and needs the same local Python. Open the dashboard itself when you want charts, or want to switch filters and rearrange the layout yourself. ## Data access and counting @@ -50,7 +52,7 @@ The runtime sends nothing to external services and has no telemetry. It writes a The page is in `miniapp/client/index.html` (ECharts is bundled locally), the Node entry is `miniapp/node/server.mjs`, and the data backend is `miniapp/node/api.py`. No build step is required. -Verified environment: MiniMax Code desktop `3.0.73.166` on Windows (10.0.26200, x64). Verified during development: plugin install and open, aggregation and de-duplication, model/session filtering, preference persistence, auto refresh, theme switching, chart and table rendering, time-range presets and custom-range validation, select-all/invert gating, and card collapse and drag reordering. macOS and Linux are unverified. +Verified environment: MiniMax Code desktop `3.1.0` on Windows (10.0.26200, x64). Verified during development: plugin install and open, aggregation and de-duplication, model/session filtering, preference persistence, auto refresh, theme switching, chart and table rendering, time-range presets with custom-range validation, selecting-no-filter, layout editing (drag reordering and removal), the icon-only header controls, the recent-calls table tweaks, cross-filtering between the model and session lists (narrowing in both directions), the model comparison card empty state, Esc closing dropdown panels, atomic preference writes, and the bundled Agent skill answering usage questions from the data backend. The dashboard and the bundled skill were re-tested on the `3.1.0` release. macOS and Linux are unverified. Third-party components: [ECharts](https://echarts.apache.org/) (Apache License 2.0), bundled locally for offline use. diff --git a/plugins/yanhy2000/mcode-usage-monitor/README.zh-CN.md b/plugins/yanhy2000/mcode-usage-monitor/README.zh-CN.md index d9d49da..85c92a4 100644 --- a/plugins/yanhy2000/mcode-usage-monitor/README.zh-CN.md +++ b/plugins/yanhy2000/mcode-usage-monitor/README.zh-CN.md @@ -4,7 +4,7 @@ 近实时查看本机 MiniMax Code 的 Token 用量:消耗趋势、输出速度、缓存命中率、模型对比、项目用量排行、工具调用统计和最近请求,可按时间范围、模型和会话筛选。 -作者:[yanhy2000](https://github.com/yanhy2000) · 版本:`1.2.0` +作者:[yanhy2000](https://github.com/yanhy2000) · 版本:`1.3.0`  @@ -26,9 +26,11 @@ 完成后重启支持 MiniApp 的 MiniMax Code,确认插件已启用,然后打开「Token 用量看板」,或在对话里让 Agent 打开。如果给 MiniMax Code 设置了自定义数据目录(环境变量 `MINIMAX_DATA_DIR`),则放进该目录下的 `plugins/` 里。 -页面默认显示最近 24 小时,可切换今天(自当天 00:00 起)/ 1 小时 / 12 小时 / 24 小时 / 7 天 / 30 天 / 全部,也可输入整数小时自定义范围(1–8760 小时,只保留最近输入的一条)。支持按模型和会话多选筛选,会话列表只列出所选范围内有用量的会话;项目用量排行按会话工作区目录汇总,工具调用统计来自每次请求的工具调用记录,两者都跟随当前筛选。每张卡片可收起 / 展开,收起后可拖拽调整排列顺序(展开时不可拖动);顶部「重置布局」可一键恢复全部展开与默认排列,不影响上方的筛选条件。筛选条件、时间范围、刷新间隔、主题、卡片收起与排列都会被记住。页面默认每 10 秒自动刷新(可切换 5 秒 / 10 秒 / 30 秒或手动刷新)。 +页面默认显示最近 24 小时,可切换今天(自当天 00:00 起)/ 1 小时 / 12 小时 / 24 小时 / 7 天 / 30 天 / 全部,也可输入整数小时自定义范围(1–8760 小时,只保留最近输入的一条)。支持按模型和会话多选筛选,两侧列表互相牵制:模型列表只显示所选会话中出现过的模型,会话列表只显示所选模型出现过的会话,对应一侧选「全部」时才展示该侧完整列表;全选按钮在全部选中时置灰,反选始终可用,也允许全部不选(此时页面数据为 0)。项目用量排行按会话工作区目录汇总,工具调用统计来自每次请求的工具调用记录,两者都跟随当前筛选。每张卡片可收起 / 展开;点击「编辑布局」进入编辑模式后,顶部数字方块与下方卡片(无论展开还是收起)都可以拖拽排序,也可以点 ✕ 从页面移除,「保存布局」退出并记住排列,「取消保存」放弃本次修改,「重置布局」一键恢复全部显示与默认排列,均不影响上方的筛选条件。筛选条件、时间范围、刷新间隔、主题、方块与卡片的显示和排列都会被记住。页面默认每 10 秒自动刷新(可切换 5 秒 / 10 秒 / 30 秒或手动刷新)。 -本插件需要本机已安装 **Python 3.8+** 用于读取本地数据库;只用标准库,无需 `pip install`。不需要 API Key 或其他配置。 +本插件需要本机已安装 **Python 3.8+** 用于读取本地数据库;只用标准库,无需 `pip install`。其自带 SQLite 需启用 JSON1 扩展(SQLite 3.38+ 与 Python 官方构建均默认启用,缺失时页面会提示具体原因)。不需要 API Key 或其他配置。 + +插件还内置一个 Agent 技能(`skills/usage-query/`):在对话里问到用量类问题(如「这个对话用了多少 token」「哪个模型用得最多」)时,Agent 可以直接调用打包的数据后端(`miniapp/node/api.py`)查询后回答,不必先打开看板;这条查询路径与页面一样是只读快照,依赖同样的本机 Python。想看趋势图或自己切换筛选、调整布局时,再打开看板页面。 ## 数据与统计口径 @@ -50,7 +52,7 @@ 页面位于 `miniapp/client/index.html`(ECharts 已本地化打包),Node 入口位于 `miniapp/node/server.mjs`,数据后端位于 `miniapp/node/api.py`,无需构建。 -已验证环境:MiniMax Code 桌面端 `3.0.73.166`,Windows(10.0.26200,x64)。开发过程中已验证:插件安装与打开、数据聚合与去重、模型/会话筛选、偏好记忆、自动刷新、主题切换、图表与列表渲染、时间范围预设与自定义输入校验、全选/反选置灰、卡片收起与拖拽排序。macOS 与 Linux 未验证。 +已验证环境:MiniMax Code 桌面端 `3.1.0` 正式版,Windows(10.0.26200,x64)。开发过程中已验证:插件安装与打开、数据聚合与去重、模型/会话筛选、偏好记忆、自动刷新、主题切换、图表与列表渲染、时间范围预设与自定义输入校验、筛选全不选、编辑布局(拖拽排序与移除)、顶栏图标化与最近调用表格优化、模型与会话列表交叉筛选(双向收窄)、模型对比卡空态提示、Esc 收起下拉面板、偏好文件原子写入、Agent 通过内置技能直接查询用量;看板与内置技能在 `3.1.0` 正式版下复测通过。macOS 与 Linux 未验证。 第三方组件:[ECharts](https://echarts.apache.org/)(Apache License 2.0),已本地化打包以便离线使用。 diff --git a/plugins/yanhy2000/mcode-usage-monitor/docs/preview.png b/plugins/yanhy2000/mcode-usage-monitor/docs/preview.png index ca0ef0c..06d5ae3 100644 Binary files a/plugins/yanhy2000/mcode-usage-monitor/docs/preview.png and b/plugins/yanhy2000/mcode-usage-monitor/docs/preview.png differ diff --git a/plugins/yanhy2000/mcode-usage-monitor/miniapp/client/index.html b/plugins/yanhy2000/mcode-usage-monitor/miniapp/client/index.html index d5501d4..5b51271 100644 --- a/plugins/yanhy2000/mcode-usage-monitor/miniapp/client/index.html +++ b/plugins/yanhy2000/mcode-usage-monitor/miniapp/client/index.html @@ -31,13 +31,24 @@ Roboto,Helvetica,Arial,sans-serif; max-width:1240px;margin:0 auto; padding:clamp(12px,2vw,20px) clamp(14px,3vw,24px) 40px} -header{display:flex;align-items:flex-start;justify-content:space-between; - gap:12px 16px;flex-wrap:wrap;margin-bottom:16px} +header{display:flex;flex-direction:column;align-items:stretch;gap:8px; + margin-bottom:16px} +.htop{display:flex;align-items:flex-start;justify-content:space-between; + gap:12px;flex-wrap:wrap} .hgroup{display:flex;flex-direction:column;gap:2px} h1{font-size:18px;font-weight:600;line-height:28px} .sub{color:var(--mcode-text-muted);font-size:12px;line-height:16px} #dbinfo{color:var(--mcode-text-subtle)} -.hctl{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding-top:2px} +/* 标题行右端的裸图标组: 无边框按钮, hover 才有淡背景 */ +.hicons{display:flex;align-items:center;gap:2px} +.hicons button{background:transparent;border:0;width:28px;height:28px;padding:0; + display:inline-flex;align-items:center;justify-content:center; + border-radius:7px;color:var(--mcode-text-muted)} +.hicons button:hover{background:var(--mcode-bg-secondary); + color:var(--mcode-text)} +.hicons button:focus-visible{outline:2px solid var(--mcode-accent);outline-offset:1px} +.hicons .dd{position:relative} +.hctl{display:flex;align-items:center;gap:10px;flex-wrap:wrap} .spacer{flex:1} select,button{background:var(--mcode-bg-secondary);color:var(--mcode-text); border:1px solid var(--mcode-border);border-radius:8px;padding:4px 10px; @@ -53,7 +64,10 @@ /* --- 多选下拉 --- */ .dd{position:relative} -.ddbtn{display:flex;align-items:center;gap:7px;min-width:150px;justify-content:space-between} +.ddbtn{display:flex;align-items:center;gap:7px;min-width:150px;max-width:240px; + justify-content:space-between} +.ddbtn>span:first-child{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis; + white-space:nowrap;text-align:left} .ddbtn .cnt{color:var(--mcode-accent);font-weight:600} .ddbtn .caret{color:var(--mcode-text-muted);display:inline-flex} .ddpanel{position:absolute;top:calc(100% + 6px);left:0;z-index:50; @@ -75,6 +89,17 @@ .ddrow .mname{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} .ddrow .mmeta{color:var(--mcode-text-muted);font-size:11px;font-variant-numeric:tabular-nums} .swatch{width:9px;height:9px;border-radius:2px;flex:none} +/* 图标按钮通用尺寸(标题行右端使用, 外观由 .hicons 定义) */ +.iconbtn{width:28px;min-width:28px;padding:0;display:inline-flex; + align-items:center;justify-content:center} +.iconbtn svg{flex:none} +/* 加 .ddpanel 前缀提高优先级: 面板位于 .hicons 内, 否则被 .hicons button 的 + 28px 图标规则压成小块横排 */ +.ddpanel .ivrow{display:block;width:100%;padding:6px 10px;border:0;background:transparent; + color:var(--mcode-text);font-size:13px;font-family:inherit;cursor:pointer; + border-radius:8px;text-align:left;height:auto} +.ddpanel .ivrow:hover{background:var(--mcode-bg-secondary)} +.ddpanel .ivrow.on{color:var(--mcode-accent);font-weight:600} .kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:12px;margin-bottom:16px} @@ -84,8 +109,10 @@ .kpi .value{font-size:22px;font-weight:600;font-variant-numeric:tabular-nums} .kpi .hint{color:var(--mcode-text-muted);font-size:11px;margin-top:2px} section.panel{background:var(--mcode-surface);border:1px solid var(--mcode-border); - border-radius:12px;padding:14px 16px;margin-bottom:16px} -.phead{display:flex;align-items:center;gap:10px;margin-bottom:10px;flex-wrap:wrap} + border-radius:12px;padding:14px 16px;margin-bottom:16px;position:relative} +.phead{display:flex;align-items:center;gap:10px;margin-bottom:10px;flex-wrap:wrap; + padding-right:40px} /* 右上角收起按钮占位 */ +body.editing .phead{padding-right:100px} /* 编辑模式三个按钮占位 */ .panel h2{font-size:15px;font-weight:500;color:var(--mcode-text)} .toggle{display:flex;border:1px solid var(--mcode-border);border-radius:8px;overflow:hidden} .toggle button{border:0;border-radius:0;background:transparent;padding:3px 10px; @@ -102,36 +129,49 @@ #cards{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px} #cards>section.panel{margin-bottom:0;grid-column:1/-1;min-width:0} #cards>section.panel.narrow{grid-column:span 1} -/* 卡片收起按钮与拖拽态 */ -.cbtns{display:flex;align-items:center;gap:2px;margin-left:auto} +/* 卡片收起/编辑按钮: 固定在卡片右上角, 不受头部内容换行影响 */ +.cbtns{display:flex;align-items:center;gap:2px;position:absolute;top:12px;right:12px} .cbtn{padding:0;width:24px;height:24px;display:inline-flex;align-items:center; justify-content:center;background:transparent;border-color:transparent; border-radius:6px;color:var(--mcode-text-muted)} .cbtn:hover{background:var(--mcode-bg-secondary);border-color:var(--mcode-border); color:var(--mcode-text)} +.cbtn.grab{cursor:grab} .cbtn .chev{transition:transform .15s} .panel.collapsed .cbtn .chev{transform:rotate(-90deg)} -.draghint{display:none;color:var(--mcode-text-subtle);font-size:12px;letter-spacing:0; - user-select:none;cursor:grab} -.panel.collapsed .draghint{display:inline} .panel.collapsed .phead{margin-bottom:0} -.panel.collapsed{cursor:grab} +/* 收起态头部保留标题与简介(便于识别卡片), 只收起切换按钮与动态统计小字 */ +.panel.collapsed .phead .toggle, +.panel.collapsed .phead #bucketInfo{display:none!important} .panel.dragging{opacity:.55;cursor:grabbing} +/* 编辑布局模式: KPI 方块与卡片出现拖拽/移除按钮, 重置按钮让位给取消按钮 */ +.kpi{position:relative} +.kbtns{position:absolute;top:5px;right:5px;display:none;align-items:center;gap:2px} +body.editing .kbtns{display:flex} +.cbtns .ebtn,#layoutCancel{display:none} +body.editing .cbtns .ebtn{display:inline-flex} +body.editing #layoutCancel{display:inline-block} +body.editing #layoutReset{display:none} +.kpi.dragging{opacity:.55} +.kpi.drop-before{box-shadow:inset 0 3px 0 var(--mcode-accent)} +.kpi.drop-after{box-shadow:inset 0 -3px 0 var(--mcode-accent)} +#kpis:empty{display:none} /* KPI 删光时隐藏占位容器 */ .panel.drop-before{box-shadow:inset 0 3px 0 var(--mcode-accent)} .panel.drop-after{box-shadow:inset 0 -3px 0 var(--mcode-accent)} body.dragging-card{user-select:none;cursor:grabbing} .empty{padding:40px;text-align:center;color:var(--mcode-text-muted);font-size:13px} table{width:100%;border-collapse:collapse;font-size:13px; font-variant-numeric:tabular-nums} -th{color:var(--mcode-text-muted);font-weight:500;text-align:right;padding:6px 10px; +th{color:var(--mcode-text-muted);font-weight:500;text-align:center;padding:6px 10px; border-bottom:1px solid var(--mcode-border);position:sticky;top:0; - background:var(--mcode-surface)} -th:first-child,td:first-child{text-align:left} + background:var(--mcode-surface);white-space:nowrap} /* 表头不换行, 列宽随内容自适应 */ td{text-align:right;padding:5px 10px;border-bottom:1px solid var(--mcode-border); - color:var(--mcode-text)} + color:var(--mcode-text);white-space:nowrap} +td:first-child,td:nth-child(2),td:nth-child(3){text-align:left} /* 时间/模型/会话 */ tr:hover td{background:var(--mcode-bg-secondary)} .tag{display:inline-block;padding:1px 8px;border-radius:10px;font-size:12px; - border:1px solid var(--mcode-border);white-space:nowrap} + border:1px solid var(--mcode-border);white-space:nowrap;max-width:160px; + overflow:hidden;text-overflow:ellipsis;vertical-align:middle} .tablewrap{max-height:420px;overflow:auto} .tfoot{color:var(--mcode-text-muted);font-size:12px;text-align:center;padding:8px 2px 2px} footer{color:var(--mcode-text-muted);font-size:12px;text-align:center;margin-top:8px} @@ -139,7 +179,8 @@ footer a:hover{text-decoration:underline} #toast{position:fixed;top:14px;right:14px;background:var(--mcode-bg-secondary); border:1px solid var(--mcode-danger);color:var(--mcode-danger);border-radius:8px; - padding:8px 14px;font-size:13px;display:none;max-width:420px;z-index:99} + padding:8px 14px;font-size:13px;display:none;max-width:420px;z-index:99; + pointer-events:none} /* 纯通知, 不拦截其下顶栏图标的点击 */ /* ---------- 响应式 ---------- */ @media (max-width:720px){ @@ -165,10 +206,27 @@
统计本机 MiniMax Code 会话的 token 消耗,可按模型和会话筛选
-正在读取本地用量数据…
+统计本机 MiniMax Code 会话的 token 消耗,可按模型和会话筛选
+正在读取本地用量数据…
+