@rejacky/opencode-insights 0.1.11 → 0.1.13
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.md +24 -1
- package/dist/{capture-BIiGg2nW.d.ts → capture-BpktoFGh.d.ts} +8 -1
- package/dist/{chunk-3YLLHABZ.js → chunk-RWPY5QOE.js} +20 -4
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/tui.js +214 -1
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -93,12 +93,13 @@ The `uninstall` command removes plugin config entries and local Insights data; i
|
|
|
93
93
|
|
|
94
94
|
- Configurable live metrics in the OpenCode session prompt zone.
|
|
95
95
|
- A collapsible session-wide `Token Usage` sidebar showing total tokens, response count, input/output/reasoning usage, cache read/write usage, and aggregate cache rate. It loads completed responses already present in the session and continues updating live.
|
|
96
|
+
- An opt-in `Go Usage` sidebar showing OpenCode Go rolling/weekly/monthly usage limits for sessions that use the `opencode-go` provider.
|
|
96
97
|
- Subagent status (running, done, failed, elapsed time, and token/context usage) in the sidebar.
|
|
97
98
|
- Local capture of OpenCode hook/event data without redaction.
|
|
98
99
|
- A local web viewer for reconstructed sessions, user turns, hidden request context, system/messages transforms, and assistant thinking/response sequences.
|
|
99
100
|
- Native OpenCode footer components (project directory and version) remain visible — the plugin does not override `sidebar_footer` or `home_prompt_right` slots.
|
|
100
101
|
|
|
101
|
-
The right sidebar contains
|
|
102
|
+
The right sidebar contains the plugin sections: `Token Usage`, `Go Usage` (when enabled and the session uses `opencode-go`), and `Subagents`. Click any section header to collapse or expand it. Token usage is aggregated across the full session; prompt-right `used` and `cache` values continue to represent the latest completed assistant response.
|
|
102
103
|
|
|
103
104
|
## TUI Metrics Configuration
|
|
104
105
|
|
|
@@ -118,6 +119,28 @@ With a custom database path, the configuration file is created in that database'
|
|
|
118
119
|
|
|
119
120
|
`promptRightMetrics` controls both the fields and their order. Supported values are `tps`, `avg`, `ttft`, `used`, `cache`, `input`, `output`, and `reasoning`. Values that are not recognized are ignored; an empty or invalid configuration uses the default. Restart OpenCode after editing this file.
|
|
120
121
|
|
|
122
|
+
## Go Usage Configuration
|
|
123
|
+
|
|
124
|
+
The `Go Usage` sidebar shows the rolling (5 hour), weekly, and monthly usage limits of your OpenCode Go subscription for sessions that use the `opencode-go` provider. It is disabled by default and opt-in:
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"goUsage": {
|
|
129
|
+
"enabled": true,
|
|
130
|
+
"cookie": "Fe26.2**...",
|
|
131
|
+
"workspaceID": "wrk_...",
|
|
132
|
+
"refreshMs": 300000
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
- `enabled` — set to `true` to activate the section. Defaults to `false`.
|
|
138
|
+
- `cookie` — the `auth` session cookie for `opencode.ai` (see below).
|
|
139
|
+
- `workspaceID` — your workspace id, visible in the console URL (`/workspace/<workspaceID>/go`).
|
|
140
|
+
- `refreshMs` — how often to re-fetch usage from the console. Defaults to `300000` (5 minutes); values below 60000 are clamped.
|
|
141
|
+
|
|
142
|
+
To get the cookie, log in to `https://opencode.ai`, open the workspace `/go` page, then copy the `auth` cookie value from your browser's DevTools (Application → Cookies → `https://opencode.ai`). The cookie lasts up to a year; if the section shows an error, copy it again. Restart OpenCode after editing this file.
|
|
143
|
+
|
|
121
144
|
## Open The Viewer
|
|
122
145
|
|
|
123
146
|
Start the local web viewer and open it in your browser:
|
|
@@ -101,8 +101,15 @@ type InsightsOptions = {
|
|
|
101
101
|
dbPath?: unknown;
|
|
102
102
|
retentionDays?: unknown;
|
|
103
103
|
};
|
|
104
|
+
type GoUsageConfig = {
|
|
105
|
+
enabled: boolean;
|
|
106
|
+
cookie: string;
|
|
107
|
+
workspaceID: string;
|
|
108
|
+
refreshMs: number;
|
|
109
|
+
};
|
|
104
110
|
type InsightsConfig = {
|
|
105
111
|
promptRightMetrics: PromptRightMetric[];
|
|
112
|
+
goUsage: GoUsageConfig;
|
|
106
113
|
};
|
|
107
114
|
declare function defaultDataDir(): string;
|
|
108
115
|
declare function resolveCapturePath(options?: InsightsOptions): string;
|
|
@@ -145,4 +152,4 @@ declare class SqliteCaptureStore implements CaptureStore {
|
|
|
145
152
|
declare function createCaptureStore(options?: InsightsOptions): CaptureStore;
|
|
146
153
|
declare function resolveRetentionDays(value: unknown): number;
|
|
147
154
|
|
|
148
|
-
export { type AssistantResponseUsage as A, renderMetricsText as B, type CaptureRecord as C, DEFAULT_PROMPT_RIGHT_METRICS as D, renderPromptRightMetricsText as E, renderResponseMetricsText as F,
|
|
155
|
+
export { type AssistantResponseUsage as A, renderMetricsText as B, type CaptureRecord as C, DEFAULT_PROMPT_RIGHT_METRICS as D, renderPromptRightMetricsText as E, renderResponseMetricsText as F, type GoUsageConfig as G, renderSessionTokenUsage as H, type InsightsConfig as I, JsonlCaptureStore as J, resolveCapturePath as K, resolveInsightsConfigPath as L, type MessageTiming as M, resolveRetentionDays as N, type PromptRightMetric as P, type SessionAverage as S, type CaptureKind as a, type CaptureStore as b, type InsightsOptions as c, type MetricsState as d, type SessionTokenUsage as e, SqliteCaptureStore as f, type SqliteDb as g, type StreamSample as h, createCaptureStore as i, createMetricsState as j, defaultDataDir as k, estimateStreamTokens as l, extractEventType as m, getSessionTokenUsage as n, normalizeChatHeadersCapture as o, normalizeChatMessageCapture as p, normalizeChatParamsCapture as q, normalizeEventCapture as r, normalizeExperimentalChatMessagesTransformCapture as s, normalizeExperimentalChatSystemTransformCapture as t, normalizeToolCapture as u, openDatabase as v, readInsightsConfig as w, recordAssistantDelta as x, recordAssistantMessage as y, recordToolActivity as z };
|
|
@@ -268,6 +268,8 @@ import { dirname, join } from "path";
|
|
|
268
268
|
import { homedir } from "os";
|
|
269
269
|
var DEFAULT_RETENTION_DAYS = 1;
|
|
270
270
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
271
|
+
var DEFAULT_GO_USAGE_REFRESH_MS = 3e5;
|
|
272
|
+
var MIN_GO_USAGE_REFRESH_MS = 6e4;
|
|
271
273
|
var sequence = 0;
|
|
272
274
|
function nextID(timestamp) {
|
|
273
275
|
sequence += 1;
|
|
@@ -346,12 +348,26 @@ async function readInsightsConfig(options = {}) {
|
|
|
346
348
|
}
|
|
347
349
|
}
|
|
348
350
|
function defaultInsightsConfig() {
|
|
349
|
-
return { promptRightMetrics: [...DEFAULT_PROMPT_RIGHT_METRICS] };
|
|
351
|
+
return { promptRightMetrics: [...DEFAULT_PROMPT_RIGHT_METRICS], goUsage: defaultGoUsageConfig() };
|
|
352
|
+
}
|
|
353
|
+
function defaultGoUsageConfig() {
|
|
354
|
+
return { enabled: false, cookie: "", workspaceID: "", refreshMs: DEFAULT_GO_USAGE_REFRESH_MS };
|
|
350
355
|
}
|
|
351
356
|
function insightsConfigFrom(value) {
|
|
352
|
-
|
|
353
|
-
const metrics =
|
|
354
|
-
return
|
|
357
|
+
const record = isRecord(value) ? value : {};
|
|
358
|
+
const metrics = Array.isArray(record.promptRightMetrics) ? record.promptRightMetrics.filter(isPromptRightMetric) : [];
|
|
359
|
+
return {
|
|
360
|
+
promptRightMetrics: metrics.length ? metrics : [...DEFAULT_PROMPT_RIGHT_METRICS],
|
|
361
|
+
goUsage: goUsageConfigFrom(record.goUsage)
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
function goUsageConfigFrom(value) {
|
|
365
|
+
const record = isRecord(value) ? value : {};
|
|
366
|
+
const enabled = record.enabled === true;
|
|
367
|
+
const cookie = typeof record.cookie === "string" && record.cookie.length > 0 ? record.cookie : "";
|
|
368
|
+
const workspaceID = typeof record.workspaceID === "string" && record.workspaceID.length > 0 ? record.workspaceID : "";
|
|
369
|
+
const refreshMs = typeof record.refreshMs === "number" && Number.isFinite(record.refreshMs) ? Math.max(MIN_GO_USAGE_REFRESH_MS, Math.trunc(record.refreshMs)) : DEFAULT_GO_USAGE_REFRESH_MS;
|
|
370
|
+
return { enabled, cookie, workspaceID, refreshMs };
|
|
355
371
|
}
|
|
356
372
|
function isPromptRightMetric(value) {
|
|
357
373
|
return value === "tps" || value === "avg" || value === "ttft" || value === "used" || value === "cache" || value === "input" || value === "output" || value === "reasoning";
|
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Plugin } from '@opencode-ai/plugin';
|
|
2
2
|
import { TuiPlugin } from '@opencode-ai/plugin/tui';
|
|
3
|
-
export { A as AssistantResponseUsage, a as CaptureKind, C as CaptureRecord, b as CaptureStore, D as DEFAULT_PROMPT_RIGHT_METRICS, I as InsightsConfig, c as InsightsOptions, J as JsonlCaptureStore, M as MessageTiming, d as MetricsState, P as PromptRightMetric, S as SessionAverage, e as SessionTokenUsage, f as SqliteCaptureStore, g as SqliteDb, h as StreamSample, i as createCaptureStore, j as createMetricsState, k as defaultDataDir, l as estimateStreamTokens, m as extractEventType, n as getSessionTokenUsage, o as normalizeChatHeadersCapture, p as normalizeChatMessageCapture, q as normalizeChatParamsCapture, r as normalizeEventCapture, s as normalizeExperimentalChatMessagesTransformCapture, t as normalizeExperimentalChatSystemTransformCapture, u as normalizeToolCapture, v as openDatabase, w as readInsightsConfig, x as recordAssistantDelta, y as recordAssistantMessage, z as recordToolActivity, B as renderMetricsText, E as renderPromptRightMetricsText, F as renderResponseMetricsText,
|
|
3
|
+
export { A as AssistantResponseUsage, a as CaptureKind, C as CaptureRecord, b as CaptureStore, D as DEFAULT_PROMPT_RIGHT_METRICS, G as GoUsageConfig, I as InsightsConfig, c as InsightsOptions, J as JsonlCaptureStore, M as MessageTiming, d as MetricsState, P as PromptRightMetric, S as SessionAverage, e as SessionTokenUsage, f as SqliteCaptureStore, g as SqliteDb, h as StreamSample, i as createCaptureStore, j as createMetricsState, k as defaultDataDir, l as estimateStreamTokens, m as extractEventType, n as getSessionTokenUsage, o as normalizeChatHeadersCapture, p as normalizeChatMessageCapture, q as normalizeChatParamsCapture, r as normalizeEventCapture, s as normalizeExperimentalChatMessagesTransformCapture, t as normalizeExperimentalChatSystemTransformCapture, u as normalizeToolCapture, v as openDatabase, w as readInsightsConfig, x as recordAssistantDelta, y as recordAssistantMessage, z as recordToolActivity, B as renderMetricsText, E as renderPromptRightMetricsText, F as renderResponseMetricsText, H as renderSessionTokenUsage, K as resolveCapturePath, L as resolveInsightsConfigPath, N as resolveRetentionDays } from './capture-BpktoFGh.js';
|
|
4
4
|
|
|
5
5
|
type SubagentStatus = "running" | "done" | "error";
|
|
6
6
|
type SubagentInfo = {
|
package/dist/index.js
CHANGED
package/dist/tui.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
recordToolActivity,
|
|
13
13
|
renderPromptRightMetricsText,
|
|
14
14
|
renderSessionTokenUsage
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-RWPY5QOE.js";
|
|
16
16
|
|
|
17
17
|
// src/tui.tsx
|
|
18
18
|
import { createTextAttributes, StyledText } from "@opentui/core";
|
|
@@ -37,6 +37,120 @@ function hasRenderStateChanged(previous, next) {
|
|
|
37
37
|
return !previous || previous.content !== next.content || previous.visible !== next.visible || previous.height !== next.height;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
// src/go-usage.ts
|
|
41
|
+
var GoUsageError = class extends Error {
|
|
42
|
+
};
|
|
43
|
+
var USAGE_PATTERN = /(rollingUsage|weeklyUsage|monthlyUsage):\$R\[(\d+)\]=\{([^{}]*)\}/g;
|
|
44
|
+
var KEY_PATTERN = /([a-zA-Z]+):/g;
|
|
45
|
+
function parseGoUsageHtml(html) {
|
|
46
|
+
const usage = {};
|
|
47
|
+
USAGE_PATTERN.lastIndex = 0;
|
|
48
|
+
let match;
|
|
49
|
+
while (match = USAGE_PATTERN.exec(html)) {
|
|
50
|
+
const key = match[1];
|
|
51
|
+
const literal = match[3] ?? "";
|
|
52
|
+
try {
|
|
53
|
+
usage[key] = JSON.parse(`{${literal.replace(KEY_PATTERN, '"$1":')}}`);
|
|
54
|
+
} catch {
|
|
55
|
+
return void 0;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!usage.rollingUsage || !usage.weeklyUsage || !usage.monthlyUsage) return void 0;
|
|
59
|
+
return usage;
|
|
60
|
+
}
|
|
61
|
+
async function fetchGoUsage(input, fetchImpl = fetch) {
|
|
62
|
+
const response = await fetchImpl(`https://opencode.ai/workspace/${input.workspaceID}/go`, {
|
|
63
|
+
headers: {
|
|
64
|
+
"user-agent": "Mozilla/5.0",
|
|
65
|
+
cookie: `auth=${input.cookie}`
|
|
66
|
+
},
|
|
67
|
+
redirect: "manual"
|
|
68
|
+
});
|
|
69
|
+
if (response.status >= 300 && response.status < 400) {
|
|
70
|
+
throw new GoUsageError("console redirected to login; the auth cookie may be expired");
|
|
71
|
+
}
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
throw new GoUsageError(`console request failed with status ${response.status}`);
|
|
74
|
+
}
|
|
75
|
+
const usage = parseGoUsageHtml(await response.text());
|
|
76
|
+
if (!usage) throw new GoUsageError("could not parse go usage from the console page");
|
|
77
|
+
return usage;
|
|
78
|
+
}
|
|
79
|
+
function formatReset(seconds) {
|
|
80
|
+
const total = Math.max(0, Math.floor(seconds));
|
|
81
|
+
const days = Math.floor(total / 86400);
|
|
82
|
+
const hours = Math.floor(total % 86400 / 3600);
|
|
83
|
+
const minutes = Math.floor(total % 3600 / 60);
|
|
84
|
+
if (days > 0) return hours > 0 ? `${days}d ${hours}h` : `${days}d`;
|
|
85
|
+
if (hours > 0) return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
|
|
86
|
+
return `${minutes}m`;
|
|
87
|
+
}
|
|
88
|
+
function formatGoUsageRow(row) {
|
|
89
|
+
const fill = Math.min(4, Math.ceil(row.usagePercent / 25));
|
|
90
|
+
const bar = "\u2588".repeat(fill) + "\u2591".repeat(4 - fill);
|
|
91
|
+
return `${row.label.padEnd(9)}${`${row.usagePercent}%`.padEnd(3)} ${bar} ${row.reset}`;
|
|
92
|
+
}
|
|
93
|
+
function goUsageSectionVisible(config, usesGoProvider) {
|
|
94
|
+
return usesGoProvider && config.goUsage.enabled && config.goUsage.cookie.length > 0 && config.goUsage.workspaceID.length > 0;
|
|
95
|
+
}
|
|
96
|
+
function createGoProviderTracker() {
|
|
97
|
+
const providers = /* @__PURE__ */ new Map();
|
|
98
|
+
return {
|
|
99
|
+
record(sessionID, providerID) {
|
|
100
|
+
if (providerID) providers.set(sessionID, providerID);
|
|
101
|
+
},
|
|
102
|
+
usesOpenCodeGo(sessionID) {
|
|
103
|
+
return providers.get(sessionID) === "opencode-go";
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function goUsageRows(state, now) {
|
|
108
|
+
if (!state.data) return void 0;
|
|
109
|
+
const elapsedSeconds = state.lastFetchAt === void 0 ? 0 : Math.max(0, (now - state.lastFetchAt) / 1e3);
|
|
110
|
+
return [
|
|
111
|
+
{
|
|
112
|
+
label: "Rolling",
|
|
113
|
+
usagePercent: state.data.rollingUsage.usagePercent,
|
|
114
|
+
reset: formatReset(state.data.rollingUsage.resetInSec - elapsedSeconds)
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
label: "Weekly",
|
|
118
|
+
usagePercent: state.data.weeklyUsage.usagePercent,
|
|
119
|
+
reset: formatReset(state.data.weeklyUsage.resetInSec - elapsedSeconds)
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
label: "Monthly",
|
|
123
|
+
usagePercent: state.data.monthlyUsage.usagePercent,
|
|
124
|
+
reset: formatReset(state.data.monthlyUsage.resetInSec - elapsedSeconds)
|
|
125
|
+
}
|
|
126
|
+
];
|
|
127
|
+
}
|
|
128
|
+
function createGoUsageRefresher(config, fetchImpl = fetch) {
|
|
129
|
+
const state = {};
|
|
130
|
+
let inflight;
|
|
131
|
+
async function refresh(now = Date.now()) {
|
|
132
|
+
if (inflight) {
|
|
133
|
+
await inflight;
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
if (state.lastFetchAt !== void 0 && now - state.lastFetchAt < config.refreshMs) return false;
|
|
137
|
+
inflight = (async () => {
|
|
138
|
+
try {
|
|
139
|
+
state.data = await fetchGoUsage({ cookie: config.cookie, workspaceID: config.workspaceID }, fetchImpl);
|
|
140
|
+
state.error = void 0;
|
|
141
|
+
} catch (error) {
|
|
142
|
+
state.error = error instanceof Error ? error.message : String(error);
|
|
143
|
+
} finally {
|
|
144
|
+
state.lastFetchAt = now;
|
|
145
|
+
inflight = void 0;
|
|
146
|
+
}
|
|
147
|
+
})();
|
|
148
|
+
await inflight;
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
return { state, refresh };
|
|
152
|
+
}
|
|
153
|
+
|
|
40
154
|
// src/tui.tsx
|
|
41
155
|
import { Fragment, jsx, jsxs } from "@opentui/solid/jsx-runtime";
|
|
42
156
|
function isSessionID(value) {
|
|
@@ -119,6 +233,74 @@ function TokenUsageSidebar(props) {
|
|
|
119
233
|
}
|
|
120
234
|
);
|
|
121
235
|
}
|
|
236
|
+
function GoUsageSidebar(props) {
|
|
237
|
+
let text;
|
|
238
|
+
const [collapsed, setCollapsed] = createSignal(false);
|
|
239
|
+
const titleAttributes = createTextAttributes({ bold: true });
|
|
240
|
+
let previous;
|
|
241
|
+
const toggle = (event) => {
|
|
242
|
+
if (!text || event.y !== text.y) return;
|
|
243
|
+
setCollapsed((prev) => !prev);
|
|
244
|
+
sync();
|
|
245
|
+
};
|
|
246
|
+
const sync = () => {
|
|
247
|
+
if (!text) return;
|
|
248
|
+
const visible = goUsageSectionVisible(props.config, props.tracker.usesOpenCodeGo(props.sessionID));
|
|
249
|
+
if (visible) props.refresh();
|
|
250
|
+
const rows = visible ? goUsageRows(props.state, Date.now()) : void 0;
|
|
251
|
+
const error = props.state.error;
|
|
252
|
+
const showContent = visible && (rows || error);
|
|
253
|
+
const signature = showContent ? JSON.stringify({ collapsed: collapsed(), rows, error }) : "";
|
|
254
|
+
const next = {
|
|
255
|
+
content: signature,
|
|
256
|
+
visible: signature.length > 0,
|
|
257
|
+
height: signature.length > 0 ? "auto" : 0
|
|
258
|
+
};
|
|
259
|
+
if (!hasRenderStateChanged(previous, next)) return;
|
|
260
|
+
previous = next;
|
|
261
|
+
text.visible = next.visible;
|
|
262
|
+
text.height = next.height;
|
|
263
|
+
text.content = showContent ? renderGoUsageSidebar(rows, error, props.api, titleAttributes, collapsed()) : "";
|
|
264
|
+
props.api.renderer.requestRender();
|
|
265
|
+
};
|
|
266
|
+
const unsubscribe = props.subscribe(sync);
|
|
267
|
+
const unsubscribeGoUsage = props.goUsageSubscribe(sync);
|
|
268
|
+
const timer = setInterval(sync, 1e3);
|
|
269
|
+
onCleanup(() => {
|
|
270
|
+
unsubscribe();
|
|
271
|
+
unsubscribeGoUsage();
|
|
272
|
+
clearInterval(timer);
|
|
273
|
+
});
|
|
274
|
+
return /* @__PURE__ */ jsx(
|
|
275
|
+
"text",
|
|
276
|
+
{
|
|
277
|
+
ref: (ref) => {
|
|
278
|
+
text = ref;
|
|
279
|
+
sync();
|
|
280
|
+
},
|
|
281
|
+
onMouseDown: toggle,
|
|
282
|
+
fg: props.api.theme.current.textMuted,
|
|
283
|
+
children: ""
|
|
284
|
+
}
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
function renderGoUsageSidebar(rows, error, api, titleAttributes, collapsed) {
|
|
288
|
+
const chunks = [
|
|
289
|
+
textChunk(`${collapsed ? "\u25B6" : "\u25BC"} Go Usage
|
|
290
|
+
`, api.theme.current.text, titleAttributes)
|
|
291
|
+
];
|
|
292
|
+
if (!collapsed) {
|
|
293
|
+
if (rows && rows.length > 0) {
|
|
294
|
+
for (const [index, row] of rows.entries()) {
|
|
295
|
+
if (index > 0) chunks.push(textChunk("\n"));
|
|
296
|
+
chunks.push(textChunk(formatGoUsageRow(row), api.theme.current.textMuted));
|
|
297
|
+
}
|
|
298
|
+
} else if (error) {
|
|
299
|
+
chunks.push(textChunk(`Go usage: ${error}`, api.theme.current.error));
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return new StyledText(chunks);
|
|
303
|
+
}
|
|
122
304
|
function SubagentSidebar(props) {
|
|
123
305
|
let text;
|
|
124
306
|
const [collapsed, setCollapsed] = createSignal(false);
|
|
@@ -238,7 +420,13 @@ var tui = async (api, options) => {
|
|
|
238
420
|
const subagents = createSubagentState();
|
|
239
421
|
const metricListeners = createListenerRegistry();
|
|
240
422
|
const subagentListeners = createListenerRegistry();
|
|
423
|
+
const goUsageListeners = createListenerRegistry();
|
|
241
424
|
const hydratedSessions = /* @__PURE__ */ new Set();
|
|
425
|
+
const goProviderTracker = createGoProviderTracker();
|
|
426
|
+
const goUsage = createGoUsageRefresher(config.goUsage);
|
|
427
|
+
const refreshGoUsage = async () => {
|
|
428
|
+
if (await goUsage.refresh()) goUsageListeners.notify();
|
|
429
|
+
};
|
|
242
430
|
const hydrateSessionMetrics = async (sessionID) => {
|
|
243
431
|
if (!isSessionID(sessionID) || hydratedSessions.has(sessionID)) return;
|
|
244
432
|
hydratedSessions.add(sessionID);
|
|
@@ -247,6 +435,8 @@ var tui = async (api, options) => {
|
|
|
247
435
|
const messages = response.data ?? [];
|
|
248
436
|
for (const message of messages) {
|
|
249
437
|
const info = message.info;
|
|
438
|
+
const providerID = info.providerID;
|
|
439
|
+
goProviderTracker.record(sessionID, typeof providerID === "string" ? providerID : void 0);
|
|
250
440
|
if (info.role !== "assistant" || typeof info.time.completed !== "number") continue;
|
|
251
441
|
const input = {
|
|
252
442
|
sessionID: info.sessionID,
|
|
@@ -279,6 +469,9 @@ var tui = async (api, options) => {
|
|
|
279
469
|
const offMessage = api.event.on("message.updated", (evt) => {
|
|
280
470
|
const info = evt.properties.info;
|
|
281
471
|
if (applySubagentEvent(subagents, evt)) subagentListeners.notify();
|
|
472
|
+
const sessionID = info.sessionID ?? evt.properties.sessionID;
|
|
473
|
+
const providerID = info.providerID ?? info.model?.providerID;
|
|
474
|
+
goProviderTracker.record(sessionID, typeof providerID === "string" ? providerID : void 0);
|
|
282
475
|
if (info.role !== "assistant") return;
|
|
283
476
|
const messageInput = {
|
|
284
477
|
sessionID: info.sessionID ?? evt.properties.sessionID,
|
|
@@ -308,6 +501,13 @@ var tui = async (api, options) => {
|
|
|
308
501
|
});
|
|
309
502
|
const offSessionUpdated = api.event.on("session.updated", (evt) => {
|
|
310
503
|
if (applySubagentEvent(subagents, evt)) subagentListeners.notify();
|
|
504
|
+
const info = evt.properties.info;
|
|
505
|
+
const sessionID = typeof info?.id === "string" ? info.id : void 0;
|
|
506
|
+
const providerID = info?.model?.providerID;
|
|
507
|
+
if (sessionID && typeof providerID === "string") {
|
|
508
|
+
goProviderTracker.record(sessionID, providerID);
|
|
509
|
+
metricListeners.notify();
|
|
510
|
+
}
|
|
311
511
|
});
|
|
312
512
|
const offSessionStatus = api.event.on("session.status", (evt) => {
|
|
313
513
|
if (applySubagentEvent(subagents, evt)) subagentListeners.notify();
|
|
@@ -347,6 +547,19 @@ var tui = async (api, options) => {
|
|
|
347
547
|
hydrate: () => void hydrateSessionMetrics(props.session_id)
|
|
348
548
|
}
|
|
349
549
|
),
|
|
550
|
+
/* @__PURE__ */ jsx(
|
|
551
|
+
GoUsageSidebar,
|
|
552
|
+
{
|
|
553
|
+
api,
|
|
554
|
+
state: goUsage.state,
|
|
555
|
+
config,
|
|
556
|
+
tracker: goProviderTracker,
|
|
557
|
+
sessionID: props.session_id,
|
|
558
|
+
subscribe: metricListeners.subscribe,
|
|
559
|
+
goUsageSubscribe: goUsageListeners.subscribe,
|
|
560
|
+
refresh: () => void refreshGoUsage()
|
|
561
|
+
}
|
|
562
|
+
),
|
|
350
563
|
/* @__PURE__ */ jsx(
|
|
351
564
|
SubagentSidebar,
|
|
352
565
|
{
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@rejacky/opencode-insights",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.13",
|
|
5
5
|
"description": "OpenCode plugin for local request capture, TPS metrics, and subagent status visibility.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"author": "opencode-insights contributors",
|
|
@@ -68,18 +68,18 @@
|
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
70
|
"@opencode-ai/plugin": ">=1.15.0 <2",
|
|
71
|
-
"@opentui/core": ">=0.4.0 <0.
|
|
72
|
-
"@opentui/solid": ">=0.4.0 <0.
|
|
73
|
-
"solid-js": "1.9.12"
|
|
71
|
+
"@opentui/core": ">=0.4.0 <0.6",
|
|
72
|
+
"@opentui/solid": ">=0.4.0 <0.6",
|
|
73
|
+
"solid-js": "^1.9.12"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@opencode-ai/plugin": "^1.17.13",
|
|
77
|
-
"@opentui/core": "^0.
|
|
78
|
-
"@opentui/solid": "^0.
|
|
77
|
+
"@opentui/core": "^0.5.1",
|
|
78
|
+
"@opentui/solid": "^0.5.1",
|
|
79
79
|
"@types/better-sqlite3": "^7.6.13",
|
|
80
80
|
"@types/node": "^24.12.2",
|
|
81
81
|
"@types/sql.js": "^1.4.11",
|
|
82
|
-
"solid-js": "1.9.12",
|
|
82
|
+
"solid-js": "^1.9.12",
|
|
83
83
|
"tsup": "^8.5.1",
|
|
84
84
|
"typescript": "^6.0.3",
|
|
85
85
|
"vitest": "^4.1.10"
|