opencode-tokenwatch 0.4.0 → 0.6.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.en.md +32 -38
- package/README.md +33 -39
- package/dist/host/command-actions.d.ts +20 -0
- package/dist/host/runtime.d.ts +7 -0
- package/dist/host/types.d.ts +148 -0
- package/dist/host/v1/adapter.d.ts +9 -0
- package/dist/host/v1/commands.d.ts +9 -0
- package/dist/{queries.d.ts → host/v1/data-source.d.ts} +1 -6
- package/dist/host/v2/adapter.d.ts +63 -0
- package/dist/host/v2/commands.d.ts +8 -0
- package/dist/host/v2/data-source.d.ts +26 -0
- package/dist/kernel/config.d.ts +33 -0
- package/dist/{formatter.d.ts → kernel/format.d.ts} +9 -10
- package/dist/kernel/model.d.ts +19 -0
- package/dist/kernel/perf-aggregate.d.ts +62 -0
- package/dist/{perf-tracker.d.ts → kernel/perf.d.ts} +13 -7
- package/dist/{generate-usage-html.d.ts → kernel/report-html.d.ts} +1 -1
- package/dist/kernel/report.d.ts +19 -0
- package/dist/{stats-store.d.ts → kernel/store.d.ts} +5 -5
- package/dist/server.d.ts +15 -0
- package/dist/server.js +15 -0
- package/dist/tui.d.ts +11 -12
- package/dist/tui.js +4802 -0
- package/dist/ui/sidebar.d.ts +14 -0
- package/index.ts +11 -0
- package/package.json +39 -12
- package/tui.ts +9 -0
- package/dist/commands.d.ts +0 -2
- package/dist/commands.js +0 -208
- package/dist/commands.jsx +0 -381
- package/dist/formatter.js +0 -289
- package/dist/generate-usage-html.js +0 -962
- package/dist/i18n.js +0 -173
- package/dist/index.d.ts +0 -5
- package/dist/index.js +0 -7
- package/dist/perf-tracker.js +0 -299
- package/dist/queries.js +0 -393
- package/dist/sidebar.d.ts +0 -22
- package/dist/sidebar.jsx +0 -604
- package/dist/stats-store.js +0 -258
- package/dist/tui.jsx +0 -178
- /package/dist/{i18n.d.ts → kernel/i18n.d.ts} +0 -0
package/README.en.md
CHANGED
|
@@ -4,29 +4,33 @@
|
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
A terminal monitoring plugin for OpenCode CLI that displays real-time token usage, cache hit rate, and model throughput in the sidebar, with one-click interactive browser reports.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
---
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
- **Cache hit rate** — Per-model tracking with trend indicators (↑/↓) and global weighted total
|
|
13
|
-
- **Performance metrics** — TTFT / TPS / End-to-end latency + P50/P95/P99 latency percentiles
|
|
14
|
-
- **Token distribution** — Per-role breakdown (system / user / tool / output)
|
|
15
|
-
- **Cost tracking** — Per-model cost display (requires provider billing data)
|
|
16
|
-
- **Error rate tracking** — Detects failed requests and computes real-time error rate
|
|
17
|
-
- **`/usage` command** — HTML Report → JSON Export → Text Report → Settings
|
|
18
|
-
- **HTML report** — Interactive ECharts dashboard: token trends, performance comparison, TPS ranking, error rate analysis — auto-opened in browser
|
|
19
|
-
- **Persistent stats** — Performance metrics accumulate forever in a dedicated file, unaffected by session resets
|
|
20
|
-
- **Multi-level collapse** — Panel, models, and sub-blocks collapsible with persisted state
|
|
21
|
-
- **Language switching** — Auto-detect or manually switch between Chinese and English
|
|
11
|
+
## Key Features
|
|
22
12
|
|
|
23
|
-
|
|
13
|
+
- **Real-time Sidebar Dashboard**: Check input/output tokens, estimated cost, cache hit rate, and average TTFT & generation speed per model during sessions.
|
|
14
|
+
- **Cache Hit Rate & Trends**: Track cache efficiency per model, with arrows showing whether the hit rate for recent requests is trending up or down.
|
|
15
|
+
- **Token Breakdown**: Automatically breaks down tokens into system prompt, user input, tool execution, and assistant output to identify major context consumers.
|
|
16
|
+
- **Interactive Usage Reports**: Type `/usage` in the terminal to launch an ECharts interactive dashboard in your browser with historical trends, cross-model comparisons, and failed request stats; export to JSON or Markdown with one click.
|
|
17
|
+
- **Dual-Host Compatibility**: Seamlessly works with both OpenCode 1.x and OpenCode 2 (beta). Automatically detects the host environment with zero manual configuration.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Installation & Setup
|
|
22
|
+
|
|
23
|
+
### 1. Install
|
|
24
|
+
|
|
25
|
+
Run in your project directory:
|
|
24
26
|
|
|
25
27
|
```sh
|
|
26
28
|
npm install opencode-tokenwatch
|
|
27
29
|
```
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
### 2. Enable Plugin
|
|
32
|
+
|
|
33
|
+
Add the plugin entry to `opencode.json` (or `opencode.jsonc`):
|
|
30
34
|
|
|
31
35
|
```json
|
|
32
36
|
{
|
|
@@ -35,45 +39,35 @@ Add to `opencode.json` or `opencode.jsonc`:
|
|
|
35
39
|
}
|
|
36
40
|
```
|
|
37
41
|
|
|
38
|
-
|
|
42
|
+
Restart OpenCode and the TokenWatch panel will load in the sidebar automatically.
|
|
39
43
|
|
|
40
|
-
|
|
44
|
+
---
|
|
41
45
|
|
|
42
46
|
## Usage
|
|
43
47
|
|
|
44
|
-
|
|
48
|
+
Type `/usage` in the terminal input to open the action menu:
|
|
45
49
|
|
|
46
|
-
- **HTML Report
|
|
47
|
-
- **
|
|
48
|
-
- **
|
|
49
|
-
- **Settings** — Toggle sidebar blocks, switch language
|
|
50
|
+
- **HTML Report**: Select a date range to generate and open an interactive chart in your default browser.
|
|
51
|
+
- **Export Data**: Export structured JSON or Markdown reports, saved to `~/.opencode/reports/` by default.
|
|
52
|
+
- **Settings**: Interactively toggle sidebar sections or switch language in the popup menu. Changes are saved automatically without editing configuration files.
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
| File | Path | Description |
|
|
54
|
-
|------|------|-------------|
|
|
55
|
-
| JSONL log | `~/.opencode/tokenwatch.jsonl` | Raw per-request log |
|
|
56
|
-
| Aggregated stats | `~/.opencode/tokenwatch-stats.json` | Persistent performance stats |
|
|
57
|
-
| Report output | `~/.opencode/reports/` | HTML / JSON / Markdown reports |
|
|
54
|
+
---
|
|
58
55
|
|
|
59
56
|
## Requirements
|
|
60
57
|
|
|
61
|
-
- OpenCode CLI (
|
|
62
|
-
- Node.js 18
|
|
58
|
+
- **OpenCode CLI**: 1.18+ or OpenCode 2 (`@opencode-ai/cli@beta`)
|
|
59
|
+
- **Node.js**: ≥ 18.0.0
|
|
63
60
|
|
|
64
|
-
|
|
61
|
+
---
|
|
65
62
|
|
|
66
|
-
|
|
67
|
-
npm install
|
|
68
|
-
npm run build
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
## Related
|
|
63
|
+
## Related Projects
|
|
72
64
|
|
|
73
65
|
- [opencode-throughput](https://github.com/Howardzhangdqs/opencode-throughput) — Real-time LLM performance monitoring (TTFT/TPS/latency/cost)
|
|
74
66
|
- [opencode-visual-cache](https://github.com/Hotakus/opencode-visual-cache) — TUI sidebar cache hit rate visualization, token distribution analysis
|
|
75
67
|
- [magic-context](https://github.com/cortexkit/magic-context/) — Cache-aware infinite context + cross-session memory system
|
|
76
68
|
|
|
69
|
+
---
|
|
70
|
+
|
|
77
71
|
## License
|
|
78
72
|
|
|
79
73
|
MIT
|
package/README.md
CHANGED
|
@@ -4,29 +4,33 @@
|
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|
|
|
7
|
-
OpenCode CLI
|
|
7
|
+
OpenCode CLI的终端监控插件,在侧边栏实时显示Token用量、缓存命中率与模型响应速度,并支持一键生成网页版可视化用量报表。
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
---
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
- **缓存命中率** — 按模型实时追踪,带趋势指示器(↑/↓)与全局加权总计
|
|
13
|
-
- **性能指标** — TTFT / TPS / 端到端延迟,含 P50/P95/P99 延迟分位数
|
|
14
|
-
- **Token 分布** — 按角色(system / user / tool / output)分解用量占比
|
|
15
|
-
- **成本追踪** — 按模型实时显示请求成本(需 provider 返回计费数据)
|
|
16
|
-
- **错误率统计** — 识别并统计失败请求,实时计算错误率
|
|
17
|
-
- **`/usage` 命令** — HTML 报告 → JSON 导出 → 文本报告 → 设置
|
|
18
|
-
- **HTML 报告** — 交互式 ECharts 仪表盘:Token 趋势、性能对比、TPS 排名、错误率分析,自动在浏览器打开
|
|
19
|
-
- **持久化统计** — 性能指标永久累积写入独立文件,历史数据不受会话重置影响
|
|
20
|
-
- **多级折叠** — 面板、模型、子区块均可折叠,状态持久化
|
|
21
|
-
- **语言切换** — 中英双语,跟随系统或手动切换
|
|
11
|
+
## 主要特性
|
|
22
12
|
|
|
23
|
-
|
|
13
|
+
- **侧边栏实时看板**:会话中随时查看各模型的输入输出Token、预估花费、缓存命中率,以及当前会话的平均首字延迟与生成速率。
|
|
14
|
+
- **缓存命中率与趋势**:按模型追踪缓存利用效率,用箭头标注近几次请求的命中率是在上升还是下降。
|
|
15
|
+
- **Token构成归类**:自动将Token按系统提示词、用户输入、工具调用和模型输出分别统计,清楚定位上下文占用大头。
|
|
16
|
+
- **交互式用量报告**:在终端输入 `/usage` 即可在浏览器中打开ECharts 交互仪表盘,查看历史用量趋势、模型横向对比与失败请求统计;也可一键导出为JSON或Markdown文件。
|
|
17
|
+
- **双版本无缝适配**:同时兼容 OpenCode 1.x 与 OpenCode 2(beta),自动识别宿主环境,无需手动切换或调整配置。
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 安装与配置
|
|
22
|
+
|
|
23
|
+
### 1. 安装
|
|
24
|
+
|
|
25
|
+
在项目中运行:
|
|
24
26
|
|
|
25
27
|
```sh
|
|
26
28
|
npm install opencode-tokenwatch
|
|
27
29
|
```
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
### 2. 启用插件
|
|
32
|
+
|
|
33
|
+
在配置文件 `opencode.json`(或 `opencode.jsonc`)中添加插件项:
|
|
30
34
|
|
|
31
35
|
```json
|
|
32
36
|
{
|
|
@@ -35,38 +39,26 @@ npm install opencode-tokenwatch
|
|
|
35
39
|
}
|
|
36
40
|
```
|
|
37
41
|
|
|
38
|
-
|
|
42
|
+
重新打开 OpenCode,侧边栏便会自动载入 TokenWatch 面板。
|
|
39
43
|
|
|
40
|
-
|
|
44
|
+
---
|
|
41
45
|
|
|
42
|
-
##
|
|
46
|
+
## 常用操作
|
|
43
47
|
|
|
44
|
-
|
|
48
|
+
在终端对话框中输入 `/usage` 即可唤出操作菜单:
|
|
45
49
|
|
|
46
|
-
- **HTML
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
- **设置** — 开关侧边栏显示项、切换语言
|
|
50
|
+
- **HTML 报告**:选择统计日期范围,在默认浏览器中打开交互式图表。
|
|
51
|
+
- **导出数据**:导出结构化 JSON 或 Markdown 报告,默认保存在 `~/.opencode/reports/` 目录下。
|
|
52
|
+
- **设置**:在弹出的交互菜单中开关各个显示区块或切换中英文显示。改动会自动保存,不需要手动修改配置文件。
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
| 文件 | 路径 | 说明 |
|
|
54
|
-
|------|------|------|
|
|
55
|
-
| JSONL 日志 | `~/.opencode/tokenwatch.jsonl` | 原始请求日志 |
|
|
56
|
-
| 聚合统计 | `~/.opencode/tokenwatch-stats.json` | 持久化性能统计 |
|
|
57
|
-
| 报告输出 | `~/.opencode/reports/` | HTML / JSON / Markdown 报告 |
|
|
54
|
+
---
|
|
58
55
|
|
|
59
56
|
## 系统要求
|
|
60
57
|
|
|
61
|
-
- OpenCode CLI
|
|
62
|
-
- Node.js 18
|
|
58
|
+
- **OpenCode CLI**:1.18+ 或 OpenCode 2(`@opencode-ai/cli@beta`)
|
|
59
|
+
- **Node.js**:≥ 18.0.0
|
|
63
60
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
```sh
|
|
67
|
-
npm install
|
|
68
|
-
npm run build
|
|
69
|
-
```
|
|
61
|
+
---
|
|
70
62
|
|
|
71
63
|
## 相关项目
|
|
72
64
|
|
|
@@ -74,6 +66,8 @@ npm run build
|
|
|
74
66
|
- [opencode-visual-cache](https://github.com/Hotakus/opencode-visual-cache) — TUI 侧边栏缓存命中率可视化,Token 分布分析
|
|
75
67
|
- [magic-context](https://github.com/cortexkit/magic-context/) — 缓存感知的无限上下文 + 跨会话记忆系统
|
|
76
68
|
|
|
77
|
-
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 协议
|
|
78
72
|
|
|
79
73
|
MIT
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { HostAdapter } from "./types.js";
|
|
2
|
+
import type { SidebarConfig, SidebarToggleKey } from "../kernel/config.js";
|
|
3
|
+
import type { UsageFilters } from "../kernel/format.js";
|
|
4
|
+
export interface CommandActions {
|
|
5
|
+
htmlReport(filters?: UsageFilters, presetTag?: string): Promise<void>;
|
|
6
|
+
jsonExport(): Promise<void>;
|
|
7
|
+
textReport(): Promise<void>;
|
|
8
|
+
config(): SidebarConfig;
|
|
9
|
+
toggle(key: SidebarToggleKey): SidebarConfig;
|
|
10
|
+
setLanguage(language: SidebarConfig["language"]): SidebarConfig;
|
|
11
|
+
}
|
|
12
|
+
export declare function createCommandActions(host: HostAdapter): CommandActions;
|
|
13
|
+
/** 报告时间范围预设,供两代宿主菜单共用 */
|
|
14
|
+
export interface RangePreset {
|
|
15
|
+
readonly id: string;
|
|
16
|
+
readonly label: string;
|
|
17
|
+
readonly tag: string;
|
|
18
|
+
readonly filters: UsageFilters;
|
|
19
|
+
}
|
|
20
|
+
export declare function rangePresets(): RangePreset[];
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 宿主无关的能力契约。
|
|
3
|
+
*
|
|
4
|
+
* v1(opencode 1.x,TuiPluginApi)与 v2(opencode2,Plugin Context)在
|
|
5
|
+
* 事件模型、插槽命名、主题结构、命令注册、存储 API 上全都不同。
|
|
6
|
+
* 本文件定义两者共同能力的交集,由各自的 adapter 负责映射。
|
|
7
|
+
*
|
|
8
|
+
* 上层 UI / 命令逻辑只依赖本接口,因此可以在两代宿主间完全复用。
|
|
9
|
+
*/
|
|
10
|
+
import type { RGBA } from "@opentui/core";
|
|
11
|
+
import type { JSX } from "@opentui/solid";
|
|
12
|
+
import type { UsageFilters, UsageReport } from "../kernel/format.js";
|
|
13
|
+
export type HostKind = "v1" | "v2";
|
|
14
|
+
/** 归一化主题色。v1 扁平、v2 嵌套,由 adapter 各自映射。 */
|
|
15
|
+
export interface ThemeColors {
|
|
16
|
+
readonly primary: RGBA;
|
|
17
|
+
readonly text: RGBA;
|
|
18
|
+
readonly textMuted: RGBA;
|
|
19
|
+
readonly background: RGBA;
|
|
20
|
+
readonly border: RGBA;
|
|
21
|
+
readonly success: RGBA;
|
|
22
|
+
readonly warning: RGBA;
|
|
23
|
+
readonly error: RGBA;
|
|
24
|
+
}
|
|
25
|
+
/** 归一化消息事件(由两代各自的事件形状折叠而来) */
|
|
26
|
+
export interface NormalizedMessageEvent {
|
|
27
|
+
readonly messageID: string;
|
|
28
|
+
readonly sessionID: string;
|
|
29
|
+
readonly role: string;
|
|
30
|
+
readonly providerID: string;
|
|
31
|
+
readonly modelID: string;
|
|
32
|
+
readonly input: number;
|
|
33
|
+
readonly output: number;
|
|
34
|
+
readonly reasoning: number;
|
|
35
|
+
readonly cacheRead: number;
|
|
36
|
+
readonly cacheWrite: number;
|
|
37
|
+
readonly total: number;
|
|
38
|
+
readonly cost: number;
|
|
39
|
+
/** 请求开始时间(epoch ms),供性能追踪计算延迟/TPS */
|
|
40
|
+
readonly timeCreated?: number;
|
|
41
|
+
/** 请求完成时间(epoch ms),缺失时性能追踪跳过该样本 */
|
|
42
|
+
readonly timeCompleted?: number;
|
|
43
|
+
/** 原始事件对象,供性能追踪器读取时间戳等宿主专属字段 */
|
|
44
|
+
readonly raw: any;
|
|
45
|
+
}
|
|
46
|
+
/** 归一化 part 事件(用于 TTFT 计时) */
|
|
47
|
+
export interface NormalizedPartEvent {
|
|
48
|
+
readonly messageID?: string;
|
|
49
|
+
readonly type?: string;
|
|
50
|
+
readonly text?: string;
|
|
51
|
+
readonly timeStart?: number;
|
|
52
|
+
readonly raw: any;
|
|
53
|
+
}
|
|
54
|
+
export interface HostEventHandlers {
|
|
55
|
+
onMessageUpdated(event: NormalizedMessageEvent): void;
|
|
56
|
+
onPartUpdated(event: NormalizedPartEvent): void;
|
|
57
|
+
/** 消息被删除 / 会话数据失效,UI 应重算 */
|
|
58
|
+
onInvalidate(): void;
|
|
59
|
+
}
|
|
60
|
+
/** 侧边栏渲染入参(归一化:两代宿主都提供 sessionID) */
|
|
61
|
+
export interface SidebarInput {
|
|
62
|
+
readonly sessionID: string;
|
|
63
|
+
}
|
|
64
|
+
/** 键值存储(配置与折叠状态) */
|
|
65
|
+
export interface KeyValueStore {
|
|
66
|
+
get<T>(key: string, fallback: T): T;
|
|
67
|
+
set<T>(key: string, value: T): void;
|
|
68
|
+
/** 删除条目;v1 宿主可能不提供(调用方需判空),用于会话消息 KV 的淘汰 */
|
|
69
|
+
delete?(key: string): void;
|
|
70
|
+
}
|
|
71
|
+
export interface CommandSpec {
|
|
72
|
+
readonly id: string;
|
|
73
|
+
readonly title: string;
|
|
74
|
+
readonly description?: string;
|
|
75
|
+
readonly category?: string;
|
|
76
|
+
/** 斜杠命令名(不含前导斜杠) */
|
|
77
|
+
readonly slash?: string;
|
|
78
|
+
readonly run: () => void | Promise<void>;
|
|
79
|
+
}
|
|
80
|
+
export interface SelectOption<Value> {
|
|
81
|
+
readonly title: string;
|
|
82
|
+
readonly value: Value;
|
|
83
|
+
readonly description?: string;
|
|
84
|
+
}
|
|
85
|
+
export type NotifyVariant = "info" | "success" | "warning" | "error";
|
|
86
|
+
/**
|
|
87
|
+
* 历史用量数据源。
|
|
88
|
+
*
|
|
89
|
+
* v1 通过 `opencode db` 执行 SQL(快、功能完整);
|
|
90
|
+
* v2 移除了 db 子命令,只能在客户端遍历会话消息重算(慢、需要缓存与进度提示)。
|
|
91
|
+
*/
|
|
92
|
+
export interface UsageDataSource {
|
|
93
|
+
readonly kind: "sql" | "client-scan";
|
|
94
|
+
/**
|
|
95
|
+
* 首次取数是否需要显著耗时。
|
|
96
|
+
* v2 为 true —— 宿主应在调用前提示用户。
|
|
97
|
+
*/
|
|
98
|
+
readonly needsFirstRunNotice: boolean;
|
|
99
|
+
/**
|
|
100
|
+
* 本次取数是否会触发冷启动全量扫描。
|
|
101
|
+
* SQL 数据源恒为 false;client-scan 在缓存为空时返回 true。
|
|
102
|
+
*/
|
|
103
|
+
readonly isCold: () => boolean;
|
|
104
|
+
getUsageReport(filters: UsageFilters): Promise<UsageReport>;
|
|
105
|
+
}
|
|
106
|
+
/** 宿主能力统一抽象 */
|
|
107
|
+
export interface HostAdapter {
|
|
108
|
+
readonly kind: HostKind;
|
|
109
|
+
/** 宿主版本号,用于诊断与报告元信息 */
|
|
110
|
+
readonly hostVersion: string;
|
|
111
|
+
theme(): ThemeColors;
|
|
112
|
+
store: KeyValueStore;
|
|
113
|
+
/** 订阅事件,返回取消订阅函数 */
|
|
114
|
+
subscribe(handlers: HostEventHandlers): () => void;
|
|
115
|
+
/** 挂载侧边栏 UI,返回卸载函数 */
|
|
116
|
+
registerSidebar(render: (input: SidebarInput) => JSX.Element): () => void;
|
|
117
|
+
/**
|
|
118
|
+
* 注册命令,返回注销函数。
|
|
119
|
+
* v1 经由 host/v1/commands.tsx 用原生 DialogSelect 实现菜单,不走此方法。
|
|
120
|
+
*/
|
|
121
|
+
registerCommands?(specs: readonly CommandSpec[]): () => void;
|
|
122
|
+
notify(message: string, variant?: NotifyVariant): void;
|
|
123
|
+
alert(input: {
|
|
124
|
+
title: string;
|
|
125
|
+
message: string;
|
|
126
|
+
}): Promise<void>;
|
|
127
|
+
select<Value>(input: {
|
|
128
|
+
title: string;
|
|
129
|
+
options: readonly SelectOption<Value>[];
|
|
130
|
+
}): Promise<Value | undefined>;
|
|
131
|
+
/** 宿主退出 / 插件卸载时调用 */
|
|
132
|
+
onDispose(fn: () => void): void;
|
|
133
|
+
/** 当前会话消息列表(用于侧边栏 token 分布估算与历史重建) */
|
|
134
|
+
sessionMessages(sessionID: string): readonly any[];
|
|
135
|
+
/**
|
|
136
|
+
* 指定消息的 part 列表,归一化到 v1 part 形状。
|
|
137
|
+
*
|
|
138
|
+
* v1 需要二次查询 `api.state.part(id)`(会话无关,sessionID 忽略);
|
|
139
|
+
* v2 传入 sessionID 时走 O(1) 的 `message.get(sessionID, id)`,
|
|
140
|
+
* 避免跨全部会话线性扫描。
|
|
141
|
+
*/
|
|
142
|
+
messageParts(messageID: string, sessionID?: string): readonly any[];
|
|
143
|
+
/** 宿主应用配置(读取 agent prompt 用于 system token 估算) */
|
|
144
|
+
appConfig(): Record<string, unknown>;
|
|
145
|
+
/** 订阅 part 级更新(用于 token 分布重算),返回取消订阅函数 */
|
|
146
|
+
onPartUpdated(handler: () => void): () => void;
|
|
147
|
+
readonly dataSource: UsageDataSource;
|
|
148
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
/**
|
|
3
|
+
* v1 宿主适配层:把 opencode 1.x 的 `TuiPluginApi` 折叠为统一 `HostAdapter`。
|
|
4
|
+
*/
|
|
5
|
+
import type { TuiPluginApi } from "@opencode-ai/plugin/tui";
|
|
6
|
+
import type { HostAdapter, KeyValueStore } from "../types.js";
|
|
7
|
+
/** v1 kv 的 KeyValueStore 折叠(v1/commands.tsx 复用同一份,避免两套实现漂移) */
|
|
8
|
+
export declare function makeStore(api: TuiPluginApi): KeyValueStore;
|
|
9
|
+
export declare function createV1Adapter(api: TuiPluginApi): HostAdapter;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
/**
|
|
3
|
+
* v1 命令注册 —— /usage 菜单(HTML 报告 / JSON 导出 / 文本报告 / 设置)。
|
|
4
|
+
*
|
|
5
|
+
* v1 拥有原生 DialogSelect 对话栈,UX 优于通用 select,因此菜单在此原生实现;
|
|
6
|
+
* 实际动作(取数、装配、落盘、打开)全部委托内核层,与 v2 行为一致。
|
|
7
|
+
*/
|
|
8
|
+
import type { TuiPluginApi } from "@opencode-ai/plugin/tui";
|
|
9
|
+
export declare function registerCommands(api: TuiPluginApi): Promise<void>;
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import type { DailyBreakdownItem, ErrorStats, ModelBreakdownItem, ProviderBreakdownItem, SessionBreakdownItem, SessionTokenData, UsageFilters, UsageReport } from "
|
|
1
|
+
import type { DailyBreakdownItem, ErrorStats, ModelBreakdownItem, ProviderBreakdownItem, SessionBreakdownItem, SessionTokenData, UsageFilters, UsageReport } from "../../kernel/format.js";
|
|
2
2
|
export declare function getPresetRange(preset: "all" | "7d" | "30d" | "month"): Pick<UsageFilters, "startDate" | "endDate">;
|
|
3
|
-
export declare function getCurrentSessionStats(sessionId?: string): Promise<SessionTokenData>;
|
|
4
3
|
export declare function getSummary(filters?: UsageFilters): Promise<SessionTokenData>;
|
|
5
4
|
export declare function getModelBreakdown(filters?: UsageFilters): Promise<ModelBreakdownItem[]>;
|
|
6
5
|
export declare function getProviderBreakdown(filters?: UsageFilters): Promise<ProviderBreakdownItem[]>;
|
|
7
6
|
export declare function getDailyBreakdown(filters?: UsageFilters): Promise<DailyBreakdownItem[]>;
|
|
8
7
|
export declare function getSessionBreakdown(filters?: UsageFilters): Promise<SessionBreakdownItem[]>;
|
|
9
|
-
export declare function getAvailableModels(): Promise<string[]>;
|
|
10
|
-
export declare function getAvailableProviders(): Promise<string[]>;
|
|
11
|
-
/** 失败请求计数 SQL。1次运行获取成功数+失败数+按模型细分 */
|
|
12
8
|
export declare function getErrorStats(filters?: UsageFilters): Promise<ErrorStats>;
|
|
13
9
|
export declare function getUsageReport(filters?: UsageFilters): Promise<UsageReport>;
|
|
14
|
-
export declare function exportReportAsCsv(report: UsageReport, section: "models" | "providers" | "daily" | "sessions"): string;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { HostAdapter } from "../types.js";
|
|
2
|
+
/** v2 插件 Context 的最小结构约束(避免对 beta SDK 的硬类型依赖) */
|
|
3
|
+
export interface V2Context {
|
|
4
|
+
readonly app: {
|
|
5
|
+
readonly version: string;
|
|
6
|
+
readonly channel: string;
|
|
7
|
+
};
|
|
8
|
+
readonly theme: any;
|
|
9
|
+
readonly themeMode: "dark" | "light";
|
|
10
|
+
readonly data: {
|
|
11
|
+
readonly on: (type: string, handler: (event: any) => void) => () => void;
|
|
12
|
+
readonly session: {
|
|
13
|
+
list(): any[];
|
|
14
|
+
get(sessionID: string): any | undefined;
|
|
15
|
+
readonly message: {
|
|
16
|
+
list(sessionID: string): any[];
|
|
17
|
+
get(sessionID: string, messageID: string): any | undefined;
|
|
18
|
+
sync(sessionID: string): Promise<void>;
|
|
19
|
+
invalidate(sessionID: string): void;
|
|
20
|
+
};
|
|
21
|
+
sync(sessionID: string): Promise<void>;
|
|
22
|
+
};
|
|
23
|
+
readonly location: {
|
|
24
|
+
readonly agent: {
|
|
25
|
+
list(location?: any): any[] | undefined;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
readonly ui: {
|
|
30
|
+
readonly slot: (claim: any) => () => void;
|
|
31
|
+
readonly toast: {
|
|
32
|
+
show(options: {
|
|
33
|
+
message: string;
|
|
34
|
+
variant?: string;
|
|
35
|
+
title?: string;
|
|
36
|
+
}): void;
|
|
37
|
+
};
|
|
38
|
+
readonly dialog: {
|
|
39
|
+
alert(options: {
|
|
40
|
+
title: string;
|
|
41
|
+
message: string;
|
|
42
|
+
}): Promise<void>;
|
|
43
|
+
select<Value>(options: {
|
|
44
|
+
title: string;
|
|
45
|
+
options: readonly {
|
|
46
|
+
title: string;
|
|
47
|
+
value: Value;
|
|
48
|
+
description?: string;
|
|
49
|
+
}[];
|
|
50
|
+
}): Promise<Value | undefined>;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
readonly keymap: {
|
|
54
|
+
layer(input: () => any): void;
|
|
55
|
+
};
|
|
56
|
+
readonly storage: {
|
|
57
|
+
store<Value extends object>(key: string, options: {
|
|
58
|
+
readonly initial: Value;
|
|
59
|
+
}): readonly [any, (m: (draft: Value) => void) => Promise<void>];
|
|
60
|
+
};
|
|
61
|
+
readonly client: any;
|
|
62
|
+
}
|
|
63
|
+
export declare function createV2Adapter(ctx: V2Context, dispose: Set<() => void>): HostAdapter;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2 命令注册 —— 通过 keymap.layer 暴露 /usage 斜杠命令与面板命令。
|
|
3
|
+
*
|
|
4
|
+
* 菜单用宿主通用 `dialog.select` 实现(v2 无 DialogSelect 对话栈),
|
|
5
|
+
* 动作复用两代共享的 command-actions,行为与 v1 菜单一致。
|
|
6
|
+
*/
|
|
7
|
+
import type { HostAdapter } from "../types.js";
|
|
8
|
+
export declare function registerV2Commands(host: HostAdapter): void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2 历史用量数据源 —— 客户端遍历重算。
|
|
3
|
+
*
|
|
4
|
+
* v2 移除了 `opencode db` 子命令,无法再用 SQL 聚合。这里改为:
|
|
5
|
+
* 1. `session.list()` 拿到全部会话(自带 tokens/cost,可快速出总数)
|
|
6
|
+
* 2. 逐会话 `message.list()` 遍历 assistant 消息,按模型 / 供应商 / 日期分解
|
|
7
|
+
*
|
|
8
|
+
* 第 2 步是 O(会话数 × 消息数) 的冷启动开销,因此:
|
|
9
|
+
* - 首次执行前由调用方提示用户(见 `UsageDataSource.needsFirstRunNotice`)
|
|
10
|
+
* - 结果缓存在内存中,同一 TUI 会话内不重复扫描
|
|
11
|
+
* - 每个会话只同步一次,并把让步交给事件循环,避免长时间阻塞渲染
|
|
12
|
+
*/
|
|
13
|
+
import type { UsageFilters, UsageReport } from "../../kernel/format.js";
|
|
14
|
+
/**
|
|
15
|
+
* 取用量报告(stale-while-revalidate)。
|
|
16
|
+
*
|
|
17
|
+
* - 冷启动(无缓存):等待全量扫描完成(调用方已先提示用户)。
|
|
18
|
+
* - 缓存可用且数据有更新(markUsageCacheDirty):立即返回旧快照,
|
|
19
|
+
* 同时后台重建 —— 本次报告不卡顿,下次打开即为新数据。
|
|
20
|
+
* - 缓存可用且无更新:直接返回。
|
|
21
|
+
*/
|
|
22
|
+
export declare function getUsageReport(ctx: any, filters?: UsageFilters): Promise<UsageReport>;
|
|
23
|
+
/** 有新数据落库(如 session.idle)时由适配层调用:下次取数触发后台重建 */
|
|
24
|
+
export declare function markUsageCacheDirty(): void;
|
|
25
|
+
/** 供命令层判断本次调用是否会触发冷启动扫描 */
|
|
26
|
+
export declare function isUsageCacheCold(): boolean;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 侧边栏配置 —— 宿主无关的持久化设置。
|
|
3
|
+
*
|
|
4
|
+
* 读写只依赖 `KeyValueStore`(v1 的 kv / v2 的 storage.store 在各自 adapter 中折叠),
|
|
5
|
+
* 因此设置项在两代宿主间行为完全一致。
|
|
6
|
+
*/
|
|
7
|
+
import type { KeyValueStore } from "../host/types.js";
|
|
8
|
+
export interface SidebarConfig {
|
|
9
|
+
sidebar: {
|
|
10
|
+
showPerformance: boolean;
|
|
11
|
+
showPricing: boolean;
|
|
12
|
+
showTokenDistribution: boolean;
|
|
13
|
+
showTrend: boolean;
|
|
14
|
+
};
|
|
15
|
+
language: "zh" | "en" | "auto";
|
|
16
|
+
}
|
|
17
|
+
export type SidebarToggleKey = keyof SidebarConfig["sidebar"];
|
|
18
|
+
export declare const DEFAULT_CONFIG: SidebarConfig;
|
|
19
|
+
/**
|
|
20
|
+
* 读取配置:默认值 ← 插件配置(opencode.json)← 运行时覆盖(设置菜单)。
|
|
21
|
+
* `pluginConfig` 为宿主声明式配置,v1 读 `state.config`,v2 暂无等价物。
|
|
22
|
+
*/
|
|
23
|
+
export declare function loadConfig(store: KeyValueStore, pluginConfig?: Record<string, any>): SidebarConfig;
|
|
24
|
+
export declare function saveConfig(store: KeyValueStore, cfg: SidebarConfig): void;
|
|
25
|
+
/**
|
|
26
|
+
* 递增配置版本号。
|
|
27
|
+
*
|
|
28
|
+
* 侧边栏用轮询版本号的方式感知"设置在别处被改了",
|
|
29
|
+
* 因为 v1 的 kv 不是响应式的。
|
|
30
|
+
*/
|
|
31
|
+
export declare function bumpVersion(store: KeyValueStore): void;
|
|
32
|
+
export declare function toggleSidebarSetting(store: KeyValueStore, key: SidebarToggleKey): SidebarConfig;
|
|
33
|
+
export declare function setLanguageSetting(store: KeyValueStore, language: SidebarConfig["language"]): SidebarConfig;
|
|
@@ -73,7 +73,7 @@ export interface ErrorStats {
|
|
|
73
73
|
successCount: number;
|
|
74
74
|
/** 失败请求数(tokens.total == 0 的 assistant) */
|
|
75
75
|
failedCount: number;
|
|
76
|
-
/** 失败率:failedCount / (successCount + failedCount) */
|
|
76
|
+
/** 失败率:failedCount / (successCount + failedCount),小数口径(0~1),两代宿主一致 */
|
|
77
77
|
errorRate: number;
|
|
78
78
|
/** 按模型细化的失败数 */
|
|
79
79
|
byModel: Array<{
|
|
@@ -91,6 +91,8 @@ export interface UsageReport {
|
|
|
91
91
|
daily: DailyBreakdownItem[];
|
|
92
92
|
sessions: SessionBreakdownItem[];
|
|
93
93
|
errors?: ErrorStats;
|
|
94
|
+
/** daily 明细被数据源截断(未覆盖全部日期)时为 true,报告 UI 据此提示 */
|
|
95
|
+
dailyTruncated?: boolean;
|
|
94
96
|
}
|
|
95
97
|
export declare function formatTokens(n: number): string;
|
|
96
98
|
export declare function formatCost(n: number): string;
|
|
@@ -145,15 +147,10 @@ export interface ModelPerfStats {
|
|
|
145
147
|
p99Latency: number | null;
|
|
146
148
|
/** 该模型加权缓存命中率:cacheRead / (cacheRead + input) */
|
|
147
149
|
cacheHitRate: number | null;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
agent: number;
|
|
153
|
-
toolCall: number;
|
|
154
|
-
toolResult: number;
|
|
155
|
-
output: number;
|
|
156
|
-
total: number;
|
|
150
|
+
/** 最近一次请求的 TTFT / TPS / 端到端延迟(与宿主单次口径可直接对照) */
|
|
151
|
+
lastTTFT: number | null;
|
|
152
|
+
lastTPS: number | null;
|
|
153
|
+
lastLatency: number | null;
|
|
157
154
|
}
|
|
158
155
|
export interface LogEntry {
|
|
159
156
|
ts: string;
|
|
@@ -187,4 +184,6 @@ export interface CombinedReportData {
|
|
|
187
184
|
perfLogs: LogEntry[];
|
|
188
185
|
perfSummary: ModelPerfStats[];
|
|
189
186
|
meta: HtmlReportMeta;
|
|
187
|
+
/** daily 明细被数据源截断时为 true(来自 UsageReport),报告时间线区据此提示 */
|
|
188
|
+
dailyTruncated?: boolean;
|
|
190
189
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话内单条 assistant 消息的归一化内存模型。
|
|
3
|
+
*
|
|
4
|
+
* v1 来自 `message.updated` 事件(`info.tokens.{input,output,reasoning,cache}`);
|
|
5
|
+
* v2 来自 `SessionMessageAssistant.tokens`(`{input,output,reasoning,cache:{read,write}}`)。
|
|
6
|
+
* 两者在各自的 adapter 中折叠为本类型,后续所有聚合逻辑只认这一份结构。
|
|
7
|
+
*/
|
|
8
|
+
export interface TokenMessage {
|
|
9
|
+
id: string;
|
|
10
|
+
sessionID: string;
|
|
11
|
+
providerID: string;
|
|
12
|
+
modelID: string;
|
|
13
|
+
inputTokens: number;
|
|
14
|
+
outputTokens: number;
|
|
15
|
+
reasoningTokens: number;
|
|
16
|
+
cacheRead: number;
|
|
17
|
+
cacheWrite: number;
|
|
18
|
+
cost: number;
|
|
19
|
+
}
|