@rejacky/opencode-insights 0.1.6 → 0.1.9
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/{capture-Dy799i3Z.d.ts → capture-BMWWI5GR.d.ts} +1 -1
- package/dist/{chunk-36ZNCLI3.js → chunk-7M32TU5P.js} +9 -0
- package/dist/{chunk-XCHFXQIL.js → chunk-FGTKNB7T.js} +30 -2
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +40 -11
- package/dist/index.d.ts +3 -2
- package/dist/index.js +4 -2
- package/dist/tui.js +39 -9
- package/package.json +2 -1
|
@@ -42,7 +42,7 @@ interface SqliteDb {
|
|
|
42
42
|
sync(): void;
|
|
43
43
|
close(): void;
|
|
44
44
|
}
|
|
45
|
-
declare function openDatabase(path: string): Promise<SqliteDb | undefined>;
|
|
45
|
+
declare function openDatabase(path: string, readonly?: boolean): Promise<SqliteDb | undefined>;
|
|
46
46
|
declare function extractEventType(payload: Record<string, unknown>): string | null;
|
|
47
47
|
declare class SqliteCaptureStore implements CaptureStore {
|
|
48
48
|
private readonly path;
|
|
@@ -229,6 +229,14 @@ function getSubagentSidebarModel(state, parentID, options = {}) {
|
|
|
229
229
|
}))
|
|
230
230
|
};
|
|
231
231
|
}
|
|
232
|
+
function getSubagentSidebarRowAtLine(model, line) {
|
|
233
|
+
let rowStart = 2;
|
|
234
|
+
for (const [index, row] of model.rows.entries()) {
|
|
235
|
+
if (index > 0) rowStart += 1;
|
|
236
|
+
if (line === rowStart || line === rowStart + 1) return row;
|
|
237
|
+
rowStart += 2;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
232
240
|
function renderSubagentSidebar(state, parentID, options = {}) {
|
|
233
241
|
const model = getSubagentSidebarModel(state, parentID, options);
|
|
234
242
|
if (!model) return "";
|
|
@@ -450,6 +458,7 @@ export {
|
|
|
450
458
|
getSubagentItems,
|
|
451
459
|
pruneStaleSubagents,
|
|
452
460
|
getSubagentSidebarModel,
|
|
461
|
+
getSubagentSidebarRowAtLine,
|
|
453
462
|
renderSubagentSidebar,
|
|
454
463
|
renderSubagentFooter
|
|
455
464
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/capture.ts
|
|
2
2
|
import { mkdir, appendFile, readFile, writeFile } from "fs/promises";
|
|
3
|
-
import { existsSync } from "fs";
|
|
3
|
+
import { existsSync, readFileSync } from "fs";
|
|
4
4
|
import { dirname, join } from "path";
|
|
5
5
|
import { homedir } from "os";
|
|
6
6
|
var DEFAULT_RETENTION_DAYS = 1;
|
|
@@ -191,7 +191,7 @@ var JsonlCaptureStore = class {
|
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
};
|
|
194
|
-
async function openDatabase(path) {
|
|
194
|
+
async function openDatabase(path, readonly = false) {
|
|
195
195
|
try {
|
|
196
196
|
const mod = await import("bun:sqlite").catch(() => void 0);
|
|
197
197
|
if (mod) {
|
|
@@ -233,6 +233,34 @@ async function openDatabase(path) {
|
|
|
233
233
|
}
|
|
234
234
|
} catch {
|
|
235
235
|
}
|
|
236
|
+
if (readonly) {
|
|
237
|
+
try {
|
|
238
|
+
const initSqlJs = await import("sql.js").catch(() => void 0);
|
|
239
|
+
if (initSqlJs) {
|
|
240
|
+
const SQL = await initSqlJs.default();
|
|
241
|
+
const data = readFileSync(path);
|
|
242
|
+
const db = new SQL.Database(data);
|
|
243
|
+
return {
|
|
244
|
+
all(sql, ...params) {
|
|
245
|
+
const stmt = db.prepare(sql);
|
|
246
|
+
if (params.length > 0) stmt.bind(params);
|
|
247
|
+
const rows = [];
|
|
248
|
+
while (stmt.step()) rows.push(stmt.getAsObject());
|
|
249
|
+
stmt.free();
|
|
250
|
+
return rows;
|
|
251
|
+
},
|
|
252
|
+
run() {
|
|
253
|
+
},
|
|
254
|
+
sync() {
|
|
255
|
+
},
|
|
256
|
+
close() {
|
|
257
|
+
db.close();
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
} catch {
|
|
262
|
+
}
|
|
263
|
+
}
|
|
236
264
|
return void 0;
|
|
237
265
|
}
|
|
238
266
|
function extractEventType(payload) {
|
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
openDatabase,
|
|
4
4
|
resolveCapturePath
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-FGTKNB7T.js";
|
|
6
6
|
|
|
7
7
|
// src/cli.ts
|
|
8
8
|
import { execFile as execFile2 } from "child_process";
|
|
@@ -62,7 +62,7 @@ async function readCaptureRecord(id, options = {}) {
|
|
|
62
62
|
const dbPath = resolveCapturePath(options);
|
|
63
63
|
if (!existsSync(dbPath)) return void 0;
|
|
64
64
|
try {
|
|
65
|
-
const db = await openDatabase(dbPath);
|
|
65
|
+
const db = await openDatabase(dbPath, true);
|
|
66
66
|
if (db) {
|
|
67
67
|
try {
|
|
68
68
|
const rows = db.all("select id, kind, timestamp, session_id, message_id, provider_id, model_id, payload_json from captures where id = ?", id);
|
|
@@ -269,7 +269,7 @@ function ensureEventTypeColumn(db) {
|
|
|
269
269
|
async function readSqliteCaptures(path, limit) {
|
|
270
270
|
if (!existsSync(path)) return void 0;
|
|
271
271
|
try {
|
|
272
|
-
const db = await openDatabase(path);
|
|
272
|
+
const db = await openDatabase(path, true);
|
|
273
273
|
if (!db) return readSqliteCapturesWithCli(path, limit);
|
|
274
274
|
try {
|
|
275
275
|
ensureEventTypeColumn(db);
|
|
@@ -303,7 +303,7 @@ async function readSqliteCapturesWithCli(path, limit) {
|
|
|
303
303
|
async function readSqliteViewerCaptures(path, limit) {
|
|
304
304
|
if (!existsSync(path)) return void 0;
|
|
305
305
|
try {
|
|
306
|
-
const db = await openDatabase(path);
|
|
306
|
+
const db = await openDatabase(path, true);
|
|
307
307
|
if (!db) return readSqliteViewerCapturesWithCli(path, limit);
|
|
308
308
|
try {
|
|
309
309
|
ensureEventTypeColumn(db);
|
|
@@ -355,10 +355,9 @@ function recentCaptureSql(limit) {
|
|
|
355
355
|
order by timestamp desc`;
|
|
356
356
|
}
|
|
357
357
|
function viewerCaptureSql(limit) {
|
|
358
|
-
return `
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
select id from captures
|
|
358
|
+
return `with recent_model as (
|
|
359
|
+
select id, session_id
|
|
360
|
+
from captures
|
|
362
361
|
where kind in (
|
|
363
362
|
'chat.params',
|
|
364
363
|
'chat.message',
|
|
@@ -366,9 +365,10 @@ function viewerCaptureSql(limit) {
|
|
|
366
365
|
)
|
|
367
366
|
order by timestamp desc
|
|
368
367
|
limit ${limit}
|
|
369
|
-
)
|
|
370
|
-
|
|
371
|
-
select id
|
|
368
|
+
),
|
|
369
|
+
recent_events as (
|
|
370
|
+
select id, session_id, payload_json
|
|
371
|
+
from captures
|
|
372
372
|
where kind = 'event'
|
|
373
373
|
and event_type in (
|
|
374
374
|
'message.updated',
|
|
@@ -379,7 +379,36 @@ function viewerCaptureSql(limit) {
|
|
|
379
379
|
)
|
|
380
380
|
order by timestamp desc
|
|
381
381
|
limit ${limit}
|
|
382
|
+
),
|
|
383
|
+
recent_sessions as (
|
|
384
|
+
select session_id from recent_model where session_id is not null
|
|
385
|
+
union
|
|
386
|
+
select session_id from recent_events where session_id is not null
|
|
387
|
+
union
|
|
388
|
+
select json_extract(payload_json, '$.event.properties.sessionID') from recent_events where json_extract(payload_json, '$.event.properties.sessionID') is not null
|
|
389
|
+
union
|
|
390
|
+
select json_extract(payload_json, '$.event.properties.info.sessionID') from recent_events where json_extract(payload_json, '$.event.properties.info.sessionID') is not null
|
|
391
|
+
),
|
|
392
|
+
metadata_events as (
|
|
393
|
+
select id
|
|
394
|
+
from captures
|
|
395
|
+
where kind = 'event'
|
|
396
|
+
and event_type in ('message.updated', 'session.updated', 'session.created')
|
|
397
|
+
and coalesce(
|
|
398
|
+
session_id,
|
|
399
|
+
json_extract(payload_json, '$.event.properties.sessionID'),
|
|
400
|
+
json_extract(payload_json, '$.event.properties.info.sessionID')
|
|
401
|
+
) in (select session_id from recent_sessions)
|
|
402
|
+
and (
|
|
403
|
+
json_extract(payload_json, '$.event.properties.info.path.cwd') is not null
|
|
404
|
+
or json_extract(payload_json, '$.event.properties.info.path.root') is not null
|
|
405
|
+
)
|
|
382
406
|
)
|
|
407
|
+
select id, kind, timestamp, session_id, message_id, provider_id, model_id, payload_json
|
|
408
|
+
from captures
|
|
409
|
+
where id in (select id from recent_model)
|
|
410
|
+
or id in (select id from recent_events)
|
|
411
|
+
or id in (select id from metadata_events)
|
|
383
412
|
order by timestamp desc`;
|
|
384
413
|
}
|
|
385
414
|
function isViewerCaptureKind(kind) {
|
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 CaptureKind, C as CaptureRecord, b as CaptureStore, I as InsightsOptions, J as JsonlCaptureStore, S as SqliteCaptureStore, c as SqliteDb, d as createCaptureStore, e as defaultDataDir, f as extractEventType, n as normalizeChatHeadersCapture, g as normalizeChatMessageCapture, h as normalizeChatParamsCapture, i as normalizeEventCapture, j as normalizeExperimentalChatMessagesTransformCapture, k as normalizeExperimentalChatSystemTransformCapture, l as normalizeToolCapture, o as openDatabase, r as resolveCapturePath, m as resolveRetentionDays } from './capture-
|
|
3
|
+
export { a as CaptureKind, C as CaptureRecord, b as CaptureStore, I as InsightsOptions, J as JsonlCaptureStore, S as SqliteCaptureStore, c as SqliteDb, d as createCaptureStore, e as defaultDataDir, f as extractEventType, n as normalizeChatHeadersCapture, g as normalizeChatMessageCapture, h as normalizeChatParamsCapture, i as normalizeEventCapture, j as normalizeExperimentalChatMessagesTransformCapture, k as normalizeExperimentalChatSystemTransformCapture, l as normalizeToolCapture, o as openDatabase, r as resolveCapturePath, m as resolveRetentionDays } from './capture-BMWWI5GR.js';
|
|
4
4
|
|
|
5
5
|
type StreamSample = {
|
|
6
6
|
at: number;
|
|
@@ -94,6 +94,7 @@ declare function getSubagentSidebarModel(state: SubagentState, parentID: string,
|
|
|
94
94
|
now?: number;
|
|
95
95
|
staleMs?: number;
|
|
96
96
|
}): SubagentSidebarModel | undefined;
|
|
97
|
+
declare function getSubagentSidebarRowAtLine(model: SubagentSidebarModel, line: number): SubagentSidebarRow | undefined;
|
|
97
98
|
declare function renderSubagentSidebar(state: SubagentState, parentID: string, options?: {
|
|
98
99
|
now?: number;
|
|
99
100
|
}): string;
|
|
@@ -111,4 +112,4 @@ declare const _default: {
|
|
|
111
112
|
server: Plugin;
|
|
112
113
|
};
|
|
113
114
|
|
|
114
|
-
export { type MessageTiming, type MetricsState, OpenCodeInsights, type SessionAverage, type StreamSample, type SubagentInfo, type SubagentSidebarModel, type SubagentSidebarRow, type SubagentState, type SubagentStatus, applySubagentEvent, createMetricsState, createSubagentState, _default as default, estimateStreamTokens, getSubagentItems, getSubagentSidebarModel, id, pruneStaleSubagents, recordAssistantDelta, recordAssistantMessage, recordToolActivity, renderMetricsText, renderSubagentFooter, renderSubagentSidebar, renderSubagentStatus, server, rootTui as tui };
|
|
115
|
+
export { type MessageTiming, type MetricsState, OpenCodeInsights, type SessionAverage, type StreamSample, type SubagentInfo, type SubagentSidebarModel, type SubagentSidebarRow, type SubagentState, type SubagentStatus, applySubagentEvent, createMetricsState, createSubagentState, _default as default, estimateStreamTokens, getSubagentItems, getSubagentSidebarModel, getSubagentSidebarRowAtLine, id, pruneStaleSubagents, recordAssistantDelta, recordAssistantMessage, recordToolActivity, renderMetricsText, renderSubagentFooter, renderSubagentSidebar, renderSubagentStatus, server, rootTui as tui };
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
estimateStreamTokens,
|
|
6
6
|
getSubagentItems,
|
|
7
7
|
getSubagentSidebarModel,
|
|
8
|
+
getSubagentSidebarRowAtLine,
|
|
8
9
|
pruneStaleSubagents,
|
|
9
10
|
recordAssistantDelta,
|
|
10
11
|
recordAssistantMessage,
|
|
@@ -13,7 +14,7 @@ import {
|
|
|
13
14
|
renderSubagentFooter,
|
|
14
15
|
renderSubagentSidebar,
|
|
15
16
|
renderSubagentStatus
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-7M32TU5P.js";
|
|
17
18
|
import {
|
|
18
19
|
JsonlCaptureStore,
|
|
19
20
|
SqliteCaptureStore,
|
|
@@ -30,7 +31,7 @@ import {
|
|
|
30
31
|
openDatabase,
|
|
31
32
|
resolveCapturePath,
|
|
32
33
|
resolveRetentionDays
|
|
33
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-FGTKNB7T.js";
|
|
34
35
|
|
|
35
36
|
// src/cli-shim.ts
|
|
36
37
|
import { existsSync } from "fs";
|
|
@@ -145,6 +146,7 @@ export {
|
|
|
145
146
|
extractEventType,
|
|
146
147
|
getSubagentItems,
|
|
147
148
|
getSubagentSidebarModel,
|
|
149
|
+
getSubagentSidebarRowAtLine,
|
|
148
150
|
id,
|
|
149
151
|
normalizeChatHeadersCapture,
|
|
150
152
|
normalizeChatMessageCapture,
|
package/dist/tui.js
CHANGED
|
@@ -3,11 +3,12 @@ import {
|
|
|
3
3
|
createMetricsState,
|
|
4
4
|
createSubagentState,
|
|
5
5
|
getSubagentSidebarModel,
|
|
6
|
+
getSubagentSidebarRowAtLine,
|
|
6
7
|
recordAssistantDelta,
|
|
7
8
|
recordAssistantMessage,
|
|
8
9
|
recordToolActivity,
|
|
9
10
|
renderMetricsText
|
|
10
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-7M32TU5P.js";
|
|
11
12
|
|
|
12
13
|
// src/tui.tsx
|
|
13
14
|
import { createTextAttributes, StyledText } from "@opentui/core";
|
|
@@ -43,17 +44,41 @@ function PromptRight(props) {
|
|
|
43
44
|
function SubagentSidebar(props) {
|
|
44
45
|
let text;
|
|
45
46
|
const [collapsed, setCollapsed] = createSignal(false);
|
|
47
|
+
const [hoveredRowID, setHoveredRowID] = createSignal();
|
|
46
48
|
const titleAttributes = createTextAttributes({ bold: true });
|
|
47
|
-
const toggle = () => {
|
|
49
|
+
const toggle = (event) => {
|
|
50
|
+
if (!text || event.y !== text.y) return;
|
|
48
51
|
setCollapsed((prev) => !prev);
|
|
49
52
|
props.api.renderer.requestRender();
|
|
50
53
|
};
|
|
54
|
+
const openSubagent = (event) => {
|
|
55
|
+
if (!text || collapsed()) return;
|
|
56
|
+
const model = getSubagentSidebarModel(props.state, props.sessionID);
|
|
57
|
+
if (!model) return;
|
|
58
|
+
const row = getSubagentSidebarRowAtLine(model, event.y - text.y);
|
|
59
|
+
if (!row) return;
|
|
60
|
+
props.api.route.navigate("session", { sessionID: row.id });
|
|
61
|
+
};
|
|
62
|
+
const hoverSubagent = (event) => {
|
|
63
|
+
if (!text || collapsed()) return;
|
|
64
|
+
const model = getSubagentSidebarModel(props.state, props.sessionID);
|
|
65
|
+
const row = model && getSubagentSidebarRowAtLine(model, event.y - text.y);
|
|
66
|
+
const nextRowID = row?.id;
|
|
67
|
+
if (nextRowID === hoveredRowID()) return;
|
|
68
|
+
setHoveredRowID(nextRowID);
|
|
69
|
+
sync();
|
|
70
|
+
};
|
|
71
|
+
const clearHoveredSubagent = () => {
|
|
72
|
+
if (!hoveredRowID()) return;
|
|
73
|
+
setHoveredRowID(void 0);
|
|
74
|
+
sync();
|
|
75
|
+
};
|
|
51
76
|
const sync = () => {
|
|
52
77
|
if (!text) return;
|
|
53
78
|
const model = getSubagentSidebarModel(props.state, props.sessionID);
|
|
54
79
|
text.visible = !!model;
|
|
55
80
|
text.height = model ? "auto" : 0;
|
|
56
|
-
text.content = model ? renderSubagentStyledSidebar(props.state, props.sessionID, props.api, titleAttributes, collapsed()) : "";
|
|
81
|
+
text.content = model ? renderSubagentStyledSidebar(props.state, props.sessionID, props.api, titleAttributes, collapsed(), hoveredRowID()) : "";
|
|
57
82
|
props.api.renderer.requestRender();
|
|
58
83
|
};
|
|
59
84
|
const unsubscribe = props.subscribe(sync);
|
|
@@ -70,12 +95,15 @@ function SubagentSidebar(props) {
|
|
|
70
95
|
sync();
|
|
71
96
|
},
|
|
72
97
|
onMouseDown: toggle,
|
|
98
|
+
onMouseUp: openSubagent,
|
|
99
|
+
onMouseMove: hoverSubagent,
|
|
100
|
+
onMouseOut: clearHoveredSubagent,
|
|
73
101
|
fg: props.api.theme.current.textMuted,
|
|
74
102
|
children: ""
|
|
75
103
|
}
|
|
76
104
|
);
|
|
77
105
|
}
|
|
78
|
-
function renderSubagentStyledSidebar(state, sessionID, api, titleAttributes, collapsed) {
|
|
106
|
+
function renderSubagentStyledSidebar(state, sessionID, api, titleAttributes, collapsed, hoveredRowID) {
|
|
79
107
|
const model = getSubagentSidebarModel(state, sessionID);
|
|
80
108
|
if (!model) return "";
|
|
81
109
|
const indicator = collapsed ? "\u25B6 " : "\u25BC ";
|
|
@@ -89,20 +117,22 @@ function renderSubagentStyledSidebar(state, sessionID, api, titleAttributes, col
|
|
|
89
117
|
for (const [index, row] of model.rows.entries()) {
|
|
90
118
|
if (index > 0) chunks.push(textChunk("\n"));
|
|
91
119
|
const dotColor = row.status === "running" ? api.theme.current.success : row.status === "error" ? api.theme.current.error : api.theme.current.textMuted;
|
|
92
|
-
|
|
120
|
+
const background = row.id === hoveredRowID ? api.theme.current.backgroundElement : void 0;
|
|
121
|
+
chunks.push(textChunk("\u2022 ", dotColor, void 0, background));
|
|
93
122
|
chunks.push(textChunk(`${row.title}
|
|
94
|
-
`, api.theme.current.text));
|
|
95
|
-
chunks.push(textChunk(row.subtitle, api.theme.current.textMuted));
|
|
123
|
+
`, api.theme.current.text, void 0, background));
|
|
124
|
+
chunks.push(textChunk(row.subtitle, api.theme.current.textMuted, void 0, background));
|
|
96
125
|
}
|
|
97
126
|
}
|
|
98
127
|
return new StyledText(chunks);
|
|
99
128
|
}
|
|
100
|
-
function textChunk(text, fg, attributes) {
|
|
129
|
+
function textChunk(text, fg, attributes, bg) {
|
|
101
130
|
return {
|
|
102
131
|
__isChunk: true,
|
|
103
132
|
text,
|
|
104
133
|
...fg === void 0 ? {} : { fg },
|
|
105
|
-
...attributes === void 0 ? {} : { attributes }
|
|
134
|
+
...attributes === void 0 ? {} : { attributes },
|
|
135
|
+
...bg === void 0 ? {} : { bg }
|
|
106
136
|
};
|
|
107
137
|
}
|
|
108
138
|
var tui = async (api) => {
|
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.9",
|
|
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",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"scripts": {
|
|
48
48
|
"build": "tsup",
|
|
49
49
|
"debug": "npm run build && node dist/cli.js debug",
|
|
50
|
+
"postinstall": "npm rebuild better-sqlite3",
|
|
50
51
|
"test": "vitest run",
|
|
51
52
|
"typecheck": "tsc --noEmit",
|
|
52
53
|
"verify": "npm run typecheck && npm run test && npm run build",
|