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
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ Load errors: `~/.local/share/opencode/log/` (search `cache-hit` or `failed to lo

## Configuration

Configuration files accept JSONC (line/block comments and trailing commas). `cache-hit.config.example.json` ships as strict JSON so it stays tool-friendly (jq, editors).

### Cost display (USD → CNY example)

```json
Expand All @@ -105,7 +107,7 @@ Load errors: `~/.local/share/opencode/log/` (search `cache-hit` or `failed to lo
| `currency` | Sidebar display currency |
| `rate` | Multiply `costUnit` → `currency` |

Use `"currency": "USD", "costUnit": "USD"` when no conversion is needed.
`rate` is a **manual snapshot** — the `6.77` in the example is the USD→CNY rate at the time it was written and does not auto-update. Omitting `rate` falls back to the same built-in default, so it only needs to be set when you display a non-USD currency and want a current rate (e.g. from [Xe](https://www.xe.com/currencyconverter/), [Wise](https://wise.com/currency-converter), or [OANDA](https://www.oanda.com/currency-converter/)). Use `"currency": "USD", "costUnit": "USD"` when no conversion is needed.

Supported display currencies in config: `USD`, `CNY`, `EUR`, `GBP`, `JPY` (see `cache-hit.config.example.json`). Runtime slash switching like visual-cache’s `/cache-currency` is **not** implemented yet.

Expand Down Expand Up @@ -271,7 +273,7 @@ Then reinstall via `Ctrl+P` → install plugin, and **restart OpenCode**.
To avoid the pinning issue entirely, install a **pinned version** instead of `@latest`:

```jsonc
{ "plugin": ["opencode-cache-hit@0.7.0"] }
{ "plugin": ["opencode-cache-hit@0.7.1"] }
```

## Compatibility
Expand Down
4 changes: 3 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ OpenCode **TUI 侧边栏插件**:展示 prompt cache 命中率、token 用量

## 配置

配置文件支持 JSONC(行注释、块注释、尾逗号)。`cache-hit.config.example.json` 保持严格 JSON,便于 jq、编辑器等外部工具使用。

### 成本展示(USD → CNY 示例)

```json
Expand All @@ -105,7 +107,7 @@ OpenCode **TUI 侧边栏插件**:展示 prompt cache 命中率、token 用量
| `currency` | 侧边栏展示币种 |
| `rate` | `costUnit` → `currency` 的汇率 |

当无需换算时使用 `"currency": "USD", "costUnit": "USD"`。
`rate` 是**手动维护的快照值**——示例中的 `6.77` 是当时 USD→CNY 的汇率,不会自动更新。省略 `rate` 时回落到内置默认值 6.77,因此只有在展示非 USD 币种且希望汇率准确时才需要设置(可到 [Xe](https://www.xe.com/currencyconverter/)、[Wise](https://wise.com/currency-converter) 或 [OANDA](https://www.oanda.com/currency-converter/) 查询最新汇率)。当无需换算时使用 `"currency": "USD", "costUnit": "USD"`。

支持的展示币种:`USD`、`CNY`、`EUR`、`GBP`、`JPY`(见 `cache-hit.config.example.json`)。暂未实现类似 visual-cache 的 `/cache-currency` 斜杠命令。

Expand Down
1 change: 1 addition & 0 deletions cache-hit.config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"currency": "CNY",
"costUnit": "USD",
"rate": 6.77,
"$comment-rate": "Manual exchange-rate snapshot USD→currency (6.77 = USD→CNY at the time this was written; does not auto-update). Omit to keep the built-in default 6.77, or set \"currency\": \"USD\" to skip conversion. Current rates: xe.com, wise.com, oanda.com.",
"display": {
"lang": "en",
"panelBorder": true,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "opencode-cache-hit",
"version": "0.7.0",
"version": "0.7.1",
"description": "OpenCode TUI sidebar: prompt cache hit rate, tokens & cost with sub-agent rollup. Works with opencode-visual-cache; optional per-call JSONL timeline.",
"type": "module",
"license": "MIT",
Expand Down
1 change: 1 addition & 0 deletions src/format-cost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const CURRENCY_PRESETS: Record<CurrencyCode, { symbol: string; decimals:
export const DEFAULT_COST_DISPLAY: CostDisplayConfig = {
currency: "CNY",
costUnit: "USD",
// Manual snapshot of USD→CNY at the time this was written; users override via config.
rate: 6.77,
}

Expand Down
5 changes: 3 additions & 2 deletions src/load-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
normalizePluginConfig,
DEFAULT_PLUGIN_CONFIG,
} from "./plugin-config.ts"
import { parseJsonc } from "./jsonc.ts"

/** Parent of `src/` (plugin package root). Do not wrap in `dirname` — `..` already resolves there. */
export const PLUGIN_ROOT = fileURLToPath(new URL("..", import.meta.url))
Expand All @@ -21,9 +22,9 @@ export function cloneDefault(): PluginConfig {
return structuredClone(DEFAULT_PLUGIN_CONFIG)
}

function tryRead(path: string): PluginConfig | null {
export function tryRead(path: string): PluginConfig | null {
try {
return normalizePluginConfig(JSON.parse(readFileSync(path, "utf8")))
return normalizePluginConfig(parseJsonc(readFileSync(path, "utf8")))
} catch {
return null
}
Expand Down
43 changes: 41 additions & 2 deletions tests/load-config.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { describe, test, expect } from "bun:test"
import { existsSync } from "node:fs"
import { existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"
import { tmpdir } from "node:os"
import { join } from "node:path"
import { CONFIG_PATH, PLUGIN_ROOT, XDG_CONFIG_PATH, cloneDefault, loadPluginConfig } from "../src/load-config.ts"
import { CONFIG_PATH, PLUGIN_ROOT, XDG_CONFIG_PATH, cloneDefault, loadPluginConfig, tryRead } from "../src/load-config.ts"
import { DEFAULT_PLUGIN_CONFIG } from "../src/plugin-config.ts"

describe("load-config paths", () => {
Expand Down Expand Up @@ -44,4 +45,42 @@ describe("load-config paths", () => {
expect(DEFAULT_PLUGIN_CONFIG.timeline.toolSummary).toEqual({ allTools: true, bash: false })
expect(DEFAULT_PLUGIN_CONFIG.display.lang).toBe("en")
})

test("parses JSONC configs — comments and trailing commas", () => {
const dir = mkdtempSync(join(tmpdir(), "cache-hit-"))
const path = join(dir, "cache-hit.json")
writeFileSync(
path,
[
"{",
' // line comment',
' "currency": "USD",',
' "costUnit": "USD",',
' "display": { "lang": "zh" }, // trailing comma + comment',
' /* block comment */ "timeline": { "enabled": false },',
"}",
].join("\n"),
)
try {
const cfg = tryRead(path)
expect(cfg).not.toBeNull()
expect(cfg?.cost.currency).toBe("USD")
expect(cfg?.cost.costUnit).toBe("USD")
expect(cfg?.display.lang).toBe("zh")
expect(cfg?.timeline.enabled).toBe(false)
} finally {
rmSync(dir, { recursive: true, force: true })
}
})

test("returns null for malformed config content", () => {
const dir = mkdtempSync(join(tmpdir(), "cache-hit-"))
const path = join(dir, "bad.json")
writeFileSync(path, "{ nope")
try {
expect(tryRead(path)).toBeNull()
} finally {
rmSync(dir, { recursive: true, force: true })
}
})
})
Loading