@krtclcdy/tersio 2.16.0 → 2.18.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 +18 -2
- package/cli/dashboard.js +4 -0
- package/cli/usage.js +21 -6
- package/dashboard/app.js +395 -89
- package/dashboard/styles.css +39 -5
- package/dashboard/template.html +47 -19
- package/extensions/caveman-session/index.js +3 -4
- package/extensions/combo-toggle/index.js +3 -4
- package/extensions/rtk-session/index.js +3 -4
- package/extensions/shared/rtk-gain.js +16 -1
- package/extensions/shared/session-state.js +15 -0
- package/extensions/shared/usage-ledger.js +72 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-

|
|
2
2
|
|
|
3
3
|
# ✂️ Tersio — Token-saving OMP Add-ons
|
|
4
4
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
[](https://github.com/KurutoDenzeru/tersio/actions)
|
|
9
9
|
[](./LICENSE)
|
|
10
10
|
|
|
11
|
-
Terse replies, compact shell output, and minimal code decisions for [Oh My Pi (OMP)](https://github.com/can1357/oh-my-pi) — one-command combo presets.
|
|
11
|
+
Terse replies, compact shell output, and minimal code decisions for [Oh My Pi (OMP)](https://github.com/can1357/oh-my-pi) — one-command combo presets.
|
|
12
12
|
|
|
13
13
|
## ⚡ Getting Started
|
|
14
14
|
|
|
@@ -80,6 +80,22 @@ Measured savings against the same workload without the modes. Token counts are r
|
|
|
80
80
|
|
|
81
81
|
Break-even math, balanced preset (caveman full + rtk + ponytail): a mixed turn costs 941 tok baseline vs 399 with Tersio (0.42×, **−57.6% per turn**). Overhead repays in turn 1 with the bundled floor (464 tok); turn 4 with the real installed Ponytail plugin (v4.9.0, 1,264 tok). RTK concentrates savings where noise lives — hook-wired sessions record **−97.5% on summarized test suites** (rtk `history.db`, 4 runs).
|
|
82
82
|
|
|
83
|
+
## 📈 Gain Dashboard
|
|
84
|
+
|
|
85
|
+
`tersio gain --open` serves a local dashboard (127.0.0.1 only) that charts your own savings from `~/.omp/plugins/tersio-usage.jsonl`. Three views:
|
|
86
|
+
|
|
87
|
+
**Live feed and savings.** Token throughput, cost, savings bento, and recent activity — the top of the dashboard.
|
|
88
|
+
|
|
89
|
+

|
|
90
|
+
|
|
91
|
+
**Top models and recent requests.** Per-model cost breakdown plus the last requests with elapsed time and token speed.
|
|
92
|
+
|
|
93
|
+

|
|
94
|
+
|
|
95
|
+
**Command tools.** Token savings, average rate, and timing per shell tool.
|
|
96
|
+
|
|
97
|
+

|
|
98
|
+
|
|
83
99
|
## 🖥️ CLI
|
|
84
100
|
|
|
85
101
|
| Command | Purpose |
|
package/cli/dashboard.js
CHANGED
|
@@ -88,6 +88,10 @@ async function runDashboard(options) {
|
|
|
88
88
|
res.end(html);
|
|
89
89
|
});
|
|
90
90
|
server.listen(options.port, '127.0.0.1', () => {
|
|
91
|
+
try {
|
|
92
|
+
process.title = 'tersio gain';
|
|
93
|
+
}
|
|
94
|
+
catch { /* non-POSIX shells keep node */ }
|
|
91
95
|
const address = server.address();
|
|
92
96
|
const port = typeof address === 'object' && address ? address.port : options.port;
|
|
93
97
|
const url = `http://127.0.0.1:${port}`;
|
package/cli/usage.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
// cli/usage.ts — ledger + session-token usage report, tokscale-style.
|
|
2
|
+
import path from 'node:path';
|
|
2
3
|
import { co2GramsFor, energyWhFor, importSessionTokens, ledgerPath, priceFor, readResetWatermark, readUsage, refreshPricesIfStale, sessionsDir, usdCost, } from '../extensions/shared/usage-ledger.js';
|
|
3
4
|
import { readRtkGain } from '../extensions/shared/rtk-gain.js';
|
|
4
5
|
import { withInteractiveSpinner } from './interactive.js';
|
|
5
6
|
import { PACKAGE_VERSION } from './common.js';
|
|
7
|
+
// RTK's history is machine-wide, so every project's commands have to reach
|
|
8
|
+
// the table or a busy repo silently stands in for all of them. The old cap of
|
|
9
|
+
// 10 rows ranked by tokens saved hid whole projects whose commands save little
|
|
10
|
+
// to nothing; 2000 groups covers a long history (~1.7k today) while still
|
|
11
|
+
// bounding /data.json.
|
|
12
|
+
const RTK_COMMAND_ROWS = 2000;
|
|
6
13
|
export function summarizeUsage(rows) {
|
|
7
14
|
refreshPricesIfStale();
|
|
8
15
|
const byKind = {};
|
|
@@ -18,12 +25,14 @@ export function summarizeUsage(rows) {
|
|
|
18
25
|
let priced = true;
|
|
19
26
|
let savedUsd = 0;
|
|
20
27
|
const byModelUsd = {};
|
|
28
|
+
const byModelBucketUsd = {};
|
|
21
29
|
let co2g = 0;
|
|
22
30
|
let energyWh = 0;
|
|
23
31
|
for (const [model, t] of Object.entries(session.byModel)) {
|
|
24
32
|
const c = usdCost(t, model);
|
|
25
33
|
usd += c.usd;
|
|
26
34
|
byModelUsd[model] = c.usd;
|
|
35
|
+
byModelBucketUsd[model] = c.buckets;
|
|
27
36
|
co2g += co2GramsFor(model, t.output);
|
|
28
37
|
energyWh += energyWhFor(model, t.output);
|
|
29
38
|
if (!c.priced)
|
|
@@ -41,12 +50,16 @@ export function summarizeUsage(rows) {
|
|
|
41
50
|
tokens: session.totals,
|
|
42
51
|
byModel: session.byModel,
|
|
43
52
|
byModelUsd,
|
|
53
|
+
byModelBucketUsd,
|
|
44
54
|
byModelMessages: session.byModelMessages,
|
|
45
55
|
byDay: session.byDay,
|
|
46
56
|
byDayModel: session.byDayModel,
|
|
47
57
|
byTool,
|
|
48
|
-
recent: session.recent
|
|
49
|
-
|
|
58
|
+
recent: session.recent.map((r) => ({
|
|
59
|
+
...r,
|
|
60
|
+
est: usdCost({ input: r.i, output: r.o, cacheRead: r.cr ?? 0, cacheWrite: r.cw ?? 0 }, r.m).usd,
|
|
61
|
+
})),
|
|
62
|
+
rtkGain: readRtkGain(RTK_COMMAND_ROWS, watermark || undefined),
|
|
50
63
|
usd,
|
|
51
64
|
priced,
|
|
52
65
|
savedUsd,
|
|
@@ -119,28 +132,30 @@ function printReport(report) {
|
|
|
119
132
|
console.log(l);
|
|
120
133
|
}
|
|
121
134
|
}
|
|
122
|
-
const cmdRows = report.byTool.map(([tool, n]) => ({ name: tool, count: n, saved: null, avgPct: null, avgMs: null }));
|
|
135
|
+
const cmdRows = report.byTool.map(([tool, n]) => ({ name: tool, project: '', count: n, saved: null, avgPct: null, avgMs: null }));
|
|
123
136
|
for (const r of report.rtkGain.byCommand) {
|
|
124
|
-
cmdRows.push({ name: r.command, count: r.count, saved: r.saved, avgPct: r.avgPct, avgMs: r.avgMs });
|
|
137
|
+
cmdRows.push({ name: r.command, project: r.project, count: r.count, saved: r.saved, avgPct: r.avgPct, avgMs: r.avgMs });
|
|
125
138
|
}
|
|
126
139
|
cmdRows.sort((a, b) => b.count - a.count);
|
|
127
140
|
if (cmdRows.length) {
|
|
128
141
|
const g = report.rtkGain;
|
|
142
|
+
const projects = new Set(g.byCommand.map((r) => r.project).filter(Boolean));
|
|
129
143
|
const scope = g.commands
|
|
130
|
-
? ` COMMAND TOOLS ${fmt(report.byTool.length)} tools · ${fmt(g.commands)} commands · ${fmtShort(g.saved)} saved`
|
|
144
|
+
? ` COMMAND TOOLS ${fmt(report.byTool.length)} tools · ${fmt(g.commands)} commands · ${fmtShort(g.saved)} saved · ${fmt(projects.size)} projects`
|
|
131
145
|
: ' COMMAND TOOLS';
|
|
132
146
|
console.log(scope);
|
|
133
147
|
const top = cmdRows.reduce((m, r) => Math.max(m, r.count), 1);
|
|
134
148
|
const crows = cmdRows.slice(0, 15).map((r, idx) => [
|
|
135
149
|
String(idx + 1),
|
|
136
150
|
r.name,
|
|
151
|
+
r.project ? path.basename(r.project) : '–',
|
|
137
152
|
fmt(r.count),
|
|
138
153
|
r.saved === null ? '–' : fmtShort(r.saved),
|
|
139
154
|
r.avgPct === null ? '–' : `${r.avgPct.toFixed(1)}%`,
|
|
140
155
|
r.avgMs === null ? '–' : fmtMs(r.avgMs),
|
|
141
156
|
bar(r.count / top, 8),
|
|
142
157
|
]);
|
|
143
|
-
for (const l of table(['#', 'Tool/Command', 'Count', 'Saved', 'Avg%', 'Time', 'Impact'], crows, [true, false, true, true, true, true, false], 30)) {
|
|
158
|
+
for (const l of table(['#', 'Tool/Command', 'Project', 'Count', 'Saved', 'Avg%', 'Time', 'Impact'], crows, [true, false, false, true, true, true, true, false], 30)) {
|
|
144
159
|
console.log(l);
|
|
145
160
|
}
|
|
146
161
|
}
|