opencode-visual-cache 1.7.0-beta.2 → 1.7.0-beta.4
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/dist/_version.d.ts +1 -1
- package/dist/_version.js +1 -1
- package/dist/balance-providers.js +27 -1
- package/dist/index.js +4 -1
- package/dist/server.d.ts +1 -4
- package/dist/server.js +0 -4
- package/dist/tui.js +31 -2
- package/dist/v2/index.d.ts +3 -1
- package/dist/v2/index.js +3 -0
- package/dist/v2.js +27 -3
- package/package.json +3 -2
- package/src/_version.ts +1 -1
- package/src/balance-providers.ts +25 -1
- package/src/core/color.ts +108 -108
- package/src/core/currency.ts +46 -46
- package/src/core/estimate.ts +36 -36
- package/src/core/format.ts +81 -81
- package/src/core/index.ts +5 -5
- package/src/core/types.ts +17 -17
- package/src/index.tsx +3 -0
- package/src/panel/panel-api.ts +104 -104
- package/src/server.ts +1 -5
- package/src/v2/data.ts +112 -112
- package/src/v2/index.tsx +187 -184
- package/src/v2/sidebar.tsx +69 -69
- package/src/v2/status.tsx +96 -96
- package/src/v2/theme.ts +19 -19
- package/src/v2/types.ts +159 -159
- package/src/v2/v2-panel-api.ts +177 -177
- package/tui/index.js +12 -0
package/src/core/format.ts
CHANGED
|
@@ -1,81 +1,81 @@
|
|
|
1
|
-
/** CJK characters occupy 2 terminal columns; padEnd/padStart count
|
|
2
|
-
* string length (=1 per char), which breaks alignment with mixed text. */
|
|
3
|
-
|
|
4
|
-
export function charColumns(c: string): number {
|
|
5
|
-
const code = c.codePointAt(0) ?? 0
|
|
6
|
-
if (code < 0x20) return 0 // control
|
|
7
|
-
if (code < 0x7F) return 1 // ASCII
|
|
8
|
-
if (code < 0xA0) return 0 // C1 controls
|
|
9
|
-
// East-Asian wide / fullwidth ranges
|
|
10
|
-
if ((code >= 0x1100 && code <= 0x115F) || // Hangul Jamo
|
|
11
|
-
(code >= 0x2E80 && code <= 0xA4CF) || // CJK Radicals … Yi
|
|
12
|
-
(code >= 0xAC00 && code <= 0xD7A3) || // Hangul
|
|
13
|
-
(code >= 0xF900 && code <= 0xFAFF) || // CJK Compat
|
|
14
|
-
(code >= 0xFE10 && code <= 0xFE6F) || // Vertical / Compat
|
|
15
|
-
(code >= 0xFF01 && code <= 0xFF60) || // Fullwidth
|
|
16
|
-
(code >= 0xFFE0 && code <= 0xFFE6) || // Fullwidth signs
|
|
17
|
-
(code >= 0x1F300 && code <= 0x1F64F) || // Misc Symbols (emoji)
|
|
18
|
-
(code >= 0x20000 && code <= 0x3FFFD)) // SIP / TIP
|
|
19
|
-
return 2
|
|
20
|
-
return 1
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function visualWidth(s: string): number {
|
|
24
|
-
let w = 0; for (const c of s) w += charColumns(c); return w
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function visualPadEnd(s: string, cols: number): string {
|
|
28
|
-
const pad = cols - visualWidth(s)
|
|
29
|
-
return pad > 0 ? s + " ".repeat(pad) : s
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** Truncate `s` to fit within `maxCols` visual columns, appending "…" when cut. */
|
|
33
|
-
export function truncateVisual(s: string, maxCols: number): string {
|
|
34
|
-
if (visualWidth(s) <= maxCols) return s
|
|
35
|
-
let result = "", w = 0
|
|
36
|
-
for (const c of s) {
|
|
37
|
-
const cw = charColumns(c)
|
|
38
|
-
if (w + cw > maxCols - 1) { result += "\u2026"; break }
|
|
39
|
-
result += c; w += cw
|
|
40
|
-
}
|
|
41
|
-
return result
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function progressBar(percent: number, width: number): string {
|
|
45
|
-
const clamped = Math.max(0, Math.min(100, percent))
|
|
46
|
-
const filled = Math.round((clamped / 100) * width)
|
|
47
|
-
const empty = Math.max(0, width - filled)
|
|
48
|
-
return "\u2588".repeat(filled) + "\u2591".repeat(empty)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function fmt(n: number): string {
|
|
52
|
-
if (n >= 1_000_000) return (n / 1_000_000).toFixed(1) + "M"
|
|
53
|
-
if (n >= 10_000) return (n / 1_000).toFixed(1) + "K"
|
|
54
|
-
return n.toLocaleString("en-US")
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function num(v: unknown): number {
|
|
58
|
-
return typeof v === "number" && Number.isFinite(v) ? v : 0
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function fmtCost(n: number, symbol = "$", rate = 1): string {
|
|
62
|
-
const v = n * rate
|
|
63
|
-
if (v >= 1) return symbol + v.toFixed(2)
|
|
64
|
-
if (v >= 0.01) return symbol + v.toFixed(3)
|
|
65
|
-
return symbol + v.toFixed(4)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** 紧凑数字缩写(底部状态栏用):1234 → "1.2K",1234567 → "1.2M"。 */
|
|
69
|
-
export function fmtCompact(n: number): string {
|
|
70
|
-
if (n >= 1e6) return (n / 1e6).toFixed(1) + "M"
|
|
71
|
-
if (n >= 1e3) return (n / 1e3).toFixed(1) + "K"
|
|
72
|
-
return String(Math.round(n))
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** 余额数值格式化:≥1 或 0 显示固定 2 位小数;小额(<1)保留精度(最多 6 位),避免抹成 0.00。 */
|
|
76
|
-
export function formatBalanceAmount(total: string): string {
|
|
77
|
-
const n = parseFloat(total)
|
|
78
|
-
if (!Number.isFinite(n)) return total
|
|
79
|
-
if (n === 0 || n >= 1) return n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
|
80
|
-
return n.toLocaleString("en-US", { maximumFractionDigits: 6 })
|
|
81
|
-
}
|
|
1
|
+
/** CJK characters occupy 2 terminal columns; padEnd/padStart count
|
|
2
|
+
* string length (=1 per char), which breaks alignment with mixed text. */
|
|
3
|
+
|
|
4
|
+
export function charColumns(c: string): number {
|
|
5
|
+
const code = c.codePointAt(0) ?? 0
|
|
6
|
+
if (code < 0x20) return 0 // control
|
|
7
|
+
if (code < 0x7F) return 1 // ASCII
|
|
8
|
+
if (code < 0xA0) return 0 // C1 controls
|
|
9
|
+
// East-Asian wide / fullwidth ranges
|
|
10
|
+
if ((code >= 0x1100 && code <= 0x115F) || // Hangul Jamo
|
|
11
|
+
(code >= 0x2E80 && code <= 0xA4CF) || // CJK Radicals … Yi
|
|
12
|
+
(code >= 0xAC00 && code <= 0xD7A3) || // Hangul
|
|
13
|
+
(code >= 0xF900 && code <= 0xFAFF) || // CJK Compat
|
|
14
|
+
(code >= 0xFE10 && code <= 0xFE6F) || // Vertical / Compat
|
|
15
|
+
(code >= 0xFF01 && code <= 0xFF60) || // Fullwidth
|
|
16
|
+
(code >= 0xFFE0 && code <= 0xFFE6) || // Fullwidth signs
|
|
17
|
+
(code >= 0x1F300 && code <= 0x1F64F) || // Misc Symbols (emoji)
|
|
18
|
+
(code >= 0x20000 && code <= 0x3FFFD)) // SIP / TIP
|
|
19
|
+
return 2
|
|
20
|
+
return 1
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function visualWidth(s: string): number {
|
|
24
|
+
let w = 0; for (const c of s) w += charColumns(c); return w
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function visualPadEnd(s: string, cols: number): string {
|
|
28
|
+
const pad = cols - visualWidth(s)
|
|
29
|
+
return pad > 0 ? s + " ".repeat(pad) : s
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Truncate `s` to fit within `maxCols` visual columns, appending "…" when cut. */
|
|
33
|
+
export function truncateVisual(s: string, maxCols: number): string {
|
|
34
|
+
if (visualWidth(s) <= maxCols) return s
|
|
35
|
+
let result = "", w = 0
|
|
36
|
+
for (const c of s) {
|
|
37
|
+
const cw = charColumns(c)
|
|
38
|
+
if (w + cw > maxCols - 1) { result += "\u2026"; break }
|
|
39
|
+
result += c; w += cw
|
|
40
|
+
}
|
|
41
|
+
return result
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function progressBar(percent: number, width: number): string {
|
|
45
|
+
const clamped = Math.max(0, Math.min(100, percent))
|
|
46
|
+
const filled = Math.round((clamped / 100) * width)
|
|
47
|
+
const empty = Math.max(0, width - filled)
|
|
48
|
+
return "\u2588".repeat(filled) + "\u2591".repeat(empty)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function fmt(n: number): string {
|
|
52
|
+
if (n >= 1_000_000) return (n / 1_000_000).toFixed(1) + "M"
|
|
53
|
+
if (n >= 10_000) return (n / 1_000).toFixed(1) + "K"
|
|
54
|
+
return n.toLocaleString("en-US")
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function num(v: unknown): number {
|
|
58
|
+
return typeof v === "number" && Number.isFinite(v) ? v : 0
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function fmtCost(n: number, symbol = "$", rate = 1): string {
|
|
62
|
+
const v = n * rate
|
|
63
|
+
if (v >= 1) return symbol + v.toFixed(2)
|
|
64
|
+
if (v >= 0.01) return symbol + v.toFixed(3)
|
|
65
|
+
return symbol + v.toFixed(4)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** 紧凑数字缩写(底部状态栏用):1234 → "1.2K",1234567 → "1.2M"。 */
|
|
69
|
+
export function fmtCompact(n: number): string {
|
|
70
|
+
if (n >= 1e6) return (n / 1e6).toFixed(1) + "M"
|
|
71
|
+
if (n >= 1e3) return (n / 1e3).toFixed(1) + "K"
|
|
72
|
+
return String(Math.round(n))
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** 余额数值格式化:≥1 或 0 显示固定 2 位小数;小额(<1)保留精度(最多 6 位),避免抹成 0.00。 */
|
|
76
|
+
export function formatBalanceAmount(total: string): string {
|
|
77
|
+
const n = parseFloat(total)
|
|
78
|
+
if (!Number.isFinite(n)) return total
|
|
79
|
+
if (n === 0 || n >= 1) return n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
|
80
|
+
return n.toLocaleString("en-US", { maximumFractionDigits: 6 })
|
|
81
|
+
}
|
package/src/core/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export * from "./color"
|
|
2
|
-
export * from "./currency"
|
|
3
|
-
export * from "./estimate"
|
|
4
|
-
export * from "./format"
|
|
5
|
-
export * from "./types"
|
|
1
|
+
export * from "./color"
|
|
2
|
+
export * from "./currency"
|
|
3
|
+
export * from "./estimate"
|
|
4
|
+
export * from "./format"
|
|
5
|
+
export * from "./types"
|
package/src/core/types.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Token distribution breakdown for a session round.
|
|
3
|
-
* Pure data model shared by V1/V2 shells.
|
|
4
|
-
*/
|
|
5
|
-
export interface TokenDist {
|
|
6
|
-
system: number // UserMessage.system
|
|
7
|
-
user: number // user message text/file parts
|
|
8
|
-
agent: number // task tool input prompt/description (sub-agent delegation)
|
|
9
|
-
toolCall: number // ToolPart.input (actual tool params)
|
|
10
|
-
toolResult: number // ToolPart completed output / error
|
|
11
|
-
output: number // AssistantMessage.tokens.output (API exact, reasoning excluded)
|
|
12
|
-
reasoning: number // AssistantMessage.tokens.reasoning (API exact)
|
|
13
|
-
apiOutput: number // StepFinishPart.tokens.output (API exact, preferred)
|
|
14
|
-
apiInput: number // API exact total input context (input + cache read + cache write)
|
|
15
|
-
stepCost: number // last step-finish part cost (USD) in the current round
|
|
16
|
-
stepCount: number // step-finish parts count across the current round (parentID chain)
|
|
17
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Token distribution breakdown for a session round.
|
|
3
|
+
* Pure data model shared by V1/V2 shells.
|
|
4
|
+
*/
|
|
5
|
+
export interface TokenDist {
|
|
6
|
+
system: number // UserMessage.system
|
|
7
|
+
user: number // user message text/file parts
|
|
8
|
+
agent: number // task tool input prompt/description (sub-agent delegation)
|
|
9
|
+
toolCall: number // ToolPart.input (actual tool params)
|
|
10
|
+
toolResult: number // ToolPart completed output / error
|
|
11
|
+
output: number // AssistantMessage.tokens.output (API exact, reasoning excluded)
|
|
12
|
+
reasoning: number // AssistantMessage.tokens.reasoning (API exact)
|
|
13
|
+
apiOutput: number // StepFinishPart.tokens.output (API exact, preferred)
|
|
14
|
+
apiInput: number // API exact total input context (input + cache read + cache write)
|
|
15
|
+
stepCost: number // last step-finish part cost (USD) in the current round
|
|
16
|
+
stepCount: number // step-finish parts count across the current round (parentID chain)
|
|
17
|
+
}
|
package/src/index.tsx
CHANGED
|
@@ -493,6 +493,9 @@ const tui: TuiPlugin = async (api: TuiPluginApi) => {
|
|
|
493
493
|
onSubmit={input.on_submit}
|
|
494
494
|
ref={input.ref}
|
|
495
495
|
hint={<BottomStatusBar api={api} signals={signals} sessionId={input.session_id} />}
|
|
496
|
+
// 接管 session_prompt 后需透传宿主的 session_prompt_right 插槽,
|
|
497
|
+
// 否则 oc-tps 等依赖该插槽的插件无法显示;无注册时 Slot 为 null。
|
|
498
|
+
right={<api.ui.Slot name="session_prompt_right" session_id={input.session_id} />}
|
|
496
499
|
/>
|
|
497
500
|
)
|
|
498
501
|
},
|
package/src/panel/panel-api.ts
CHANGED
|
@@ -1,104 +1,104 @@
|
|
|
1
|
-
import type { Message, Part, Session } from "@opencode-ai/sdk"
|
|
2
|
-
import type { TuiThemeCurrent } from "@opencode-ai/plugin/tui"
|
|
3
|
-
import type { BalanceEntry } from "../balance-providers"
|
|
4
|
-
import type { LangCode } from "../i18n"
|
|
5
|
-
|
|
6
|
-
/** 会话信息(面板消费的字段;SDK Session 类型过严,用宽松接口) */
|
|
7
|
-
export interface PanelSession {
|
|
8
|
-
id: string
|
|
9
|
-
title?: string
|
|
10
|
-
agent?: string
|
|
11
|
-
model?: { providerID?: string; id?: string }
|
|
12
|
-
tokens?: { input?: number; output?: number; reasoning?: number; cache?: { read?: number; write?: number } }
|
|
13
|
-
cost?: number
|
|
14
|
-
[key: string]: unknown
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Panel API 契约:TokenCachePanel 消费的宿主 API 子集。
|
|
19
|
-
* V1(TuiPluginApi)天然满足;V2(opencode2 context)由 v2-panel-api 适配实现。
|
|
20
|
-
*/
|
|
21
|
-
export interface PanelApi {
|
|
22
|
-
kv: {
|
|
23
|
-
ready: boolean
|
|
24
|
-
get<T>(key: string, fallback?: T): T | undefined
|
|
25
|
-
set(key: string, value: unknown): void | Promise<void>
|
|
26
|
-
}
|
|
27
|
-
state: {
|
|
28
|
-
session: {
|
|
29
|
-
get(id: string): PanelSession | undefined
|
|
30
|
-
/** V1 返回 sdk/v2 的 Message、V2 返回 SessionMessageInfo——统一放宽 */
|
|
31
|
-
messages(id: string): readonly any[]
|
|
32
|
-
}
|
|
33
|
-
provider: readonly Record<string, any>[]
|
|
34
|
-
config: any
|
|
35
|
-
part(messageID: string): readonly any[]
|
|
36
|
-
path: { directory: string }
|
|
37
|
-
}
|
|
38
|
-
event: {
|
|
39
|
-
on(type: string, handler: (event: unknown) => void): () => void
|
|
40
|
-
}
|
|
41
|
-
renderer: { terminalWidth: number }
|
|
42
|
-
keys: { formatBindings(binding: unknown): string | undefined }
|
|
43
|
-
tuiConfig: { keybinds: { get(command: string): unknown } }
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface BalanceState {
|
|
47
|
-
status: "idle" | "loading" | "ok" | "error"
|
|
48
|
-
data: BalanceEntry[] | null
|
|
49
|
-
lastFetch: number
|
|
50
|
-
error?: string
|
|
51
|
-
key?: string // 上次成功/尝试查询所用的 key,用于检测 key 是否更换
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** Signals shared between the TUI component and slash commands.
|
|
55
|
-
* Created in the `tui` function scope so they do not survive module reload —
|
|
56
|
-
* the component re-creates them on mount and restores user config from kv. */
|
|
57
|
-
export interface PanelSignals {
|
|
58
|
-
currencySymbol: () => string
|
|
59
|
-
setCurrencySymbol: (v: string) => void
|
|
60
|
-
exchangeRate: () => number
|
|
61
|
-
setExchangeRate: (v: number) => void
|
|
62
|
-
langCode: () => LangCode
|
|
63
|
-
setLangCode: (v: LangCode) => void
|
|
64
|
-
sectionDetail: () => boolean
|
|
65
|
-
setSectionDetail: (v: boolean) => void
|
|
66
|
-
sectionModel: () => boolean
|
|
67
|
-
setSectionModel: (v: boolean) => void
|
|
68
|
-
sectionDist: () => boolean
|
|
69
|
-
setSectionDist: (v: boolean) => void
|
|
70
|
-
sectionSkills: () => boolean
|
|
71
|
-
setSectionSkills: (v: boolean) => void
|
|
72
|
-
sectionBalance: () => boolean
|
|
73
|
-
setSectionBalance: (v: boolean) => void
|
|
74
|
-
/** Bottom status bar (prompt hint line) visibility. */
|
|
75
|
-
sectionBottom: () => boolean
|
|
76
|
-
setSectionBottom: (v: boolean) => void
|
|
77
|
-
/** Increment to force a balance re-fetch. */
|
|
78
|
-
balanceRefresh: () => number
|
|
79
|
-
setBalanceRefresh: (v: number) => void
|
|
80
|
-
/** Currently selected balance provider id (e.g. "deepseek"). */
|
|
81
|
-
balanceProviderId: () => string
|
|
82
|
-
setBalanceProviderId: (v: string) => void
|
|
83
|
-
/** Auto-switch to the session's provider for balance display. Manual switch disables it. */
|
|
84
|
-
autoBalance: () => boolean
|
|
85
|
-
setAutoBalance: (v: boolean) => void
|
|
86
|
-
/** True when the session's provider has no balance adapter (auto mode). Suppresses balance polling. */
|
|
87
|
-
balanceUnsupported: () => boolean
|
|
88
|
-
setBalanceUnsupported: (v: boolean) => void
|
|
89
|
-
/** Shared balance query state — single source of truth for sidebar and bottom bar. */
|
|
90
|
-
balanceState: () => BalanceState
|
|
91
|
-
/** Preferred currency code for balance display (CNY / USD / …). Empty = first entry. */
|
|
92
|
-
balanceCurrency: () => string
|
|
93
|
-
setBalanceCurrency: (v: string) => void
|
|
94
|
-
borderVisible: () => boolean
|
|
95
|
-
setBorderVisible: (v: boolean) => void
|
|
96
|
-
/** When set, the panel renders stats for this session instead of the main one. */
|
|
97
|
-
overrideSessionId: () => string | undefined
|
|
98
|
-
setOverrideSessionId: (v: string | undefined) => void
|
|
99
|
-
/** True while our sidebar panel is mounted — host sidebar is visible (occupies 42 cols). */
|
|
100
|
-
sidebarVisible: () => boolean
|
|
101
|
-
setSidebarVisible: (v: boolean) => void
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export type { TuiThemeCurrent }
|
|
1
|
+
import type { Message, Part, Session } from "@opencode-ai/sdk"
|
|
2
|
+
import type { TuiThemeCurrent } from "@opencode-ai/plugin/tui"
|
|
3
|
+
import type { BalanceEntry } from "../balance-providers"
|
|
4
|
+
import type { LangCode } from "../i18n"
|
|
5
|
+
|
|
6
|
+
/** 会话信息(面板消费的字段;SDK Session 类型过严,用宽松接口) */
|
|
7
|
+
export interface PanelSession {
|
|
8
|
+
id: string
|
|
9
|
+
title?: string
|
|
10
|
+
agent?: string
|
|
11
|
+
model?: { providerID?: string; id?: string }
|
|
12
|
+
tokens?: { input?: number; output?: number; reasoning?: number; cache?: { read?: number; write?: number } }
|
|
13
|
+
cost?: number
|
|
14
|
+
[key: string]: unknown
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Panel API 契约:TokenCachePanel 消费的宿主 API 子集。
|
|
19
|
+
* V1(TuiPluginApi)天然满足;V2(opencode2 context)由 v2-panel-api 适配实现。
|
|
20
|
+
*/
|
|
21
|
+
export interface PanelApi {
|
|
22
|
+
kv: {
|
|
23
|
+
ready: boolean
|
|
24
|
+
get<T>(key: string, fallback?: T): T | undefined
|
|
25
|
+
set(key: string, value: unknown): void | Promise<void>
|
|
26
|
+
}
|
|
27
|
+
state: {
|
|
28
|
+
session: {
|
|
29
|
+
get(id: string): PanelSession | undefined
|
|
30
|
+
/** V1 返回 sdk/v2 的 Message、V2 返回 SessionMessageInfo——统一放宽 */
|
|
31
|
+
messages(id: string): readonly any[]
|
|
32
|
+
}
|
|
33
|
+
provider: readonly Record<string, any>[]
|
|
34
|
+
config: any
|
|
35
|
+
part(messageID: string): readonly any[]
|
|
36
|
+
path: { directory: string }
|
|
37
|
+
}
|
|
38
|
+
event: {
|
|
39
|
+
on(type: string, handler: (event: unknown) => void): () => void
|
|
40
|
+
}
|
|
41
|
+
renderer: { terminalWidth: number }
|
|
42
|
+
keys: { formatBindings(binding: unknown): string | undefined }
|
|
43
|
+
tuiConfig: { keybinds: { get(command: string): unknown } }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface BalanceState {
|
|
47
|
+
status: "idle" | "loading" | "ok" | "error"
|
|
48
|
+
data: BalanceEntry[] | null
|
|
49
|
+
lastFetch: number
|
|
50
|
+
error?: string
|
|
51
|
+
key?: string // 上次成功/尝试查询所用的 key,用于检测 key 是否更换
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Signals shared between the TUI component and slash commands.
|
|
55
|
+
* Created in the `tui` function scope so they do not survive module reload —
|
|
56
|
+
* the component re-creates them on mount and restores user config from kv. */
|
|
57
|
+
export interface PanelSignals {
|
|
58
|
+
currencySymbol: () => string
|
|
59
|
+
setCurrencySymbol: (v: string) => void
|
|
60
|
+
exchangeRate: () => number
|
|
61
|
+
setExchangeRate: (v: number) => void
|
|
62
|
+
langCode: () => LangCode
|
|
63
|
+
setLangCode: (v: LangCode) => void
|
|
64
|
+
sectionDetail: () => boolean
|
|
65
|
+
setSectionDetail: (v: boolean) => void
|
|
66
|
+
sectionModel: () => boolean
|
|
67
|
+
setSectionModel: (v: boolean) => void
|
|
68
|
+
sectionDist: () => boolean
|
|
69
|
+
setSectionDist: (v: boolean) => void
|
|
70
|
+
sectionSkills: () => boolean
|
|
71
|
+
setSectionSkills: (v: boolean) => void
|
|
72
|
+
sectionBalance: () => boolean
|
|
73
|
+
setSectionBalance: (v: boolean) => void
|
|
74
|
+
/** Bottom status bar (prompt hint line) visibility. */
|
|
75
|
+
sectionBottom: () => boolean
|
|
76
|
+
setSectionBottom: (v: boolean) => void
|
|
77
|
+
/** Increment to force a balance re-fetch. */
|
|
78
|
+
balanceRefresh: () => number
|
|
79
|
+
setBalanceRefresh: (v: number) => void
|
|
80
|
+
/** Currently selected balance provider id (e.g. "deepseek"). */
|
|
81
|
+
balanceProviderId: () => string
|
|
82
|
+
setBalanceProviderId: (v: string) => void
|
|
83
|
+
/** Auto-switch to the session's provider for balance display. Manual switch disables it. */
|
|
84
|
+
autoBalance: () => boolean
|
|
85
|
+
setAutoBalance: (v: boolean) => void
|
|
86
|
+
/** True when the session's provider has no balance adapter (auto mode). Suppresses balance polling. */
|
|
87
|
+
balanceUnsupported: () => boolean
|
|
88
|
+
setBalanceUnsupported: (v: boolean) => void
|
|
89
|
+
/** Shared balance query state — single source of truth for sidebar and bottom bar. */
|
|
90
|
+
balanceState: () => BalanceState
|
|
91
|
+
/** Preferred currency code for balance display (CNY / USD / …). Empty = first entry. */
|
|
92
|
+
balanceCurrency: () => string
|
|
93
|
+
setBalanceCurrency: (v: string) => void
|
|
94
|
+
borderVisible: () => boolean
|
|
95
|
+
setBorderVisible: (v: boolean) => void
|
|
96
|
+
/** When set, the panel renders stats for this session instead of the main one. */
|
|
97
|
+
overrideSessionId: () => string | undefined
|
|
98
|
+
setOverrideSessionId: (v: string | undefined) => void
|
|
99
|
+
/** True while our sidebar panel is mounted — host sidebar is visible (occupies 42 cols). */
|
|
100
|
+
sidebarVisible: () => boolean
|
|
101
|
+
setSidebarVisible: (v: boolean) => void
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export type { TuiThemeCurrent }
|
package/src/server.ts
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import type { Plugin, PluginModule } from "@opencode-ai/plugin"
|
|
2
|
-
import v2Mod from "./v2/index"
|
|
3
2
|
|
|
4
|
-
// V1 server 插件(空实现,保持原行为);V2 经 exports["./server"] 加载此入口,
|
|
5
|
-
// 需要 default.setup——此处复用 v2 的 setup,一个入口同时满足 V1(server)与 V2(setup)。
|
|
6
3
|
const server: Plugin = async () => ({})
|
|
7
4
|
|
|
8
|
-
const mod: PluginModule
|
|
5
|
+
const mod: PluginModule = {
|
|
9
6
|
id: "opencode-visual-cache",
|
|
10
7
|
server,
|
|
11
|
-
setup: v2Mod.setup,
|
|
12
8
|
}
|
|
13
9
|
|
|
14
10
|
export default mod
|