@rejacky/opencode-insights 0.1.12 → 0.2.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.md +42 -13
- package/dist/{capture-BIiGg2nW.d.ts → capture-CwesXqmX.d.ts} +12 -1
- package/dist/{chunk-3YLLHABZ.js → chunk-RZGCLQ2M.js} +69 -15
- package/dist/cli.d.ts +3 -3
- package/dist/cli.js +35 -34
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -2
- package/dist/tui.js +224 -1
- package/package.json +1 -1
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:
|
|
@@ -216,24 +239,30 @@ If SQLite is unavailable in the plugin runtime, the fallback path is:
|
|
|
216
239
|
~/.opencode-insights/insights.sqlite.jsonl
|
|
217
240
|
```
|
|
218
241
|
|
|
219
|
-
The database keeps one day of captures by default and auto-cleans older rows on
|
|
242
|
+
The database keeps one day of captures by default and auto-cleans older rows on
|
|
243
|
+
startup and after new captures; `retentionDays` sets how many days to keep (`0`
|
|
244
|
+
disables auto-cleaning).
|
|
220
245
|
|
|
221
|
-
|
|
246
|
+
Storage settings live in the config file `~/.opencode-insights/config.jsonc` (JSONC —
|
|
247
|
+
comments allowed). The plugin creates it on first run with `dbPath` commented out
|
|
248
|
+
(uncomment to relocate the database) and `retentionDays` defaulting to 1:
|
|
222
249
|
|
|
223
|
-
```
|
|
250
|
+
```jsonc
|
|
224
251
|
{
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
"retentionDays": 1
|
|
231
|
-
}
|
|
232
|
-
]
|
|
233
|
-
]
|
|
252
|
+
// Database file. Default: ~/.opencode-insights/insights.sqlite
|
|
253
|
+
// "dbPath": "/absolute/path/to/insights.sqlite",
|
|
254
|
+
"retentionDays": 1,
|
|
255
|
+
"promptRightMetrics": ["tps", "avg", "used", "cache"],
|
|
256
|
+
"goUsage": { "enabled": false, "cookie": "", "workspaceID": "", "refreshMs": 300000 }
|
|
234
257
|
}
|
|
235
258
|
```
|
|
236
259
|
|
|
260
|
+
A legacy `config.json` is still honored when `config.jsonc` does not exist. Plugin
|
|
261
|
+
params such as `{ "dbPath": … }` in `opencode.json` are ignored after the upgrade —
|
|
262
|
+
if you previously set `dbPath` or `retentionDays` there, copy those values into
|
|
263
|
+
`~/.opencode-insights/config.jsonc`. CLI commands read the configured database path
|
|
264
|
+
from this file; the former `--db`/`--data-dir`/`--retention-days` flags are removed.
|
|
265
|
+
|
|
237
266
|
## Privacy Model
|
|
238
267
|
|
|
239
268
|
This plugin intentionally does not redact anything. It stores data locally exactly as OpenCode exposes it to plugin hooks and events.
|
|
@@ -101,13 +101,24 @@ 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;
|
|
113
|
+
dbPath?: string | undefined;
|
|
114
|
+
retentionDays?: number | undefined;
|
|
106
115
|
};
|
|
107
116
|
declare function defaultDataDir(): string;
|
|
108
117
|
declare function resolveCapturePath(options?: InsightsOptions): string;
|
|
109
118
|
declare function resolveInsightsConfigPath(options?: InsightsOptions): string;
|
|
119
|
+
declare function resolveLegacyInsightsConfigPath(options?: InsightsOptions): string;
|
|
110
120
|
declare function readInsightsConfig(options?: InsightsOptions): Promise<InsightsConfig>;
|
|
121
|
+
declare function insightsOptionsFromConfig(config: InsightsConfig, dataDir?: string): InsightsOptions;
|
|
111
122
|
declare function normalizeChatMessageCapture(input: unknown, output: unknown, timestamp?: number): CaptureRecord;
|
|
112
123
|
declare function normalizeChatParamsCapture(input: unknown, output: unknown, timestamp?: number): CaptureRecord;
|
|
113
124
|
declare function normalizeChatHeadersCapture(input: unknown, output: unknown, timestamp?: number): CaptureRecord;
|
|
@@ -145,4 +156,4 @@ declare class SqliteCaptureStore implements CaptureStore {
|
|
|
145
156
|
declare function createCaptureStore(options?: InsightsOptions): CaptureStore;
|
|
146
157
|
declare function resolveRetentionDays(value: unknown): number;
|
|
147
158
|
|
|
148
|
-
export { type AssistantResponseUsage as A,
|
|
159
|
+
export { type AssistantResponseUsage as A, recordToolActivity as B, type CaptureRecord as C, DEFAULT_PROMPT_RIGHT_METRICS as D, renderMetricsText as E, renderPromptRightMetricsText as F, type GoUsageConfig as G, renderResponseMetricsText as H, type InsightsConfig as I, JsonlCaptureStore as J, renderSessionTokenUsage as K, resolveCapturePath as L, type MessageTiming as M, resolveInsightsConfigPath as N, resolveLegacyInsightsConfigPath as O, type PromptRightMetric as P, resolveRetentionDays as Q, 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, insightsOptionsFromConfig as o, normalizeChatHeadersCapture as p, normalizeChatMessageCapture as q, normalizeChatParamsCapture as r, normalizeEventCapture as s, normalizeExperimentalChatMessagesTransformCapture as t, normalizeExperimentalChatSystemTransformCapture as u, normalizeToolCapture as v, openDatabase as w, readInsightsConfig as x, recordAssistantDelta as y, recordAssistantMessage as z };
|
|
@@ -266,8 +266,11 @@ import { mkdir, appendFile, readFile, writeFile } from "fs/promises";
|
|
|
266
266
|
import { existsSync, readFileSync } from "fs";
|
|
267
267
|
import { dirname, join } from "path";
|
|
268
268
|
import { homedir } from "os";
|
|
269
|
+
import { parse } from "jsonc-parser";
|
|
269
270
|
var DEFAULT_RETENTION_DAYS = 1;
|
|
270
271
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
272
|
+
var DEFAULT_GO_USAGE_REFRESH_MS = 3e5;
|
|
273
|
+
var MIN_GO_USAGE_REFRESH_MS = 6e4;
|
|
271
274
|
var sequence = 0;
|
|
272
275
|
function nextID(timestamp) {
|
|
273
276
|
sequence += 1;
|
|
@@ -326,32 +329,81 @@ function resolveCapturePath(options = {}) {
|
|
|
326
329
|
return join(dataDir, "insights.sqlite");
|
|
327
330
|
}
|
|
328
331
|
function resolveInsightsConfigPath(options = {}) {
|
|
329
|
-
return join(
|
|
332
|
+
return join(resolveConfigDataDir(options), "config.jsonc");
|
|
333
|
+
}
|
|
334
|
+
function resolveLegacyInsightsConfigPath(options = {}) {
|
|
335
|
+
return join(resolveConfigDataDir(options), "config.json");
|
|
336
|
+
}
|
|
337
|
+
function resolveConfigDataDir(options = {}) {
|
|
338
|
+
return typeof options.dataDir === "string" && options.dataDir.length > 0 ? options.dataDir : defaultDataDir();
|
|
330
339
|
}
|
|
331
340
|
async function readInsightsConfig(options = {}) {
|
|
332
|
-
const
|
|
333
|
-
if (
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
return defaultInsightsConfig();
|
|
340
|
-
}
|
|
341
|
+
const jsoncPath = resolveInsightsConfigPath(options);
|
|
342
|
+
if (existsSync(jsoncPath)) {
|
|
343
|
+
return parseInsightsConfigFile(jsoncPath);
|
|
344
|
+
}
|
|
345
|
+
const legacyPath = resolveLegacyInsightsConfigPath(options);
|
|
346
|
+
if (existsSync(legacyPath)) {
|
|
347
|
+
return parseInsightsConfigFile(legacyPath);
|
|
341
348
|
}
|
|
342
349
|
try {
|
|
343
|
-
|
|
350
|
+
await mkdir(dirname(jsoncPath), { recursive: true });
|
|
351
|
+
await writeFile(jsoncPath, defaultInsightsConfigJsonc(), "utf8");
|
|
352
|
+
} catch {
|
|
353
|
+
}
|
|
354
|
+
return defaultInsightsConfig();
|
|
355
|
+
}
|
|
356
|
+
async function parseInsightsConfigFile(path) {
|
|
357
|
+
try {
|
|
358
|
+
const parseErrors = [];
|
|
359
|
+
const parsed = parse(await readFile(path, "utf8"), parseErrors, { allowTrailingComma: true });
|
|
360
|
+
if (parseErrors.length > 0) return defaultInsightsConfig();
|
|
361
|
+
return insightsConfigFrom(parsed);
|
|
344
362
|
} catch {
|
|
345
363
|
return defaultInsightsConfig();
|
|
346
364
|
}
|
|
347
365
|
}
|
|
366
|
+
function defaultInsightsConfigJsonc() {
|
|
367
|
+
return [
|
|
368
|
+
"{",
|
|
369
|
+
" // Database file. Default: ~/.opencode-insights/insights.sqlite",
|
|
370
|
+
' // "dbPath": "/absolute/path/to/insights.sqlite",',
|
|
371
|
+
` "retentionDays": ${DEFAULT_RETENTION_DAYS},`,
|
|
372
|
+
` "promptRightMetrics": ${JSON.stringify(DEFAULT_PROMPT_RIGHT_METRICS)},`,
|
|
373
|
+
` "goUsage": ${JSON.stringify(defaultGoUsageConfig())}`,
|
|
374
|
+
"}",
|
|
375
|
+
""
|
|
376
|
+
].join("\n");
|
|
377
|
+
}
|
|
378
|
+
function insightsOptionsFromConfig(config, dataDir) {
|
|
379
|
+
return compactUndefined({ dataDir, dbPath: config.dbPath, retentionDays: config.retentionDays });
|
|
380
|
+
}
|
|
348
381
|
function defaultInsightsConfig() {
|
|
349
|
-
return { promptRightMetrics: [...DEFAULT_PROMPT_RIGHT_METRICS] };
|
|
382
|
+
return { promptRightMetrics: [...DEFAULT_PROMPT_RIGHT_METRICS], goUsage: defaultGoUsageConfig() };
|
|
383
|
+
}
|
|
384
|
+
function defaultGoUsageConfig() {
|
|
385
|
+
return { enabled: false, cookie: "", workspaceID: "", refreshMs: DEFAULT_GO_USAGE_REFRESH_MS };
|
|
350
386
|
}
|
|
351
387
|
function insightsConfigFrom(value) {
|
|
352
|
-
|
|
353
|
-
const metrics =
|
|
354
|
-
|
|
388
|
+
const record = isRecord(value) ? value : {};
|
|
389
|
+
const metrics = Array.isArray(record.promptRightMetrics) ? record.promptRightMetrics.filter(isPromptRightMetric) : [];
|
|
390
|
+
const dbPath = typeof record.dbPath === "string" && record.dbPath.trim().length > 0 ? record.dbPath.trim() : void 0;
|
|
391
|
+
const rawRetention = record.retentionDays;
|
|
392
|
+
const retentionDays = rawRetention === void 0 || rawRetention === null || rawRetention === "" ? void 0 : resolveRetentionDays(rawRetention);
|
|
393
|
+
return {
|
|
394
|
+
promptRightMetrics: metrics.length ? metrics : [...DEFAULT_PROMPT_RIGHT_METRICS],
|
|
395
|
+
goUsage: goUsageConfigFrom(record.goUsage),
|
|
396
|
+
dbPath,
|
|
397
|
+
retentionDays
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
function goUsageConfigFrom(value) {
|
|
401
|
+
const record = isRecord(value) ? value : {};
|
|
402
|
+
const enabled = record.enabled === true;
|
|
403
|
+
const cookie = typeof record.cookie === "string" && record.cookie.length > 0 ? record.cookie : "";
|
|
404
|
+
const workspaceID = typeof record.workspaceID === "string" && record.workspaceID.length > 0 ? record.workspaceID : "";
|
|
405
|
+
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;
|
|
406
|
+
return { enabled, cookie, workspaceID, refreshMs };
|
|
355
407
|
}
|
|
356
408
|
function isPromptRightMetric(value) {
|
|
357
409
|
return value === "tps" || value === "avg" || value === "ttft" || value === "used" || value === "cache" || value === "input" || value === "output" || value === "reasoning";
|
|
@@ -692,7 +744,9 @@ export {
|
|
|
692
744
|
defaultDataDir,
|
|
693
745
|
resolveCapturePath,
|
|
694
746
|
resolveInsightsConfigPath,
|
|
747
|
+
resolveLegacyInsightsConfigPath,
|
|
695
748
|
readInsightsConfig,
|
|
749
|
+
insightsOptionsFromConfig,
|
|
696
750
|
normalizeChatMessageCapture,
|
|
697
751
|
normalizeChatParamsCapture,
|
|
698
752
|
normalizeChatHeadersCapture,
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { C as CaptureRecord } from './capture-
|
|
2
|
+
import { C as CaptureRecord } from './capture-CwesXqmX.js';
|
|
3
3
|
|
|
4
4
|
type HistoryMessage = {
|
|
5
5
|
id: string;
|
|
@@ -72,10 +72,10 @@ type CliOptions = {
|
|
|
72
72
|
port?: number | undefined;
|
|
73
73
|
output?: string | undefined;
|
|
74
74
|
configDir?: string | undefined;
|
|
75
|
-
retentionDays?: number | undefined;
|
|
76
75
|
dryRun: boolean;
|
|
77
76
|
keepData: boolean;
|
|
78
77
|
};
|
|
78
|
+
declare function unsupportedFlagWarning(arg: string): string | undefined;
|
|
79
79
|
declare function parseOptions(args: string[]): CliOptions;
|
|
80
80
|
declare function summarizeSessions(sessions: ReturnType<typeof buildRequestHistory>["sessions"]): {
|
|
81
81
|
id: string;
|
|
@@ -95,4 +95,4 @@ declare function addUniquePlugin(config: JsonObject, plugin: string): boolean;
|
|
|
95
95
|
declare function removePlugin(config: JsonObject, plugin: string): boolean;
|
|
96
96
|
declare function uninstallOpenCode(options: CliOptions): Promise<string>;
|
|
97
97
|
|
|
98
|
-
export { addUniquePlugin, configureOpenCodeDebug, defaultOpenCodeConfigDir, formatSessionSummary, parseOptions, removePlugin, resolveOpenCodeConfigPath, stripJsonCommentsAndTrailingCommas, summarizeSessions, uninstallOpenCode };
|
|
98
|
+
export { addUniquePlugin, configureOpenCodeDebug, defaultOpenCodeConfigDir, formatSessionSummary, parseOptions, removePlugin, resolveOpenCodeConfigPath, stripJsonCommentsAndTrailingCommas, summarizeSessions, uninstallOpenCode, unsupportedFlagWarning };
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
openDatabase,
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
readInsightsConfig,
|
|
5
|
+
resolveCapturePath,
|
|
6
|
+
resolveInsightsConfigPath
|
|
7
|
+
} from "./chunk-RZGCLQ2M.js";
|
|
6
8
|
|
|
7
9
|
// src/cli.ts
|
|
8
10
|
import { execFile as execFile2 } from "child_process";
|
|
@@ -1446,10 +1448,25 @@ var DEFAULT_HISTORY_LIMIT = 5e3;
|
|
|
1446
1448
|
var SERVER_PLUGIN_SPEC = "@rejacky/opencode-insights";
|
|
1447
1449
|
var TUI_PLUGIN_SPEC = SERVER_PLUGIN_SPEC;
|
|
1448
1450
|
var SUBPATH_TUI_PLUGIN_SPEC = "@rejacky/opencode-insights/tui";
|
|
1451
|
+
function unsupportedFlagWarning(arg) {
|
|
1452
|
+
if (arg === "--data-dir") {
|
|
1453
|
+
return "warning: --data-dir is no longer supported; the CLI reads the configured database path from ~/.opencode-insights/config.jsonc";
|
|
1454
|
+
}
|
|
1455
|
+
const setting = arg === "--db" ? "dbPath" : arg === "--retention-days" ? "retentionDays" : void 0;
|
|
1456
|
+
if (!setting) return void 0;
|
|
1457
|
+
return `warning: ${arg} is no longer supported; set ${setting} in ~/.opencode-insights/config.jsonc`;
|
|
1458
|
+
}
|
|
1449
1459
|
async function main(argv) {
|
|
1450
1460
|
const command = argv[2] ?? "recent";
|
|
1461
|
+
for (const arg of argv.slice(3)) {
|
|
1462
|
+
const warning = unsupportedFlagWarning(arg);
|
|
1463
|
+
if (warning) process.stderr.write(`${warning}
|
|
1464
|
+
`);
|
|
1465
|
+
}
|
|
1451
1466
|
const options = parseOptions(argv.slice(3));
|
|
1452
1467
|
const positionals = parsePositionals(argv.slice(3));
|
|
1468
|
+
const config = await readInsightsConfig();
|
|
1469
|
+
options.dbPath = config.dbPath;
|
|
1453
1470
|
if (command === "help" || command === "--help" || command === "-h") {
|
|
1454
1471
|
process.stdout.write(`${usage()}
|
|
1455
1472
|
`);
|
|
@@ -1553,14 +1570,6 @@ function parseOptions(args) {
|
|
|
1553
1570
|
options.dryRun = true;
|
|
1554
1571
|
} else if (arg === "--keep-data") {
|
|
1555
1572
|
options.keepData = true;
|
|
1556
|
-
} else if (arg === "--db") {
|
|
1557
|
-
const value = args[index + 1];
|
|
1558
|
-
if (value) options.dbPath = value;
|
|
1559
|
-
index += 1;
|
|
1560
|
-
} else if (arg === "--data-dir") {
|
|
1561
|
-
const value = args[index + 1];
|
|
1562
|
-
if (value) options.dataDir = value;
|
|
1563
|
-
index += 1;
|
|
1564
1573
|
} else if (arg === "--limit") {
|
|
1565
1574
|
options.limit = Number.parseInt(args[index + 1] ?? "20", 10);
|
|
1566
1575
|
options.limitProvided = true;
|
|
@@ -1580,16 +1589,10 @@ function parseOptions(args) {
|
|
|
1580
1589
|
const value = args[index + 1];
|
|
1581
1590
|
if (value) options.configDir = value;
|
|
1582
1591
|
index += 1;
|
|
1583
|
-
} else if (arg === "--retention-days") {
|
|
1584
|
-
options.retentionDays = Number.parseFloat(args[index + 1] ?? "");
|
|
1585
|
-
index += 1;
|
|
1586
1592
|
}
|
|
1587
1593
|
}
|
|
1588
1594
|
if (!Number.isFinite(options.limit) || options.limit < 1) options.limit = DEFAULT_RECENT_LIMIT;
|
|
1589
1595
|
if (options.port !== void 0 && (!Number.isFinite(options.port) || options.port < 1)) options.port = 8765;
|
|
1590
|
-
if (options.retentionDays !== void 0 && (!Number.isFinite(options.retentionDays) || options.retentionDays < 0)) {
|
|
1591
|
-
options.retentionDays = void 0;
|
|
1592
|
-
}
|
|
1593
1596
|
return options;
|
|
1594
1597
|
}
|
|
1595
1598
|
function parsePositionals(args) {
|
|
@@ -1732,7 +1735,7 @@ async function configureOpenCodeDebug(options) {
|
|
|
1732
1735
|
const tuiSource = await readJsonConfigSource(tuiPath);
|
|
1733
1736
|
const opencodeConfig = await readJsonConfig(opencodePath, { plugin: [] }, opencodeSource);
|
|
1734
1737
|
const tuiConfig = await readJsonConfig(tuiPath, { plugin: [] }, tuiSource);
|
|
1735
|
-
setSinglePluginSpec(opencodeConfig, SERVER_PLUGIN_SPEC,
|
|
1738
|
+
setSinglePluginSpec(opencodeConfig, SERVER_PLUGIN_SPEC, localServerEntry);
|
|
1736
1739
|
setSinglePluginSpec(tuiConfig, TUI_PLUGIN_SPEC, localTuiEntry);
|
|
1737
1740
|
removePlugin(tuiConfig, SUBPATH_TUI_PLUGIN_SPEC);
|
|
1738
1741
|
const lines = [
|
|
@@ -1740,6 +1743,7 @@ async function configureOpenCodeDebug(options) {
|
|
|
1740
1743
|
`TUI config: ${tuiPath}`,
|
|
1741
1744
|
`Local server plugin: ${localServerEntry}`,
|
|
1742
1745
|
`Local TUI plugin: ${localTuiEntry}`,
|
|
1746
|
+
`Insights config: ${resolveInsightsConfigPath({ dataDir: options.dataDir })}`,
|
|
1743
1747
|
`Server plugin: set local build output`,
|
|
1744
1748
|
`TUI plugin: set local build output`
|
|
1745
1749
|
];
|
|
@@ -1747,6 +1751,7 @@ async function configureOpenCodeDebug(options) {
|
|
|
1747
1751
|
lines.push("Dry run: no files written.");
|
|
1748
1752
|
return lines.join("\n");
|
|
1749
1753
|
}
|
|
1754
|
+
await readInsightsConfig({ dataDir: options.dataDir });
|
|
1750
1755
|
await mkdir(configDir, { recursive: true });
|
|
1751
1756
|
await writeJsonConfig(opencodePath, opencodeConfig, opencodeSource);
|
|
1752
1757
|
await writeJsonConfig(tuiPath, tuiConfig, tuiSource);
|
|
@@ -1826,11 +1831,6 @@ function stripJsonCommentsAndTrailingCommas(input) {
|
|
|
1826
1831
|
function isJsonObject(value) {
|
|
1827
1832
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
1828
1833
|
}
|
|
1829
|
-
function debugServerOptions(options) {
|
|
1830
|
-
const serverOptions = {};
|
|
1831
|
-
if (options.retentionDays !== void 0) serverOptions.retentionDays = options.retentionDays;
|
|
1832
|
-
return serverOptions;
|
|
1833
|
-
}
|
|
1834
1834
|
function addUniquePlugin(config, plugin) {
|
|
1835
1835
|
const current = Array.isArray(config.plugin) ? config.plugin : [];
|
|
1836
1836
|
if (current.includes(plugin)) {
|
|
@@ -1928,17 +1928,17 @@ async function writeJsonConfig(path, config, source) {
|
|
|
1928
1928
|
function usage() {
|
|
1929
1929
|
return [
|
|
1930
1930
|
"Usage:",
|
|
1931
|
-
" opencode-insights debug [--config-dir DIR] [--
|
|
1932
|
-
" opencode-insights uninstall [--config-dir DIR] [--
|
|
1933
|
-
" opencode-insights recent [--
|
|
1934
|
-
" opencode-insights sessions [--
|
|
1935
|
-
" opencode-insights history [--
|
|
1936
|
-
" opencode-insights show <session-id> [--
|
|
1937
|
-
" opencode-insights export <session-id> [--output PATH] [--
|
|
1938
|
-
" opencode-insights serve [--
|
|
1939
|
-
" opencode-insights open [--
|
|
1940
|
-
" opencode-insights doctor
|
|
1941
|
-
" opencode-insights vacuum
|
|
1931
|
+
" opencode-insights debug [--config-dir DIR] [--dry-run]",
|
|
1932
|
+
" opencode-insights uninstall [--config-dir DIR] [--keep-data] [--dry-run]",
|
|
1933
|
+
" opencode-insights recent [--limit N] [--json]",
|
|
1934
|
+
" opencode-insights sessions [--limit N] [--json]",
|
|
1935
|
+
" opencode-insights history [--limit N]",
|
|
1936
|
+
" opencode-insights show <session-id> [--limit N]",
|
|
1937
|
+
" opencode-insights export <session-id> [--output PATH] [--limit N]",
|
|
1938
|
+
" opencode-insights serve [--limit N] [--host HOST] [--port PORT]",
|
|
1939
|
+
" opencode-insights open [--limit N] [--host HOST] [--port PORT]",
|
|
1940
|
+
" opencode-insights doctor",
|
|
1941
|
+
" opencode-insights vacuum"
|
|
1942
1942
|
].join("\n");
|
|
1943
1943
|
}
|
|
1944
1944
|
function isDirectRun() {
|
|
@@ -1967,5 +1967,6 @@ export {
|
|
|
1967
1967
|
resolveOpenCodeConfigPath,
|
|
1968
1968
|
stripJsonCommentsAndTrailingCommas,
|
|
1969
1969
|
summarizeSessions,
|
|
1970
|
-
uninstallOpenCode
|
|
1970
|
+
uninstallOpenCode,
|
|
1971
|
+
unsupportedFlagWarning
|
|
1971
1972
|
};
|
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
|
|
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 insightsOptionsFromConfig, p as normalizeChatHeadersCapture, q as normalizeChatMessageCapture, r as normalizeChatParamsCapture, s as normalizeEventCapture, t as normalizeExperimentalChatMessagesTransformCapture, u as normalizeExperimentalChatSystemTransformCapture, v as normalizeToolCapture, w as openDatabase, x as readInsightsConfig, y as recordAssistantDelta, z as recordAssistantMessage, B as recordToolActivity, E as renderMetricsText, F as renderPromptRightMetricsText, H as renderResponseMetricsText, K as renderSessionTokenUsage, L as resolveCapturePath, N as resolveInsightsConfigPath, O as resolveLegacyInsightsConfigPath, Q as resolveRetentionDays } from './capture-CwesXqmX.js';
|
|
4
4
|
|
|
5
5
|
type SubagentStatus = "running" | "done" | "error";
|
|
6
6
|
type SubagentInfo = {
|
package/dist/index.js
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
estimateStreamTokens,
|
|
20
20
|
extractEventType,
|
|
21
21
|
getSessionTokenUsage,
|
|
22
|
+
insightsOptionsFromConfig,
|
|
22
23
|
normalizeChatHeadersCapture,
|
|
23
24
|
normalizeChatMessageCapture,
|
|
24
25
|
normalizeChatParamsCapture,
|
|
@@ -37,8 +38,9 @@ import {
|
|
|
37
38
|
renderSessionTokenUsage,
|
|
38
39
|
resolveCapturePath,
|
|
39
40
|
resolveInsightsConfigPath,
|
|
41
|
+
resolveLegacyInsightsConfigPath,
|
|
40
42
|
resolveRetentionDays
|
|
41
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-RZGCLQ2M.js";
|
|
42
44
|
|
|
43
45
|
// src/cli-shim.ts
|
|
44
46
|
import { existsSync } from "fs";
|
|
@@ -91,7 +93,8 @@ var OpenCodeInsights = async (_input, options) => {
|
|
|
91
93
|
if (options?.cliShim !== false) {
|
|
92
94
|
void ensureCliShim().catch(() => void 0);
|
|
93
95
|
}
|
|
94
|
-
const
|
|
96
|
+
const config = await readInsightsConfig(options);
|
|
97
|
+
const store = createCaptureStore(insightsOptionsFromConfig(config, options?.dataDir));
|
|
95
98
|
try {
|
|
96
99
|
await store.initialize?.();
|
|
97
100
|
} catch {
|
|
@@ -157,6 +160,7 @@ export {
|
|
|
157
160
|
getSubagentSidebarModel,
|
|
158
161
|
getSubagentSidebarRowAtLine,
|
|
159
162
|
id,
|
|
163
|
+
insightsOptionsFromConfig,
|
|
160
164
|
normalizeChatHeadersCapture,
|
|
161
165
|
normalizeChatMessageCapture,
|
|
162
166
|
normalizeChatParamsCapture,
|
|
@@ -179,6 +183,7 @@ export {
|
|
|
179
183
|
renderSubagentStatus,
|
|
180
184
|
resolveCapturePath,
|
|
181
185
|
resolveInsightsConfigPath,
|
|
186
|
+
resolveLegacyInsightsConfigPath,
|
|
182
187
|
resolveRetentionDays,
|
|
183
188
|
server,
|
|
184
189
|
rootTui as tui
|
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-RZGCLQ2M.js";
|
|
16
16
|
|
|
17
17
|
// src/tui.tsx
|
|
18
18
|
import { createTextAttributes, StyledText } from "@opentui/core";
|
|
@@ -37,6 +37,130 @@ 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
|
+
var BLOCK_PARTIALS = ["", "\u258F", "\u258E", "\u258D", "\u258C", "\u258B", "\u258A", "\u2589"];
|
|
89
|
+
function formatUsageBar(usagePercent, width = 10) {
|
|
90
|
+
const percent = Math.max(0, Math.min(100, usagePercent));
|
|
91
|
+
const steps = width * 8;
|
|
92
|
+
const filled = Math.min(steps, Math.max(0, Math.round(percent / 100 * steps)));
|
|
93
|
+
const full = Math.floor(filled / 8);
|
|
94
|
+
const remainder = filled % 8;
|
|
95
|
+
const partial = BLOCK_PARTIALS[remainder] ?? "";
|
|
96
|
+
const empty = Math.max(0, width - full - (partial.length > 0 ? 1 : 0));
|
|
97
|
+
return "\u2588".repeat(full) + partial + "\u2591".repeat(empty);
|
|
98
|
+
}
|
|
99
|
+
function formatGoUsageRow(row) {
|
|
100
|
+
const bar = formatUsageBar(row.usagePercent);
|
|
101
|
+
return `${row.label.padEnd(9)}${`${row.usagePercent}%`.padEnd(3)} ${bar} ${row.reset}`;
|
|
102
|
+
}
|
|
103
|
+
function goUsageSectionVisible(config, usesGoProvider) {
|
|
104
|
+
return usesGoProvider && config.goUsage.enabled && config.goUsage.cookie.length > 0 && config.goUsage.workspaceID.length > 0;
|
|
105
|
+
}
|
|
106
|
+
function createGoProviderTracker() {
|
|
107
|
+
const providers = /* @__PURE__ */ new Map();
|
|
108
|
+
return {
|
|
109
|
+
record(sessionID, providerID) {
|
|
110
|
+
if (providerID) providers.set(sessionID, providerID);
|
|
111
|
+
},
|
|
112
|
+
usesOpenCodeGo(sessionID) {
|
|
113
|
+
return providers.get(sessionID) === "opencode-go";
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function goUsageRows(state, now) {
|
|
118
|
+
if (!state.data) return void 0;
|
|
119
|
+
const elapsedSeconds = state.lastFetchAt === void 0 ? 0 : Math.max(0, (now - state.lastFetchAt) / 1e3);
|
|
120
|
+
return [
|
|
121
|
+
{
|
|
122
|
+
label: "Rolling",
|
|
123
|
+
usagePercent: state.data.rollingUsage.usagePercent,
|
|
124
|
+
reset: formatReset(state.data.rollingUsage.resetInSec - elapsedSeconds)
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
label: "Weekly",
|
|
128
|
+
usagePercent: state.data.weeklyUsage.usagePercent,
|
|
129
|
+
reset: formatReset(state.data.weeklyUsage.resetInSec - elapsedSeconds)
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
label: "Monthly",
|
|
133
|
+
usagePercent: state.data.monthlyUsage.usagePercent,
|
|
134
|
+
reset: formatReset(state.data.monthlyUsage.resetInSec - elapsedSeconds)
|
|
135
|
+
}
|
|
136
|
+
];
|
|
137
|
+
}
|
|
138
|
+
function createGoUsageRefresher(config, fetchImpl = fetch) {
|
|
139
|
+
const state = {};
|
|
140
|
+
let inflight;
|
|
141
|
+
async function refresh(now = Date.now()) {
|
|
142
|
+
if (inflight) {
|
|
143
|
+
await inflight;
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
if (state.lastFetchAt !== void 0 && now - state.lastFetchAt < config.refreshMs) return false;
|
|
147
|
+
inflight = (async () => {
|
|
148
|
+
try {
|
|
149
|
+
state.data = await fetchGoUsage({ cookie: config.cookie, workspaceID: config.workspaceID }, fetchImpl);
|
|
150
|
+
state.error = void 0;
|
|
151
|
+
} catch (error) {
|
|
152
|
+
state.error = error instanceof Error ? error.message : String(error);
|
|
153
|
+
} finally {
|
|
154
|
+
state.lastFetchAt = now;
|
|
155
|
+
inflight = void 0;
|
|
156
|
+
}
|
|
157
|
+
})();
|
|
158
|
+
await inflight;
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
return { state, refresh };
|
|
162
|
+
}
|
|
163
|
+
|
|
40
164
|
// src/tui.tsx
|
|
41
165
|
import { Fragment, jsx, jsxs } from "@opentui/solid/jsx-runtime";
|
|
42
166
|
function isSessionID(value) {
|
|
@@ -119,6 +243,74 @@ function TokenUsageSidebar(props) {
|
|
|
119
243
|
}
|
|
120
244
|
);
|
|
121
245
|
}
|
|
246
|
+
function GoUsageSidebar(props) {
|
|
247
|
+
let text;
|
|
248
|
+
const [collapsed, setCollapsed] = createSignal(false);
|
|
249
|
+
const titleAttributes = createTextAttributes({ bold: true });
|
|
250
|
+
let previous;
|
|
251
|
+
const toggle = (event) => {
|
|
252
|
+
if (!text || event.y !== text.y) return;
|
|
253
|
+
setCollapsed((prev) => !prev);
|
|
254
|
+
sync();
|
|
255
|
+
};
|
|
256
|
+
const sync = () => {
|
|
257
|
+
if (!text) return;
|
|
258
|
+
const visible = goUsageSectionVisible(props.config, props.tracker.usesOpenCodeGo(props.sessionID));
|
|
259
|
+
if (visible) props.refresh();
|
|
260
|
+
const rows = visible ? goUsageRows(props.state, Date.now()) : void 0;
|
|
261
|
+
const error = props.state.error;
|
|
262
|
+
const showContent = visible && (rows || error);
|
|
263
|
+
const signature = showContent ? JSON.stringify({ collapsed: collapsed(), rows, error }) : "";
|
|
264
|
+
const next = {
|
|
265
|
+
content: signature,
|
|
266
|
+
visible: signature.length > 0,
|
|
267
|
+
height: signature.length > 0 ? "auto" : 0
|
|
268
|
+
};
|
|
269
|
+
if (!hasRenderStateChanged(previous, next)) return;
|
|
270
|
+
previous = next;
|
|
271
|
+
text.visible = next.visible;
|
|
272
|
+
text.height = next.height;
|
|
273
|
+
text.content = showContent ? renderGoUsageSidebar(rows, error, props.api, titleAttributes, collapsed()) : "";
|
|
274
|
+
props.api.renderer.requestRender();
|
|
275
|
+
};
|
|
276
|
+
const unsubscribe = props.subscribe(sync);
|
|
277
|
+
const unsubscribeGoUsage = props.goUsageSubscribe(sync);
|
|
278
|
+
const timer = setInterval(sync, 1e3);
|
|
279
|
+
onCleanup(() => {
|
|
280
|
+
unsubscribe();
|
|
281
|
+
unsubscribeGoUsage();
|
|
282
|
+
clearInterval(timer);
|
|
283
|
+
});
|
|
284
|
+
return /* @__PURE__ */ jsx(
|
|
285
|
+
"text",
|
|
286
|
+
{
|
|
287
|
+
ref: (ref) => {
|
|
288
|
+
text = ref;
|
|
289
|
+
sync();
|
|
290
|
+
},
|
|
291
|
+
onMouseDown: toggle,
|
|
292
|
+
fg: props.api.theme.current.textMuted,
|
|
293
|
+
children: ""
|
|
294
|
+
}
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
function renderGoUsageSidebar(rows, error, api, titleAttributes, collapsed) {
|
|
298
|
+
const chunks = [
|
|
299
|
+
textChunk(`${collapsed ? "\u25B6" : "\u25BC"} Go Usage
|
|
300
|
+
`, api.theme.current.text, titleAttributes)
|
|
301
|
+
];
|
|
302
|
+
if (!collapsed) {
|
|
303
|
+
if (rows && rows.length > 0) {
|
|
304
|
+
for (const [index, row] of rows.entries()) {
|
|
305
|
+
if (index > 0) chunks.push(textChunk("\n"));
|
|
306
|
+
chunks.push(textChunk(formatGoUsageRow(row), api.theme.current.textMuted));
|
|
307
|
+
}
|
|
308
|
+
} else if (error) {
|
|
309
|
+
chunks.push(textChunk(`Go usage: ${error}`, api.theme.current.error));
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return new StyledText(chunks);
|
|
313
|
+
}
|
|
122
314
|
function SubagentSidebar(props) {
|
|
123
315
|
let text;
|
|
124
316
|
const [collapsed, setCollapsed] = createSignal(false);
|
|
@@ -238,7 +430,13 @@ var tui = async (api, options) => {
|
|
|
238
430
|
const subagents = createSubagentState();
|
|
239
431
|
const metricListeners = createListenerRegistry();
|
|
240
432
|
const subagentListeners = createListenerRegistry();
|
|
433
|
+
const goUsageListeners = createListenerRegistry();
|
|
241
434
|
const hydratedSessions = /* @__PURE__ */ new Set();
|
|
435
|
+
const goProviderTracker = createGoProviderTracker();
|
|
436
|
+
const goUsage = createGoUsageRefresher(config.goUsage);
|
|
437
|
+
const refreshGoUsage = async () => {
|
|
438
|
+
if (await goUsage.refresh()) goUsageListeners.notify();
|
|
439
|
+
};
|
|
242
440
|
const hydrateSessionMetrics = async (sessionID) => {
|
|
243
441
|
if (!isSessionID(sessionID) || hydratedSessions.has(sessionID)) return;
|
|
244
442
|
hydratedSessions.add(sessionID);
|
|
@@ -247,6 +445,8 @@ var tui = async (api, options) => {
|
|
|
247
445
|
const messages = response.data ?? [];
|
|
248
446
|
for (const message of messages) {
|
|
249
447
|
const info = message.info;
|
|
448
|
+
const providerID = info.providerID;
|
|
449
|
+
goProviderTracker.record(sessionID, typeof providerID === "string" ? providerID : void 0);
|
|
250
450
|
if (info.role !== "assistant" || typeof info.time.completed !== "number") continue;
|
|
251
451
|
const input = {
|
|
252
452
|
sessionID: info.sessionID,
|
|
@@ -279,6 +479,9 @@ var tui = async (api, options) => {
|
|
|
279
479
|
const offMessage = api.event.on("message.updated", (evt) => {
|
|
280
480
|
const info = evt.properties.info;
|
|
281
481
|
if (applySubagentEvent(subagents, evt)) subagentListeners.notify();
|
|
482
|
+
const sessionID = info.sessionID ?? evt.properties.sessionID;
|
|
483
|
+
const providerID = info.providerID ?? info.model?.providerID;
|
|
484
|
+
goProviderTracker.record(sessionID, typeof providerID === "string" ? providerID : void 0);
|
|
282
485
|
if (info.role !== "assistant") return;
|
|
283
486
|
const messageInput = {
|
|
284
487
|
sessionID: info.sessionID ?? evt.properties.sessionID,
|
|
@@ -308,6 +511,13 @@ var tui = async (api, options) => {
|
|
|
308
511
|
});
|
|
309
512
|
const offSessionUpdated = api.event.on("session.updated", (evt) => {
|
|
310
513
|
if (applySubagentEvent(subagents, evt)) subagentListeners.notify();
|
|
514
|
+
const info = evt.properties.info;
|
|
515
|
+
const sessionID = typeof info?.id === "string" ? info.id : void 0;
|
|
516
|
+
const providerID = info?.model?.providerID;
|
|
517
|
+
if (sessionID && typeof providerID === "string") {
|
|
518
|
+
goProviderTracker.record(sessionID, providerID);
|
|
519
|
+
metricListeners.notify();
|
|
520
|
+
}
|
|
311
521
|
});
|
|
312
522
|
const offSessionStatus = api.event.on("session.status", (evt) => {
|
|
313
523
|
if (applySubagentEvent(subagents, evt)) subagentListeners.notify();
|
|
@@ -347,6 +557,19 @@ var tui = async (api, options) => {
|
|
|
347
557
|
hydrate: () => void hydrateSessionMetrics(props.session_id)
|
|
348
558
|
}
|
|
349
559
|
),
|
|
560
|
+
/* @__PURE__ */ jsx(
|
|
561
|
+
GoUsageSidebar,
|
|
562
|
+
{
|
|
563
|
+
api,
|
|
564
|
+
state: goUsage.state,
|
|
565
|
+
config,
|
|
566
|
+
tracker: goProviderTracker,
|
|
567
|
+
sessionID: props.session_id,
|
|
568
|
+
subscribe: metricListeners.subscribe,
|
|
569
|
+
goUsageSubscribe: goUsageListeners.subscribe,
|
|
570
|
+
refresh: () => void refreshGoUsage()
|
|
571
|
+
}
|
|
572
|
+
),
|
|
350
573
|
/* @__PURE__ */ jsx(
|
|
351
574
|
SubagentSidebar,
|
|
352
575
|
{
|
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.
|
|
4
|
+
"version": "0.2.0",
|
|
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",
|