@cesarus777/opencode-cgp 0.1.0 → 0.2.2
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 +7 -3
- package/dist/cli.js +1411 -0
- package/dist/core.js +424 -3
- package/dist/index.js +1087 -29
- package/dist/rpc.js +159 -0
- package/dist/tui.js +653 -82
- package/dist/types/adapter.d.ts +2 -0
- package/dist/types/chartreport.d.ts +14 -0
- package/dist/types/charts.d.ts +54 -0
- package/dist/types/core.d.ts +2 -0
- package/dist/types/family.d.ts +1 -1
- package/dist/types/history.d.ts +59 -0
- package/dist/types/historycharts.d.ts +17 -0
- package/dist/types/historyserver.d.ts +7 -0
- package/dist/types/performance.d.ts +57 -0
- package/dist/types/rpc.d.ts +78 -0
- package/dist/types/snapshot.d.ts +4 -1
- package/dist/types/snapshotserver.d.ts +3 -3
- package/dist/types/tui/chartformat.d.ts +46 -0
- package/dist/types/tui/charts.d.ts +11 -0
- package/dist/types/tui/compactformat.d.ts +3 -2
- package/dist/types/tui/contextchart.d.ts +19 -0
- package/dist/types/tui/format.d.ts +1 -0
- package/dist/types/tui/state.d.ts +1 -0
- package/dist/types/types.d.ts +11 -0
- package/package.json +9 -3
package/dist/tui.js
CHANGED
|
@@ -114,6 +114,154 @@ var monitorRpc = Rpc.define({
|
|
|
114
114
|
methods: { read: { input: readSchema, output: resultSchema } },
|
|
115
115
|
events: { changed: { schema: batchSchema } }
|
|
116
116
|
});
|
|
117
|
+
var nonnegative = { type: "number", minimum: 0 };
|
|
118
|
+
var performanceSchema = object({
|
|
119
|
+
observedSince: count,
|
|
120
|
+
at: count,
|
|
121
|
+
sessionCreatedAt: count,
|
|
122
|
+
sessionOutputTokens: count,
|
|
123
|
+
status: enumeration("waiting", "streaming", "idle", "unavailable"),
|
|
124
|
+
partial: { type: "boolean" },
|
|
125
|
+
liveTps: nonnegative,
|
|
126
|
+
averageModelTps: nonnegative,
|
|
127
|
+
averageSessionTps: nonnegative,
|
|
128
|
+
ttftMs: nonnegative,
|
|
129
|
+
latestRequestTps: nonnegative,
|
|
130
|
+
requestId: text,
|
|
131
|
+
requestAgent: text,
|
|
132
|
+
requestModel: text,
|
|
133
|
+
requestOutputTokens: count,
|
|
134
|
+
requestStartedAt: count,
|
|
135
|
+
firstOutputAt: count,
|
|
136
|
+
streamEndedAt: count,
|
|
137
|
+
estimatedOutputTokens: nonnegative,
|
|
138
|
+
modelOutputTokens: nonnegative,
|
|
139
|
+
modelDurationMs: nonnegative,
|
|
140
|
+
completedRequests: count,
|
|
141
|
+
tokenSource: enumeration("estimated", "reported", "unavailable")
|
|
142
|
+
}, [
|
|
143
|
+
"sessionCreatedAt",
|
|
144
|
+
"observedSince",
|
|
145
|
+
"at",
|
|
146
|
+
"status",
|
|
147
|
+
"partial",
|
|
148
|
+
"estimatedOutputTokens",
|
|
149
|
+
"modelOutputTokens",
|
|
150
|
+
"modelDurationMs",
|
|
151
|
+
"completedRequests",
|
|
152
|
+
"tokenSource"
|
|
153
|
+
]);
|
|
154
|
+
var performanceRow = {
|
|
155
|
+
...row,
|
|
156
|
+
properties: { ...row.properties, performance: performanceSchema }
|
|
157
|
+
};
|
|
158
|
+
var performanceBatchSchema = object({
|
|
159
|
+
...batchFields,
|
|
160
|
+
upserts: array(performanceRow)
|
|
161
|
+
});
|
|
162
|
+
var performanceResultSchema = object({
|
|
163
|
+
...batchFields,
|
|
164
|
+
upserts: array(performanceRow),
|
|
165
|
+
unavailable: resultSchema.properties.unavailable ?? {}
|
|
166
|
+
});
|
|
167
|
+
var performanceRpc = Rpc.define({
|
|
168
|
+
id: "context-guard.monitor.v2",
|
|
169
|
+
methods: { read: { input: readSchema, output: performanceResultSchema } },
|
|
170
|
+
events: { changed: { schema: performanceBatchSchema } }
|
|
171
|
+
});
|
|
172
|
+
function isPerformanceBatch(value) {
|
|
173
|
+
return valid(value, performanceBatchSchema) && uniqueRows(value);
|
|
174
|
+
}
|
|
175
|
+
function isPerformanceResult(value) {
|
|
176
|
+
return valid(value, performanceResultSchema) && uniqueRows(value);
|
|
177
|
+
}
|
|
178
|
+
function isPerformanceSnapshot(value) {
|
|
179
|
+
return valid(value, performanceSchema);
|
|
180
|
+
}
|
|
181
|
+
var chartPointSchema = object({
|
|
182
|
+
time: count,
|
|
183
|
+
agent: text,
|
|
184
|
+
model: text,
|
|
185
|
+
breakBefore: { type: "boolean" },
|
|
186
|
+
partial: { type: "boolean" },
|
|
187
|
+
liveTps: nonnegative,
|
|
188
|
+
averageModelTps: nonnegative,
|
|
189
|
+
averageSessionTps: nonnegative,
|
|
190
|
+
sessionOutputTokens: count
|
|
191
|
+
}, ["time", "breakBefore", "partial"]);
|
|
192
|
+
var chartRequestSchema = object({
|
|
193
|
+
time: count,
|
|
194
|
+
requestId: text,
|
|
195
|
+
started: count,
|
|
196
|
+
agent: text,
|
|
197
|
+
model: text,
|
|
198
|
+
ttftMs: nonnegative,
|
|
199
|
+
outputTokens: count,
|
|
200
|
+
durationMs: nonnegative,
|
|
201
|
+
tps: nonnegative,
|
|
202
|
+
partial: { type: "boolean" }
|
|
203
|
+
}, ["time", "requestId", "started", "partial"]);
|
|
204
|
+
var historyReadSchema = object({
|
|
205
|
+
schemaVersion: { const: 1 },
|
|
206
|
+
sessionId: text,
|
|
207
|
+
from: count,
|
|
208
|
+
to: count,
|
|
209
|
+
maxPoints: { type: "integer", minimum: 16, maximum: 500 }
|
|
210
|
+
});
|
|
211
|
+
var historyResultSchema = object({
|
|
212
|
+
schemaVersion: { const: 1 },
|
|
213
|
+
generatedAt: count,
|
|
214
|
+
enabled: { type: "boolean" },
|
|
215
|
+
truncated: { type: "boolean" },
|
|
216
|
+
metadata: object({
|
|
217
|
+
schema: { const: 1 },
|
|
218
|
+
retainedFrom: count,
|
|
219
|
+
retainedBytes: count,
|
|
220
|
+
skippedBytes: count,
|
|
221
|
+
malformedRecords: count,
|
|
222
|
+
completeness: enumeration("bounded-retention")
|
|
223
|
+
}, [
|
|
224
|
+
"schema",
|
|
225
|
+
"retainedBytes",
|
|
226
|
+
"skippedBytes",
|
|
227
|
+
"malformedRecords",
|
|
228
|
+
"completeness"
|
|
229
|
+
]),
|
|
230
|
+
data: object({
|
|
231
|
+
schema: { const: 1 },
|
|
232
|
+
omittedSessions: count,
|
|
233
|
+
omittedGaps: count,
|
|
234
|
+
maxPoints: count,
|
|
235
|
+
gaps: array(object({ time: count, sessionId: text, reason: text }), 128),
|
|
236
|
+
series: array(object({
|
|
237
|
+
sessionId: text,
|
|
238
|
+
parentId: text,
|
|
239
|
+
agents: array(text, 1024),
|
|
240
|
+
models: array(text, 1024),
|
|
241
|
+
points: array(chartPointSchema, 500),
|
|
242
|
+
requests: array(chartRequestSchema, 500),
|
|
243
|
+
originalPoints: count
|
|
244
|
+
}, [
|
|
245
|
+
"sessionId",
|
|
246
|
+
"agents",
|
|
247
|
+
"models",
|
|
248
|
+
"points",
|
|
249
|
+
"requests",
|
|
250
|
+
"originalPoints"
|
|
251
|
+
]), 1)
|
|
252
|
+
}, ["schema", "omittedSessions", "maxPoints", "gaps", "series"])
|
|
253
|
+
});
|
|
254
|
+
var historyRpc = Rpc.define({
|
|
255
|
+
id: "context-guard.history.v1",
|
|
256
|
+
methods: { read: { input: historyReadSchema, output: historyResultSchema } },
|
|
257
|
+
events: {}
|
|
258
|
+
});
|
|
259
|
+
function isHistoryRead(value) {
|
|
260
|
+
return valid(value, historyReadSchema) && record(value) && typeof value.from === "number" && typeof value.to === "number" && value.from <= value.to && value.to - value.from <= 7 * 86400000;
|
|
261
|
+
}
|
|
262
|
+
function isHistoryResult(value) {
|
|
263
|
+
return valid(value, historyResultSchema);
|
|
264
|
+
}
|
|
117
265
|
function record(x) {
|
|
118
266
|
return typeof x === "object" && x !== null && !Array.isArray(x);
|
|
119
267
|
}
|
|
@@ -164,14 +312,14 @@ function uniqueRows(value) {
|
|
|
164
312
|
}
|
|
165
313
|
|
|
166
314
|
// src/tui.tsx
|
|
167
|
-
import { effect as _$
|
|
168
|
-
import { createTextNode as _$
|
|
169
|
-
import { insertNode as _$
|
|
170
|
-
import { setProp as _$
|
|
171
|
-
import { createElement as _$
|
|
172
|
-
import { createComponent as _$
|
|
315
|
+
import { effect as _$effect5 } from "@opentui/solid";
|
|
316
|
+
import { createTextNode as _$createTextNode3 } from "@opentui/solid";
|
|
317
|
+
import { insertNode as _$insertNode5 } from "@opentui/solid";
|
|
318
|
+
import { setProp as _$setProp5 } from "@opentui/solid";
|
|
319
|
+
import { createElement as _$createElement5 } from "@opentui/solid";
|
|
320
|
+
import { createComponent as _$createComponent5 } from "@opentui/solid";
|
|
173
321
|
import { Plugin } from "@opencode/plugin/tui";
|
|
174
|
-
import { Show as
|
|
322
|
+
import { Show as Show4 } from "solid-js";
|
|
175
323
|
|
|
176
324
|
// src/tui/state.ts
|
|
177
325
|
import { createSignal } from "solid-js";
|
|
@@ -413,7 +561,7 @@ class SnapshotClient {
|
|
|
413
561
|
const value = await abortable(this.transport.read(location, ids, signal), signal);
|
|
414
562
|
if (generation !== this.generation)
|
|
415
563
|
throw new Error("Obsolete read");
|
|
416
|
-
if (!
|
|
564
|
+
if (!isPerformanceResult(value)) {
|
|
417
565
|
this.connection = "incompatible";
|
|
418
566
|
throw new Error("Incompatible snapshot");
|
|
419
567
|
}
|
|
@@ -486,7 +634,7 @@ class SnapshotClient {
|
|
|
486
634
|
for await (const value of this.transport.subscribe(this.location, signal)) {
|
|
487
635
|
if (signal.aborted || generation !== this.generation)
|
|
488
636
|
return;
|
|
489
|
-
if (!
|
|
637
|
+
if (!isPerformanceBatch(value)) {
|
|
490
638
|
this.connection = "incompatible";
|
|
491
639
|
this.changed();
|
|
492
640
|
continue;
|
|
@@ -661,6 +809,7 @@ function createGuardState(context) {
|
|
|
661
809
|
scheduleExpiry();
|
|
662
810
|
};
|
|
663
811
|
const rpc = context.client.rpc(monitorRpc);
|
|
812
|
+
const liveRpc = context.client.rpc(performanceRpc);
|
|
664
813
|
const clients = new Map;
|
|
665
814
|
let inventory, currentId = "", controller;
|
|
666
815
|
let scanGeneration = 0, scanning = false, maxNodes = 1024;
|
|
@@ -744,10 +893,20 @@ function createGuardState(context) {
|
|
|
744
893
|
let c = clients.get(key);
|
|
745
894
|
if (!c) {
|
|
746
895
|
const location = sample.location;
|
|
896
|
+
let selectedRpc = rpc;
|
|
897
|
+
try {
|
|
898
|
+
const probe = await liveRpc.read({ schemaVersion: 1, sessionIds: [] }, { location, signal });
|
|
899
|
+
if (isPerformanceResult(probe))
|
|
900
|
+
selectedRpc = liveRpc;
|
|
901
|
+
} catch {
|
|
902
|
+
}
|
|
903
|
+
if (signal.aborted || generation !== scanGeneration)
|
|
904
|
+
return;
|
|
905
|
+
const source = selectedRpc;
|
|
747
906
|
c = new SnapshotClient({
|
|
748
|
-
read: (_key, ids, requestSignal) =>
|
|
907
|
+
read: (_key, ids, requestSignal) => source.read({ schemaVersion: 1, sessionIds: ids }, { location, signal: requestSignal }),
|
|
749
908
|
subscribe: async function* (_key, requestSignal) {
|
|
750
|
-
for await (const event of
|
|
909
|
+
for await (const event of source.events.subscribe("changed", {
|
|
751
910
|
signal: requestSignal
|
|
752
911
|
})) {
|
|
753
912
|
if (event.location && sameLocation(event.location, location))
|
|
@@ -880,6 +1039,15 @@ function createGuardState(context) {
|
|
|
880
1039
|
return {
|
|
881
1040
|
version,
|
|
882
1041
|
acquire,
|
|
1042
|
+
async history(id, from, to, signal) {
|
|
1043
|
+
const info = inventory?.sessions.get(id);
|
|
1044
|
+
if (!info)
|
|
1045
|
+
throw new Error("Session location unavailable");
|
|
1046
|
+
const result = await context.client.rpc(historyRpc).read({ schemaVersion: 1, sessionId: id, from, to, maxPoints: 240 }, { location: info.location, signal });
|
|
1047
|
+
if (!isHistoryResult(result))
|
|
1048
|
+
throw new Error("Incompatible history service");
|
|
1049
|
+
return result;
|
|
1050
|
+
},
|
|
883
1051
|
rows,
|
|
884
1052
|
dispose: stop,
|
|
885
1053
|
age(id) {
|
|
@@ -986,6 +1154,7 @@ function rowLines(row2, width) {
|
|
|
986
1154
|
const label = clip(`${prefix}${row2.node.agent ?? row2.node.sessionId}`, Math.max(1, width - cells(suffix)));
|
|
987
1155
|
return [
|
|
988
1156
|
clip(`${label}${suffix}`, width),
|
|
1157
|
+
...performanceLines(row2, width),
|
|
989
1158
|
...wrap(`${quantity(m?.tokens)}/${quantity(s?.capacityTokens)} input; to ${quantity(row2.triggerDeltaTokens)}`, width),
|
|
990
1159
|
...s && s.compaction.phase !== "idle" ? wrap(`compact: ${s.compaction.phase}`, width) : [],
|
|
991
1160
|
...row2.node.interrupted ? [clip("interrupted", width)] : [],
|
|
@@ -1005,6 +1174,7 @@ function detailLines(row2, width, expanded = false, ageMs = 0) {
|
|
|
1005
1174
|
const headroom = row2.triggerDeltaTokens;
|
|
1006
1175
|
const lines = [
|
|
1007
1176
|
`${s.agent ?? "?"} / ${s.model ?? "?"}`,
|
|
1177
|
+
...performanceLines(row2, width),
|
|
1008
1178
|
`${m?.confidence === "approximate" ? "~" : ""}${quantity(m?.tokens)} / ${quantity(s.capacityTokens)} input`,
|
|
1009
1179
|
`${row2.utilizationPercent?.toFixed(1) ?? "?"}% ${row2.pressure === "crossed" ? "!! crossed" : row2.pressure === "near" ? "! near" : row2.freshness.toUpperCase()}`,
|
|
1010
1180
|
`Trigger ${threshold.type === "ratio" ? `${threshold.value * 100}%` : `${threshold.value} ${threshold.type}`} (${s.policy.comparison})`,
|
|
@@ -1042,6 +1212,72 @@ function wrap(value, width) {
|
|
|
1042
1212
|
result.push(part);
|
|
1043
1213
|
return result;
|
|
1044
1214
|
}
|
|
1215
|
+
function performanceLines(row2, width) {
|
|
1216
|
+
const p = row2.snapshot?.performance;
|
|
1217
|
+
if (!p)
|
|
1218
|
+
return [];
|
|
1219
|
+
const value = (n) => n === undefined ? "?" : n.toFixed(1);
|
|
1220
|
+
return [
|
|
1221
|
+
...wrap(`TPS ${p.status === "waiting" ? "waiting" : `${p.status === "streaming" ? "~" : ""}${value(p.liveTps)}`} \xB7 ${p.status}${p.partial ? " \xB7 partial" : ""}`, width),
|
|
1222
|
+
...wrap(`Avg model ${value(p.averageModelTps)} \xB7 Avg session ${value(p.averageSessionTps)}`, width),
|
|
1223
|
+
...wrap(`TTFT ${p.ttftMs === undefined ? "?" : `~${(p.ttftMs / 1000).toFixed(2)}s`}${p.latestRequestTps === undefined ? "" : ` \xB7 Last ${value(p.latestRequestTps)} t/s`}`, width)
|
|
1224
|
+
];
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
// src/tui/contextchart.ts
|
|
1228
|
+
function updateContextTrace(previous, row2) {
|
|
1229
|
+
const snapshot = row2?.snapshot;
|
|
1230
|
+
const model = snapshot?.model ?? row2?.node.model;
|
|
1231
|
+
const capacity = snapshot?.capacityTokens;
|
|
1232
|
+
const sessionId = row2?.node.sessionId ?? "";
|
|
1233
|
+
const samples = previous?.sessionId === sessionId && previous.model === model && previous.capacity === capacity ? previous.samples : [];
|
|
1234
|
+
const measurement2 = snapshot?.measurement;
|
|
1235
|
+
const valid2 = (row2?.freshness === "fresh" || row2?.freshness === "stale") && measurement2 && Number.isFinite(measurement2.tokens) && measurement2.tokens >= 0;
|
|
1236
|
+
const at = measurement2?.at ?? snapshot?.observedAt;
|
|
1237
|
+
let next = samples;
|
|
1238
|
+
if (at !== undefined && Number.isFinite(at)) {
|
|
1239
|
+
const sample = {
|
|
1240
|
+
at,
|
|
1241
|
+
...valid2 ? { tokens: measurement2.tokens } : {}
|
|
1242
|
+
};
|
|
1243
|
+
const last = samples.at(-1);
|
|
1244
|
+
if (!last || at > last.at)
|
|
1245
|
+
next = [...samples, sample].slice(-96);
|
|
1246
|
+
else if (at === last.at && sample.tokens !== last.tokens)
|
|
1247
|
+
next = [...samples.slice(0, -1), sample];
|
|
1248
|
+
}
|
|
1249
|
+
return { sessionId, model, capacity, samples: next };
|
|
1250
|
+
}
|
|
1251
|
+
function contextChart(samples, capacity, trigger, width) {
|
|
1252
|
+
if (capacity === undefined || !Number.isFinite(capacity) || capacity <= 0 || width < 4)
|
|
1253
|
+
return [];
|
|
1254
|
+
const height = 8;
|
|
1255
|
+
const gutter = width >= 16 ? 4 : 0;
|
|
1256
|
+
const size = Math.max(1, Math.min(96, width - gutter - 2));
|
|
1257
|
+
const values = samples.slice(-size);
|
|
1258
|
+
const offset = size - values.length;
|
|
1259
|
+
const thresholdRow = trigger !== undefined && Number.isFinite(trigger) && trigger >= 0 && trigger <= capacity ? Math.round((1 - trigger / capacity) * (height - 1)) : -1;
|
|
1260
|
+
const blocks = " \u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
|
|
1261
|
+
const lines = Array.from({ length: height }, (_, row2) => {
|
|
1262
|
+
const threshold = row2 === thresholdRow;
|
|
1263
|
+
const axis = gutter ? (row2 === 0 ? "100%" : row2 === height - 1 ? "0%" : "").padStart(gutter) : "";
|
|
1264
|
+
const plot = Array.from({ length: size }, (_2, col) => {
|
|
1265
|
+
const value = values[col - offset]?.tokens;
|
|
1266
|
+
const fill = value === undefined ? 0 : Math.max(0, Math.min(8, Math.ceil((Math.min(value / capacity, 1) * height - (height - row2 - 1)) * 8)));
|
|
1267
|
+
if (threshold)
|
|
1268
|
+
return fill > 0 ? "\u2542" : "\u2504";
|
|
1269
|
+
if (value === 0 && row2 === height - 1)
|
|
1270
|
+
return "\u2581";
|
|
1271
|
+
return blocks[fill] ?? " ";
|
|
1272
|
+
}).join("");
|
|
1273
|
+
return { line: `${axis}\u2502${plot}\u2502`, threshold };
|
|
1274
|
+
});
|
|
1275
|
+
lines.push({
|
|
1276
|
+
line: `${" ".repeat(gutter)}\u2514${"\u2500".repeat(size)}\u2518`,
|
|
1277
|
+
threshold: false
|
|
1278
|
+
});
|
|
1279
|
+
return lines;
|
|
1280
|
+
}
|
|
1045
1281
|
|
|
1046
1282
|
// src/tui/compact.tsx
|
|
1047
1283
|
import { createTextNode as _$createTextNode } from "@opentui/solid";
|
|
@@ -1077,7 +1313,7 @@ function progress(ratio, width) {
|
|
|
1077
1313
|
const filled = valid2 ? Math.round(Math.min(1, ratio) * size) : 0;
|
|
1078
1314
|
return clip("\u2588".repeat(filled) + "\u2591".repeat(size - filled) + " " + label, width);
|
|
1079
1315
|
}
|
|
1080
|
-
function mainSections(row2, width, age) {
|
|
1316
|
+
function mainSections(row2, width, age, samples) {
|
|
1081
1317
|
if (!row2)
|
|
1082
1318
|
return [{ tone: "base", lines: wrap("Loading session family...", width) }];
|
|
1083
1319
|
const s = row2.snapshot, m = s?.measurement;
|
|
@@ -1086,6 +1322,7 @@ function mainSections(row2, width, age) {
|
|
|
1086
1322
|
const ratio = (total) => measured !== undefined && total !== undefined && total > 0 ? measured / total : undefined;
|
|
1087
1323
|
const remaining = measured !== undefined && s?.policy.triggerTokens !== undefined ? s.policy.triggerTokens - measured : undefined;
|
|
1088
1324
|
return [
|
|
1325
|
+
...row2.snapshot?.performance ? [{ tone: "base", lines: performanceLines(row2, width) }] : [],
|
|
1089
1326
|
{
|
|
1090
1327
|
tone: "execution",
|
|
1091
1328
|
lines: columns(`${row2.node.agent ?? s?.agent ?? "Main"} \xB7 ${row2.node.model ?? s?.model ?? "?"}`, row2.node.status.toUpperCase(), width)
|
|
@@ -1097,7 +1334,14 @@ function mainSections(row2, width, age) {
|
|
|
1097
1334
|
...columns("Context", `${tokens(measured)} / ${tokens(s?.capacityTokens)}`, width)
|
|
1098
1335
|
]
|
|
1099
1336
|
},
|
|
1100
|
-
{
|
|
1337
|
+
...contextChart(samples ?? (m && valid2 ? [{ at: m.at, tokens: m.tokens }] : []), s?.capacityTokens, s?.policy.triggerTokens, width).map(({ line, threshold }) => ({
|
|
1338
|
+
tone: threshold ? "threshold" : "pressure",
|
|
1339
|
+
lines: [line]
|
|
1340
|
+
})),
|
|
1341
|
+
{
|
|
1342
|
+
tone: "base",
|
|
1343
|
+
lines: wrap(`${ratio(s?.capacityTokens) === undefined ? "?" : Math.round((ratio(s?.capacityTokens) ?? 0) * 100)}% used \xB7 recent samples \u2192 now`, width)
|
|
1344
|
+
},
|
|
1101
1345
|
{
|
|
1102
1346
|
tone: "base",
|
|
1103
1347
|
lines: [
|
|
@@ -1108,7 +1352,8 @@ function mainSections(row2, width, age) {
|
|
|
1108
1352
|
{
|
|
1109
1353
|
tone: "pressure",
|
|
1110
1354
|
lines: [
|
|
1111
|
-
|
|
1355
|
+
...wrap(`${ratio(s?.policy.triggerTokens) === undefined ? "?" : Math.round((ratio(s?.policy.triggerTokens) ?? 0) * 100)}% of threshold \xB7 \u2504 trigger`, width),
|
|
1356
|
+
...s?.capacityTokens !== undefined && s.policy.triggerTokens !== undefined && s.policy.triggerTokens > s.capacityTokens ? wrap("Threshold above chart capacity", width) : [],
|
|
1112
1357
|
...wrap(remaining === undefined ? "Compaction distance unknown" : remaining < 0 ? `${tokens(-remaining)} tokens over threshold` : `${tokens(remaining)} tokens until compaction`, width)
|
|
1113
1358
|
]
|
|
1114
1359
|
},
|
|
@@ -1146,6 +1391,7 @@ function agentLines(row2, width) {
|
|
|
1146
1391
|
const capacity = row2.snapshot?.capacityTokens;
|
|
1147
1392
|
const pressure = row2.pressure === "crossed" ? "CRITICAL" : row2.pressure === "near" ? "WARNING" : "";
|
|
1148
1393
|
return [
|
|
1394
|
+
...performanceLines(row2, width),
|
|
1149
1395
|
progress(measured !== undefined && capacity !== undefined && capacity > 0 ? measured / capacity : undefined, width),
|
|
1150
1396
|
...wrap(`${tokens(measured)} / ${tokens(capacity)}${pressure ? ` \xB7 ${pressure}` : ""}`, width),
|
|
1151
1397
|
...wrap(`${row2.node.status.toUpperCase()}${row2.freshness !== "fresh" ? ` \xB7 ${row2.freshness.toUpperCase()}` : ""}${row2.node.interrupted ? " \xB7 interrupted" : ""}`, width),
|
|
@@ -1225,6 +1471,7 @@ function CompactSidebar(props) {
|
|
|
1225
1471
|
setCollapsed(new Set);
|
|
1226
1472
|
});
|
|
1227
1473
|
const current = createMemo(() => props.rows.find((r) => r.node.sessionId === props.sessionID));
|
|
1474
|
+
const trace = createMemo((previous) => updateContextTrace(previous, current()));
|
|
1228
1475
|
const tree = createMemo(() => subagentTree(props.rows, props.sessionID, collapsed(), hideCompleted(), byPressure()));
|
|
1229
1476
|
const entries = createMemo(() => new Map(tree().visible.map((entry) => [entry.row.node.sessionId, entry])));
|
|
1230
1477
|
const toggle = (id) => setCollapsed((previous) => {
|
|
@@ -1314,7 +1561,7 @@ function CompactSidebar(props) {
|
|
|
1314
1561
|
});
|
|
1315
1562
|
_$insert(_el$2, _$createComponent(Index, {
|
|
1316
1563
|
get each() {
|
|
1317
|
-
return mainSections(current(), width(), props.state.age(props.sessionID));
|
|
1564
|
+
return mainSections(current(), width(), props.state.age(props.sessionID), trace().samples);
|
|
1318
1565
|
},
|
|
1319
1566
|
children: (section2) => _$createComponent(Index, {
|
|
1320
1567
|
get each() {
|
|
@@ -1323,7 +1570,7 @@ function CompactSidebar(props) {
|
|
|
1323
1570
|
children: (line) => (() => {
|
|
1324
1571
|
var _el$13 = _$createElement("text");
|
|
1325
1572
|
_$insert(_el$13, () => line() || " ");
|
|
1326
|
-
_$effect((_$p) => _$setProp(_el$13, "fg", section2().tone === "execution" ? executionColor(current()) : section2().tone === "pressure" ? pressureColor(current()) : section2().tone === "measurement" ? current()?.freshness === "fresh" ? colors().muted : colors().feedback.warning.base : colors().base, _$p));
|
|
1573
|
+
_$effect((_$p) => _$setProp(_el$13, "fg", section2().tone === "execution" ? executionColor(current()) : section2().tone === "threshold" ? colors().feedback.warning.base : section2().tone === "pressure" ? pressureColor(current()) : section2().tone === "measurement" ? current()?.freshness === "fresh" ? colors().muted : colors().feedback.warning.base : colors().base, _$p));
|
|
1327
1574
|
return _el$13;
|
|
1328
1575
|
})()
|
|
1329
1576
|
})
|
|
@@ -1469,6 +1716,8 @@ function GuardView(props) {
|
|
|
1469
1716
|
onCleanup(() => consumer.release());
|
|
1470
1717
|
const rows = createMemo2(() => props.family ?? props.state.rows([], !props.expanded));
|
|
1471
1718
|
const current = createMemo2(() => rows().find((row2) => row2.node.sessionId === props.sessionID));
|
|
1719
|
+
const trace = createMemo2((previous) => updateContextTrace(previous, props.expanded ? current() : undefined));
|
|
1720
|
+
const chart = createMemo2(() => contextChart(trace().samples, current()?.snapshot?.capacityTokens, current()?.snapshot?.policy.triggerTokens, props.width));
|
|
1472
1721
|
const detail = createMemo2(() => {
|
|
1473
1722
|
const lines = detailLines(current(), props.width, props.expanded, props.state.age(props.sessionID));
|
|
1474
1723
|
if (props.expanded) {
|
|
@@ -1536,26 +1785,48 @@ function GuardView(props) {
|
|
|
1536
1785
|
_$insert2(_el$5, () => clip(`${props.state.connection()} / ${props.state.health()?.enforcement ?? "?"}`, props.width));
|
|
1537
1786
|
_$insert2(_el$, _$createComponent2(For2, {
|
|
1538
1787
|
get each() {
|
|
1539
|
-
return
|
|
1788
|
+
return chart();
|
|
1540
1789
|
},
|
|
1541
|
-
children: (
|
|
1790
|
+
children: (item) => (() => {
|
|
1542
1791
|
var _el$1 = _$createElement2("text");
|
|
1543
|
-
_$insert2(_el$1, line);
|
|
1544
|
-
_$effect2((_$p) => _$setProp2(_el$1, "fg", props.context.theme.text.base, _$p));
|
|
1792
|
+
_$insert2(_el$1, () => item.line);
|
|
1793
|
+
_$effect2((_$p) => _$setProp2(_el$1, "fg", item.threshold ? props.context.theme.text.feedback.warning.base : props.context.theme.text.muted, _$p));
|
|
1545
1794
|
return _el$1;
|
|
1546
1795
|
})()
|
|
1547
1796
|
}), _el$6);
|
|
1548
1797
|
_$insert2(_el$, _$createComponent2(For2, {
|
|
1549
1798
|
get each() {
|
|
1550
|
-
return props.
|
|
1799
|
+
return wrap("Recent context samples \u2192 now \xB7 \u2504 trigger", props.width);
|
|
1551
1800
|
},
|
|
1552
|
-
children: (
|
|
1801
|
+
children: (line) => (() => {
|
|
1553
1802
|
var _el$10 = _$createElement2("text");
|
|
1554
|
-
_$insert2(_el$10,
|
|
1555
|
-
_$effect2((_$p) => _$setProp2(_el$10, "fg", props.context.theme.text.
|
|
1803
|
+
_$insert2(_el$10, line);
|
|
1804
|
+
_$effect2((_$p) => _$setProp2(_el$10, "fg", props.context.theme.text.muted, _$p));
|
|
1556
1805
|
return _el$10;
|
|
1557
1806
|
})()
|
|
1558
1807
|
}), _el$6);
|
|
1808
|
+
_$insert2(_el$, _$createComponent2(For2, {
|
|
1809
|
+
get each() {
|
|
1810
|
+
return detail();
|
|
1811
|
+
},
|
|
1812
|
+
children: (line) => (() => {
|
|
1813
|
+
var _el$11 = _$createElement2("text");
|
|
1814
|
+
_$insert2(_el$11, line);
|
|
1815
|
+
_$effect2((_$p) => _$setProp2(_el$11, "fg", props.context.theme.text.base, _$p));
|
|
1816
|
+
return _el$11;
|
|
1817
|
+
})()
|
|
1818
|
+
}), _el$6);
|
|
1819
|
+
_$insert2(_el$, _$createComponent2(For2, {
|
|
1820
|
+
get each() {
|
|
1821
|
+
return props.state.health()?.reasons ?? [];
|
|
1822
|
+
},
|
|
1823
|
+
children: (reason) => (() => {
|
|
1824
|
+
var _el$12 = _$createElement2("text");
|
|
1825
|
+
_$insert2(_el$12, () => clip(reason, props.width));
|
|
1826
|
+
_$effect2((_$p) => _$setProp2(_el$12, "fg", props.context.theme.text.feedback.warning.base, _$p));
|
|
1827
|
+
return _el$12;
|
|
1828
|
+
})()
|
|
1829
|
+
}), _el$6);
|
|
1559
1830
|
_$insert2(_el$6, (() => {
|
|
1560
1831
|
var _c$2 = _$memo2(() => props.state.health()?.mode === "observe");
|
|
1561
1832
|
return () => _c$2() ? current()?.pressure === "crossed" ? "Observe: would compact" : "Observe: no admission" : "";
|
|
@@ -1569,9 +1840,9 @@ function GuardView(props) {
|
|
|
1569
1840
|
return visible();
|
|
1570
1841
|
},
|
|
1571
1842
|
children: (row2) => (() => {
|
|
1572
|
-
var _el$
|
|
1573
|
-
_$setProp2(_el$
|
|
1574
|
-
_$setProp2(_el$
|
|
1843
|
+
var _el$13 = _$createElement2("box");
|
|
1844
|
+
_$setProp2(_el$13, "flexDirection", "column");
|
|
1845
|
+
_$setProp2(_el$13, "onMouseDown", () => {
|
|
1575
1846
|
if (row2.hidden)
|
|
1576
1847
|
props.state.expand(row2.node.sessionId);
|
|
1577
1848
|
else {
|
|
@@ -1583,19 +1854,19 @@ function GuardView(props) {
|
|
|
1583
1854
|
});
|
|
1584
1855
|
}
|
|
1585
1856
|
});
|
|
1586
|
-
_$insert2(_el$
|
|
1857
|
+
_$insert2(_el$13, _$createComponent2(For2, {
|
|
1587
1858
|
get each() {
|
|
1588
1859
|
return rowLines(row2, Math.max(1, props.width - (props.expanded ? 2 : 0))).map((line) => props.expanded ? `${row2.node.sessionId === props.selectedId ? ">" : " "} ${line}` : line);
|
|
1589
1860
|
},
|
|
1590
1861
|
children: (line) => (() => {
|
|
1591
|
-
var _el$
|
|
1592
|
-
_$insert2(_el$
|
|
1593
|
-
_$effect2((_$p) => _$setProp2(_el$
|
|
1594
|
-
return _el$
|
|
1862
|
+
var _el$14 = _$createElement2("text");
|
|
1863
|
+
_$insert2(_el$14, line);
|
|
1864
|
+
_$effect2((_$p) => _$setProp2(_el$14, "fg", row2.pressure === "crossed" ? props.context.theme.text.feedback.error.base : row2.pressure === "near" ? props.context.theme.text.feedback.warning.base : props.context.theme.text.base, _$p));
|
|
1865
|
+
return _el$14;
|
|
1595
1866
|
})()
|
|
1596
1867
|
}));
|
|
1597
|
-
_$effect2((_$p) => _$setProp2(_el$
|
|
1598
|
-
return _el$
|
|
1868
|
+
_$effect2((_$p) => _$setProp2(_el$13, "id", `context-guard.row.${row2.node.sessionId}`, _$p));
|
|
1869
|
+
return _el$13;
|
|
1599
1870
|
})()
|
|
1600
1871
|
}), _el$0);
|
|
1601
1872
|
_$setProp2(_el$0, "onMouseDown", () => props.context.ui.panel.open("context-guard.monitor"));
|
|
@@ -1626,26 +1897,242 @@ function GuardView(props) {
|
|
|
1626
1897
|
}
|
|
1627
1898
|
|
|
1628
1899
|
// src/tui/panel.tsx
|
|
1900
|
+
import { effect as _$effect4 } from "@opentui/solid";
|
|
1901
|
+
import { memo as _$memo3 } from "@opentui/solid";
|
|
1902
|
+
import { insertNode as _$insertNode4 } from "@opentui/solid";
|
|
1903
|
+
import { insert as _$insert4 } from "@opentui/solid";
|
|
1904
|
+
import { createComponent as _$createComponent4 } from "@opentui/solid";
|
|
1905
|
+
import { use as _$use } from "@opentui/solid";
|
|
1906
|
+
import { setProp as _$setProp4 } from "@opentui/solid";
|
|
1907
|
+
import { createElement as _$createElement4 } from "@opentui/solid";
|
|
1908
|
+
import { createSignal as createSignal4, createMemo as createMemo3, createEffect as createEffect4, onCleanup as onCleanup3, Show as Show3, For as For4 } from "solid-js";
|
|
1909
|
+
|
|
1910
|
+
// src/tui/charts.tsx
|
|
1629
1911
|
import { effect as _$effect3 } from "@opentui/solid";
|
|
1630
|
-
import { insert as _$insert3 } from "@opentui/solid";
|
|
1631
|
-
import { createTextNode as _$createTextNode3 } from "@opentui/solid";
|
|
1632
1912
|
import { insertNode as _$insertNode3 } from "@opentui/solid";
|
|
1913
|
+
import { insert as _$insert3 } from "@opentui/solid";
|
|
1633
1914
|
import { createComponent as _$createComponent3 } from "@opentui/solid";
|
|
1634
1915
|
import { setProp as _$setProp3 } from "@opentui/solid";
|
|
1635
|
-
import { use as _$use } from "@opentui/solid";
|
|
1636
1916
|
import { createElement as _$createElement3 } from "@opentui/solid";
|
|
1637
|
-
import {
|
|
1917
|
+
import { createEffect as createEffect3, createSignal as createSignal3, onCleanup as onCleanup2, For as For3 } from "solid-js";
|
|
1918
|
+
|
|
1919
|
+
// src/tui/chartformat.ts
|
|
1920
|
+
function columns2(points, from, to, width, gaps = []) {
|
|
1921
|
+
const size = Math.max(1, Math.min(120, Math.floor(width))), values = Array.from({ length: size });
|
|
1922
|
+
const column = (time) => Math.max(0, Math.min(size - 1, Math.floor((time - from) / Math.max(1, to - from) * (size - 1))));
|
|
1923
|
+
let min = Infinity, max = -Infinity;
|
|
1924
|
+
for (const point of points) {
|
|
1925
|
+
if (point.time < from || point.time > to || point.value === undefined || !Number.isFinite(point.value) || point.value < 0)
|
|
1926
|
+
continue;
|
|
1927
|
+
min = Math.min(min, point.value);
|
|
1928
|
+
max = Math.max(max, point.value);
|
|
1929
|
+
const i = column(point.time);
|
|
1930
|
+
values[i] = Math.max(values[i] ?? -Infinity, point.value);
|
|
1931
|
+
}
|
|
1932
|
+
for (const time of gaps)
|
|
1933
|
+
if (time >= from && time <= to)
|
|
1934
|
+
values[column(time)] = undefined;
|
|
1935
|
+
return { values, column, min, max };
|
|
1936
|
+
}
|
|
1937
|
+
function sparkline(points, from, to, width, gaps = []) {
|
|
1938
|
+
const { values, column, min, max } = columns2(points, from, to, width, gaps);
|
|
1939
|
+
const bars = "\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
|
|
1940
|
+
const chars = values.map((v) => v === undefined ? " " : bars[Math.min(7, Math.floor(v / Math.max(1, max) * 7))] ?? "\u2581");
|
|
1941
|
+
for (const time of gaps)
|
|
1942
|
+
if (time >= from && time <= to)
|
|
1943
|
+
chars[column(time)] = "\u2506";
|
|
1944
|
+
return { line: chars.join(""), ...min === Infinity ? {} : { min, max } };
|
|
1945
|
+
}
|
|
1946
|
+
var chartMetrics = [
|
|
1947
|
+
{ label: "Live TPS ~", unit: "t/s", key: "liveTps", source: "points" },
|
|
1948
|
+
{ label: "Avg model", unit: "t/s", key: "averageModelTps", source: "points" },
|
|
1949
|
+
{
|
|
1950
|
+
label: "Avg session",
|
|
1951
|
+
unit: "t/s",
|
|
1952
|
+
key: "averageSessionTps",
|
|
1953
|
+
source: "points"
|
|
1954
|
+
},
|
|
1955
|
+
{ label: "TTFT ~", unit: "s", key: "ttftMs", source: "requests" },
|
|
1956
|
+
{ label: "Request throughput", unit: "t/s", key: "tps", source: "requests" },
|
|
1957
|
+
{
|
|
1958
|
+
label: "Cumulative output",
|
|
1959
|
+
unit: "tokens",
|
|
1960
|
+
key: "sessionOutputTokens",
|
|
1961
|
+
source: "points"
|
|
1962
|
+
}
|
|
1963
|
+
];
|
|
1964
|
+
function metricPlot(points, from, to, width, gaps = []) {
|
|
1965
|
+
const label = (value) => value >= 1e6 ? `${(value / 1e6).toFixed(1)}M` : value >= 1000 ? `${(value / 1000).toFixed(1)}k` : value.toFixed(1);
|
|
1966
|
+
const peak = Math.max(0, ...points.filter((p) => p.time >= from && p.time <= to && Number.isFinite(p.value)).map((p) => p.value ?? 0));
|
|
1967
|
+
const scale = Math.max(1, peak);
|
|
1968
|
+
const gutter = width >= 20 ? Math.max(label(scale).length, 3) : 0;
|
|
1969
|
+
const { values, column } = columns2(points, from, to, Math.max(1, width - gutter - 2), gaps);
|
|
1970
|
+
const gapColumns = new Set(gaps.filter((time) => time >= from && time <= to).map(column));
|
|
1971
|
+
const height = 6;
|
|
1972
|
+
const blocks = " \u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
|
|
1973
|
+
const lines = Array.from({ length: height }, (_, row2) => {
|
|
1974
|
+
const axis = gutter ? (row2 === 0 ? label(scale) : row2 === height - 1 ? "0.0" : "").padStart(gutter) : "";
|
|
1975
|
+
return `${axis}\u2502${values.map((value, i) => {
|
|
1976
|
+
if (gapColumns.has(i))
|
|
1977
|
+
return "\u2506";
|
|
1978
|
+
if (value === undefined)
|
|
1979
|
+
return " ";
|
|
1980
|
+
const fill = Math.max(0, Math.min(8, Math.ceil((value / scale * height - (height - row2 - 1)) * 8)));
|
|
1981
|
+
return value === 0 && row2 === height - 1 ? "\u2581" : blocks[fill];
|
|
1982
|
+
}).join("")}\u2502`;
|
|
1983
|
+
});
|
|
1984
|
+
lines.push(`${" ".repeat(gutter)}\u2514${"\u2500".repeat(values.length)}\u2518`);
|
|
1985
|
+
const start = new Date(from).toISOString().slice(11, 16), end = new Date(to).toISOString().slice(11, 16);
|
|
1986
|
+
const axisWidth = values.length + 2;
|
|
1987
|
+
if (axisWidth >= 11)
|
|
1988
|
+
lines.push(`${" ".repeat(gutter)}${start}${" ".repeat(axisWidth - 10)}${end}`);
|
|
1989
|
+
return lines;
|
|
1990
|
+
}
|
|
1991
|
+
function historyChartLines(result, width, from, to, metricIndex = 0) {
|
|
1992
|
+
if (!result.enabled)
|
|
1993
|
+
return wrap("History recording is disabled on this server.", width);
|
|
1994
|
+
const series = result.data.series[0];
|
|
1995
|
+
if (!series)
|
|
1996
|
+
return wrap("No retained measurements in this window.", width);
|
|
1997
|
+
const gaps = result.data.gaps.map((g) => g.time);
|
|
1998
|
+
const observations = (key) => series.points.map((p) => ({
|
|
1999
|
+
time: p.time,
|
|
2000
|
+
...p[key] === undefined ? {} : { value: p[key] }
|
|
2001
|
+
}));
|
|
2002
|
+
const requests = (key) => series.requests.map((p) => ({
|
|
2003
|
+
time: p.time,
|
|
2004
|
+
...p[key] === undefined ? {} : { value: key === "ttftMs" ? p[key] / 1000 : p[key] }
|
|
2005
|
+
}));
|
|
2006
|
+
const metric2 = chartMetrics[metricIndex] ?? chartMetrics[0];
|
|
2007
|
+
const points = metric2.source === "points" ? observations(metric2.key) : requests(metric2.key);
|
|
2008
|
+
const plotted = sparkline(points, from, to, width - 2, gaps);
|
|
2009
|
+
return [
|
|
2010
|
+
...wrap(`\u2039 ${metric2.label} \u203A \xB7 ${metricIndex + 1}/${chartMetrics.length}`, width),
|
|
2011
|
+
...wrap(plotted.min === undefined ? "No measurements for this metric." : `${plotted.min.toFixed(1)}\u2013${plotted.max?.toFixed(1)} ${metric2.unit} \xB7 observed range`, width),
|
|
2012
|
+
"",
|
|
2013
|
+
...metricPlot(points, from, to, width, gaps),
|
|
2014
|
+
"",
|
|
2015
|
+
...wrap("UTC time \u2192 \xB7 peaks per column", width),
|
|
2016
|
+
...wrap("blank = no sample \xB7 \u2506 = gap", width),
|
|
2017
|
+
...wrap(`${new Date(from).toISOString()} \u2192 ${new Date(to).toISOString()}`, width),
|
|
2018
|
+
...series.points.some((p) => p.partial) ? wrap("Partial timing coverage; averages include only matched model durations.", width) : [],
|
|
2019
|
+
...result.truncated ? wrap("Downsampled/bounded history; some older requests or gap markers may be omitted.", width) : [],
|
|
2020
|
+
...result.metadata.malformedRecords ? wrap(`${result.metadata.malformedRecords} malformed/partial records skipped.`, width) : []
|
|
2021
|
+
];
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
// src/tui/charts.tsx
|
|
2025
|
+
function HistoryCharts(props) {
|
|
2026
|
+
const [result, setResult] = createSignal3();
|
|
2027
|
+
const [status, setStatus] = createSignal3("Loading retained history...");
|
|
2028
|
+
const [range, setRange] = createSignal3({
|
|
2029
|
+
from: 0,
|
|
2030
|
+
to: 0
|
|
2031
|
+
});
|
|
2032
|
+
createEffect3(() => {
|
|
2033
|
+
const { sessionId: id, windowMs } = props;
|
|
2034
|
+
props.refresh;
|
|
2035
|
+
let pending = false, disposed = false;
|
|
2036
|
+
let controller;
|
|
2037
|
+
setResult(undefined);
|
|
2038
|
+
async function read() {
|
|
2039
|
+
if (pending || disposed)
|
|
2040
|
+
return;
|
|
2041
|
+
pending = true;
|
|
2042
|
+
controller = new AbortController;
|
|
2043
|
+
const to = Date.now(), from = Math.max(0, to - windowMs);
|
|
2044
|
+
try {
|
|
2045
|
+
const next = await props.state.history(id, from, to, AbortSignal.any([controller.signal, AbortSignal.timeout(5000)]));
|
|
2046
|
+
if (!disposed) {
|
|
2047
|
+
setResult(next);
|
|
2048
|
+
setRange({
|
|
2049
|
+
from,
|
|
2050
|
+
to
|
|
2051
|
+
});
|
|
2052
|
+
setStatus("");
|
|
2053
|
+
}
|
|
2054
|
+
} catch {
|
|
2055
|
+
if (!disposed)
|
|
2056
|
+
setStatus("History unavailable. The owning server must support chart history RPC. Press r to retry.");
|
|
2057
|
+
} finally {
|
|
2058
|
+
pending = false;
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
read();
|
|
2062
|
+
const timer = setInterval(() => {
|
|
2063
|
+
read();
|
|
2064
|
+
}, 5000);
|
|
2065
|
+
onCleanup2(() => {
|
|
2066
|
+
disposed = true;
|
|
2067
|
+
controller?.abort();
|
|
2068
|
+
clearInterval(timer);
|
|
2069
|
+
});
|
|
2070
|
+
});
|
|
2071
|
+
const lines = () => {
|
|
2072
|
+
const current = result();
|
|
2073
|
+
return current ? historyChartLines(current, Math.max(4, props.width), range().from, range().to, props.metricIndex) : [];
|
|
2074
|
+
};
|
|
2075
|
+
return (() => {
|
|
2076
|
+
var _el$ = _$createElement3("box"), _el$2 = _$createElement3("box");
|
|
2077
|
+
_$insertNode3(_el$, _el$2);
|
|
2078
|
+
_$setProp3(_el$, "flexDirection", "column");
|
|
2079
|
+
_$insert3(_el$, _$createComponent3(For3, {
|
|
2080
|
+
get each() {
|
|
2081
|
+
return wrap(`Performance charts \xB7 ${props.sessionId}`, props.width);
|
|
2082
|
+
},
|
|
2083
|
+
children: (line) => (() => {
|
|
2084
|
+
var _el$3 = _$createElement3("text");
|
|
2085
|
+
_$insert3(_el$3, line);
|
|
2086
|
+
_$effect3((_$p) => _$setProp3(_el$3, "fg", props.context.theme.text.base, _$p));
|
|
2087
|
+
return _el$3;
|
|
2088
|
+
})()
|
|
2089
|
+
}), _el$2);
|
|
2090
|
+
_$insert3(_el$, _$createComponent3(For3, {
|
|
2091
|
+
get each() {
|
|
2092
|
+
return wrap(status(), props.width);
|
|
2093
|
+
},
|
|
2094
|
+
children: (line) => (() => {
|
|
2095
|
+
var _el$4 = _$createElement3("text");
|
|
2096
|
+
_$insert3(_el$4, line);
|
|
2097
|
+
_$effect3((_$p) => _$setProp3(_el$4, "fg", props.context.theme.text.muted, _$p));
|
|
2098
|
+
return _el$4;
|
|
2099
|
+
})()
|
|
2100
|
+
}), _el$2);
|
|
2101
|
+
_$setProp3(_el$2, "flexDirection", "column");
|
|
2102
|
+
_$setProp3(_el$2, "paddingTop", 1);
|
|
2103
|
+
_$insert3(_el$2, _$createComponent3(For3, {
|
|
2104
|
+
get each() {
|
|
2105
|
+
return lines();
|
|
2106
|
+
},
|
|
2107
|
+
children: (line) => (() => {
|
|
2108
|
+
var _el$5 = _$createElement3("text");
|
|
2109
|
+
_$insert3(_el$5, line || " ");
|
|
2110
|
+
_$effect3((_$p) => _$setProp3(_el$5, "fg", props.context.theme.text.base, _$p));
|
|
2111
|
+
return _el$5;
|
|
2112
|
+
})()
|
|
2113
|
+
}));
|
|
2114
|
+
return _el$;
|
|
2115
|
+
})();
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
// src/tui/panel.tsx
|
|
1638
2119
|
function GuardPanel(props) {
|
|
1639
|
-
const [
|
|
1640
|
-
const [
|
|
1641
|
-
const
|
|
2120
|
+
const [showCharts, setShowCharts] = createSignal4(false);
|
|
2121
|
+
const [metricIndex, setMetricIndex] = createSignal4(0);
|
|
2122
|
+
const width = () => Math.max(4, props.input.width - 4);
|
|
2123
|
+
const windows = [300000, 3600000, 86400000, 7 * 86400000];
|
|
2124
|
+
const [windowIndex, setWindowIndex] = createSignal4(1);
|
|
2125
|
+
const [chartRefresh, setChartRefresh] = createSignal4(0);
|
|
2126
|
+
const [inspectId, setInspectId] = createSignal4();
|
|
2127
|
+
const [selected, setSelected] = createSignal4(props.input.sessionID);
|
|
2128
|
+
const [order, setOrder] = createSignal4([]);
|
|
1642
2129
|
const rows = createMemo3(() => props.state.rows(order()));
|
|
1643
2130
|
const offset = () => Math.max(0, rows().findIndex((row2) => row2.node.sessionId === selected()));
|
|
1644
|
-
|
|
2131
|
+
createEffect4(() => {
|
|
1645
2132
|
if (!props.input.focused)
|
|
1646
2133
|
setOrder([]);
|
|
1647
2134
|
});
|
|
1648
|
-
|
|
2135
|
+
createEffect4(() => {
|
|
1649
2136
|
setSelected(props.input.sessionID);
|
|
1650
2137
|
setOrder([]);
|
|
1651
2138
|
});
|
|
@@ -1666,7 +2153,7 @@ function GuardPanel(props) {
|
|
|
1666
2153
|
props.context.renderer.setFrameCallback(follow);
|
|
1667
2154
|
props.context.renderer.requestRender();
|
|
1668
2155
|
}
|
|
1669
|
-
|
|
2156
|
+
onCleanup3(() => {
|
|
1670
2157
|
if (follow)
|
|
1671
2158
|
props.context.renderer.removeFrameCallback(follow);
|
|
1672
2159
|
});
|
|
@@ -1675,12 +2162,55 @@ function GuardPanel(props) {
|
|
|
1675
2162
|
const row2 = rows()[Math.max(0, Math.min(rows().length - 1, offset() + delta))];
|
|
1676
2163
|
if (row2) {
|
|
1677
2164
|
setSelected(row2.node.sessionId);
|
|
1678
|
-
|
|
2165
|
+
if (!showCharts())
|
|
2166
|
+
revealSelection();
|
|
1679
2167
|
}
|
|
1680
2168
|
}
|
|
1681
2169
|
props.context.keymap.layer(() => ({
|
|
1682
2170
|
enabled: () => props.input.focused,
|
|
1683
2171
|
commands: [{
|
|
2172
|
+
id: "context-guard.close",
|
|
2173
|
+
title: "Close Context Guard",
|
|
2174
|
+
bind: "q",
|
|
2175
|
+
run: () => props.input.close()
|
|
2176
|
+
}, {
|
|
2177
|
+
id: "context-guard.chart-previous",
|
|
2178
|
+
title: "Previous chart metric",
|
|
2179
|
+
bind: "left",
|
|
2180
|
+
run: () => {
|
|
2181
|
+
if (showCharts())
|
|
2182
|
+
setMetricIndex((i) => (i + chartMetrics.length - 1) % chartMetrics.length);
|
|
2183
|
+
}
|
|
2184
|
+
}, {
|
|
2185
|
+
id: "context-guard.chart-next",
|
|
2186
|
+
title: "Next chart metric",
|
|
2187
|
+
bind: "right",
|
|
2188
|
+
run: () => {
|
|
2189
|
+
if (showCharts())
|
|
2190
|
+
setMetricIndex((i) => (i + 1) % chartMetrics.length);
|
|
2191
|
+
}
|
|
2192
|
+
}, {
|
|
2193
|
+
id: "context-guard.charts",
|
|
2194
|
+
title: "Toggle performance charts",
|
|
2195
|
+
bind: "c",
|
|
2196
|
+
run: () => setShowCharts((v) => !v)
|
|
2197
|
+
}, {
|
|
2198
|
+
id: "context-guard.chart-shorter",
|
|
2199
|
+
title: "Shorter chart window",
|
|
2200
|
+
bind: "[",
|
|
2201
|
+
run: () => {
|
|
2202
|
+
if (showCharts())
|
|
2203
|
+
setWindowIndex((i) => Math.max(0, i - 1));
|
|
2204
|
+
}
|
|
2205
|
+
}, {
|
|
2206
|
+
id: "context-guard.chart-longer",
|
|
2207
|
+
title: "Longer chart window",
|
|
2208
|
+
bind: "]",
|
|
2209
|
+
run: () => {
|
|
2210
|
+
if (showCharts())
|
|
2211
|
+
setWindowIndex((i) => Math.min(windows.length - 1, i + 1));
|
|
2212
|
+
}
|
|
2213
|
+
}, {
|
|
1684
2214
|
id: "context-guard.inspect",
|
|
1685
2215
|
title: "Inspect selected session",
|
|
1686
2216
|
bind: "i",
|
|
@@ -1692,6 +2222,7 @@ function GuardPanel(props) {
|
|
|
1692
2222
|
run: () => {
|
|
1693
2223
|
setOrder([]);
|
|
1694
2224
|
props.state.refresh();
|
|
2225
|
+
setChartRefresh((n) => n + 1);
|
|
1695
2226
|
}
|
|
1696
2227
|
}, {
|
|
1697
2228
|
id: "context-guard.load-more",
|
|
@@ -1729,18 +2260,26 @@ function GuardPanel(props) {
|
|
|
1729
2260
|
}
|
|
1730
2261
|
}]
|
|
1731
2262
|
}));
|
|
2263
|
+
const hints = () => showCharts() ? ["c back \xB7 q close", "\u2190 / \u2192 metric \xB7 \u2191 / \u2193 session", `[ / ] window \xB7 ${["5 minutes", "1 hour", "24 hours", "7 days"][windowIndex()]}`, "r refresh \xB7 Enter open session"] : ["c charts \xB7 q close", `\u2191 / \u2193 select ${offset() + 1} \xB7 Enter open`, "space expand \xB7 i inspect", "r refresh \xB7 l load more"];
|
|
1732
2264
|
return (() => {
|
|
1733
|
-
var _el$ = _$
|
|
1734
|
-
_$
|
|
1735
|
-
_$
|
|
1736
|
-
_$
|
|
1737
|
-
_$
|
|
1738
|
-
_$
|
|
2265
|
+
var _el$ = _$createElement4("box"), _el$2 = _$createElement4("scrollbox"), _el$3 = _$createElement4("box"), _el$4 = _$createElement4("text"), _el$5 = _$createElement4("box");
|
|
2266
|
+
_$insertNode4(_el$, _el$2);
|
|
2267
|
+
_$insertNode4(_el$, _el$5);
|
|
2268
|
+
_$setProp4(_el$, "flexDirection", "column");
|
|
2269
|
+
_$setProp4(_el$, "flexGrow", 1);
|
|
2270
|
+
_$setProp4(_el$, "paddingLeft", 2);
|
|
2271
|
+
_$setProp4(_el$, "paddingRight", 2);
|
|
2272
|
+
_$setProp4(_el$, "paddingTop", 1);
|
|
2273
|
+
_$setProp4(_el$, "paddingBottom", 1);
|
|
2274
|
+
_$insertNode4(_el$2, _el$3);
|
|
1739
2275
|
_$use((value) => {
|
|
1740
2276
|
scroll = value;
|
|
1741
|
-
}, _el$);
|
|
1742
|
-
_$
|
|
1743
|
-
_$
|
|
2277
|
+
}, _el$2);
|
|
2278
|
+
_$setProp4(_el$2, "flexGrow", 1);
|
|
2279
|
+
_$setProp4(_el$2, "minHeight", 0);
|
|
2280
|
+
_$insertNode4(_el$3, _el$4);
|
|
2281
|
+
_$setProp4(_el$3, "flexDirection", "column");
|
|
2282
|
+
_$insert4(_el$3, _$createComponent4(GuardView, {
|
|
1744
2283
|
get sessionID() {
|
|
1745
2284
|
return props.input.sessionID;
|
|
1746
2285
|
},
|
|
@@ -1751,7 +2290,7 @@ function GuardPanel(props) {
|
|
|
1751
2290
|
return props.context;
|
|
1752
2291
|
},
|
|
1753
2292
|
get width() {
|
|
1754
|
-
return
|
|
2293
|
+
return width();
|
|
1755
2294
|
},
|
|
1756
2295
|
expanded: true,
|
|
1757
2296
|
get family() {
|
|
@@ -1763,28 +2302,60 @@ function GuardPanel(props) {
|
|
|
1763
2302
|
get inspectId() {
|
|
1764
2303
|
return inspectId();
|
|
1765
2304
|
}
|
|
1766
|
-
}), _el$
|
|
1767
|
-
_$
|
|
1768
|
-
_$
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
2305
|
+
}), _el$4);
|
|
2306
|
+
_$insert4(_el$4, () => wrap("Guard input = prompt + caches. Native Context uses different totals.", width()).join("\n"));
|
|
2307
|
+
_$insert4(_el$2, _$createComponent4(Show3, {
|
|
2308
|
+
get when() {
|
|
2309
|
+
return showCharts();
|
|
2310
|
+
},
|
|
2311
|
+
get children() {
|
|
2312
|
+
return _$createComponent4(HistoryCharts, {
|
|
2313
|
+
get sessionId() {
|
|
2314
|
+
return selected();
|
|
2315
|
+
},
|
|
2316
|
+
get windowMs() {
|
|
2317
|
+
return windows[windowIndex()] ?? 3600000;
|
|
2318
|
+
},
|
|
2319
|
+
get metricIndex() {
|
|
2320
|
+
return metricIndex();
|
|
2321
|
+
},
|
|
2322
|
+
get refresh() {
|
|
2323
|
+
return chartRefresh();
|
|
2324
|
+
},
|
|
2325
|
+
get width() {
|
|
2326
|
+
return width();
|
|
2327
|
+
},
|
|
2328
|
+
get state() {
|
|
2329
|
+
return props.state;
|
|
2330
|
+
},
|
|
2331
|
+
get context() {
|
|
2332
|
+
return props.context;
|
|
2333
|
+
}
|
|
2334
|
+
});
|
|
2335
|
+
}
|
|
2336
|
+
}), null);
|
|
2337
|
+
_$setProp4(_el$5, "flexDirection", "column");
|
|
2338
|
+
_$setProp4(_el$5, "flexShrink", 0);
|
|
2339
|
+
_$setProp4(_el$5, "paddingTop", 1);
|
|
2340
|
+
_$insert4(_el$5, _$createComponent4(For4, {
|
|
2341
|
+
get each() {
|
|
2342
|
+
return hints().flatMap((hint) => wrap(hint, width()));
|
|
2343
|
+
},
|
|
2344
|
+
children: (line) => (() => {
|
|
2345
|
+
var _el$6 = _$createElement4("text");
|
|
2346
|
+
_$insert4(_el$6, line);
|
|
2347
|
+
_$effect4((_$p) => _$setProp4(_el$6, "fg", props.context.theme.text.muted, _$p));
|
|
2348
|
+
return _el$6;
|
|
2349
|
+
})()
|
|
2350
|
+
}));
|
|
2351
|
+
_$effect4((_p$) => {
|
|
2352
|
+
var _v$ = !showCharts(), _v$2 = props.context.theme.text.muted;
|
|
2353
|
+
_v$ !== _p$.e && (_p$.e = _$setProp4(_el$3, "visible", _v$, _p$.e));
|
|
2354
|
+
_v$2 !== _p$.t && (_p$.t = _$setProp4(_el$4, "fg", _v$2, _p$.t));
|
|
1781
2355
|
return _p$;
|
|
1782
2356
|
}, {
|
|
1783
2357
|
e: undefined,
|
|
1784
|
-
t: undefined
|
|
1785
|
-
a: undefined,
|
|
1786
|
-
o: undefined,
|
|
1787
|
-
i: undefined
|
|
2358
|
+
t: undefined
|
|
1788
2359
|
});
|
|
1789
2360
|
return _el$;
|
|
1790
2361
|
})();
|
|
@@ -1801,7 +2372,7 @@ var tui_default = Plugin.define({
|
|
|
1801
2372
|
try {
|
|
1802
2373
|
unregister.push(context.ui.slot({
|
|
1803
2374
|
append: "sidebar.content",
|
|
1804
|
-
render: (input) => _$
|
|
2375
|
+
render: (input) => _$createComponent5(GuardView, {
|
|
1805
2376
|
get sessionID() {
|
|
1806
2377
|
return input.sessionID;
|
|
1807
2378
|
},
|
|
@@ -1812,12 +2383,12 @@ var tui_default = Plugin.define({
|
|
|
1812
2383
|
}));
|
|
1813
2384
|
unregister.push(context.ui.slot({
|
|
1814
2385
|
append: "session.panel",
|
|
1815
|
-
render: (input) => _$
|
|
2386
|
+
render: (input) => _$createComponent5(Show4, {
|
|
1816
2387
|
get when() {
|
|
1817
2388
|
return input.name === "context-guard.monitor";
|
|
1818
2389
|
},
|
|
1819
2390
|
get children() {
|
|
1820
|
-
return _$
|
|
2391
|
+
return _$createComponent5(GuardPanel, {
|
|
1821
2392
|
input,
|
|
1822
2393
|
state,
|
|
1823
2394
|
context
|
|
@@ -1827,15 +2398,15 @@ var tui_default = Plugin.define({
|
|
|
1827
2398
|
}));
|
|
1828
2399
|
unregister.push(context.ui.slot({
|
|
1829
2400
|
append: "session.composer.top",
|
|
1830
|
-
render: (input) => _$
|
|
2401
|
+
render: (input) => _$createComponent5(Show4, {
|
|
1831
2402
|
get when() {
|
|
1832
2403
|
return context.data.session.get(input.sessionID)?.parentID;
|
|
1833
2404
|
},
|
|
1834
2405
|
get children() {
|
|
1835
|
-
var _el$ = _$
|
|
1836
|
-
_$
|
|
1837
|
-
_$
|
|
1838
|
-
_$
|
|
2406
|
+
var _el$ = _$createElement5("text");
|
|
2407
|
+
_$insertNode5(_el$, _$createTextNode3(`Context Guard: open panel`));
|
|
2408
|
+
_$setProp5(_el$, "onMouseDown", () => context.ui.panel.open("context-guard.monitor"));
|
|
2409
|
+
_$effect5((_$p) => _$setProp5(_el$, "fg", context.theme.text.muted, _$p));
|
|
1839
2410
|
return _el$;
|
|
1840
2411
|
}
|
|
1841
2412
|
})
|