@krtclcdy/tersio 2.10.0 → 2.12.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 +3 -2
- package/cli/banner.js +5 -3
- package/cli/common.js +11 -5
- package/cli/dashboard.js +7 -1
- package/cli/doctor.js +8 -1
- package/cli/install.js +53 -83
- package/cli/reset.js +51 -0
- package/cli/uninstall.js +0 -1
- package/cli/update.js +2 -8
- package/cli/usage.js +4 -2
- package/dashboard/app.js +141 -20
- package/dashboard/styles.css +46 -8
- package/dashboard/template.html +116 -24
- package/extensions/caveman-session/index.js +12 -1
- package/extensions/combo-toggle/index.js +3 -3
- package/extensions/rtk-session/index.js +8 -1
- package/extensions/shared/session-state.js +10 -3
- package/extensions/shared/usage-ledger.js +18 -1
- package/extensions/tersio-commands/index.js +5 -10
- package/package.json +1 -1
- package/tersio.js +10 -3
|
@@ -78,8 +78,10 @@ export default function comboToggleExtension(pi) {
|
|
|
78
78
|
return useState(reconcileSharedComboEntries(sessionEntries(ctx)), ctx);
|
|
79
79
|
}
|
|
80
80
|
function listen(ctx) {
|
|
81
|
+
// Stable identity: the bridge set dedupes, so repeated track()/command
|
|
82
|
+
// calls register once instead of stacking duplicate listeners.
|
|
81
83
|
if (ctx?.hasUI)
|
|
82
|
-
setSharedComboListener(
|
|
84
|
+
setSharedComboListener(useState);
|
|
83
85
|
}
|
|
84
86
|
function track(ctx) {
|
|
85
87
|
listen(ctx);
|
|
@@ -112,8 +114,6 @@ export default function comboToggleExtension(pi) {
|
|
|
112
114
|
useState(setSharedComboLevel(level), ctx);
|
|
113
115
|
const active = activeModesSummary(getSharedComboState());
|
|
114
116
|
ctx?.ui?.notify?.(level === 'off' ? `Combo off — all tersio modes inactive for this session. Active: ${active}.` : `Combo ${level} on — ${active} active for this session.`, 'info');
|
|
115
|
-
if (ctx?.reload)
|
|
116
|
-
await ctx.reload();
|
|
117
117
|
},
|
|
118
118
|
});
|
|
119
119
|
pi.on('session_start', async (_event, ctx) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { activeModesSummary, asPromptArray, getSharedComboState, isComboPresetActive, isOmpSubagentPrompt, lastCustomValue, normalizeInputCommand, paintStatusBar, reconcileSharedComboEntries, sessionEntries, setSharedComboMode } from '../shared/session-state.js';
|
|
3
|
+
import { activeModesSummary, asPromptArray, getSharedComboState, isComboPresetActive, isOmpSubagentPrompt, lastCustomValue, normalizeInputCommand, paintStatusBar, reconcileSharedComboEntries, sessionEntries, setSharedComboListener, setSharedComboMode } from '../shared/session-state.js';
|
|
4
4
|
import { readRtkDefault } from '../shared/plugin-settings.js';
|
|
5
5
|
const DEFAULT_ENABLED = false;
|
|
6
6
|
function asBoolean(value) {
|
|
@@ -48,6 +48,13 @@ export default function rtkSessionExtension(pi) {
|
|
|
48
48
|
ctx?.ui?.notify?.(enabled ? `RTK on — compact shell output for this session. Active: ${active}.` : `RTK off. Active: ${active}.`, 'info');
|
|
49
49
|
}
|
|
50
50
|
pi.setLabel?.('RTK session toggle');
|
|
51
|
+
// Live mirror: a /tersio or /combo switch publishes shared state — adopt
|
|
52
|
+
// it at once so the next turn and the rtk_run gate see it, no reload.
|
|
53
|
+
function syncFromShared(state) {
|
|
54
|
+
enabled = state.rtk === 'on';
|
|
55
|
+
syncStatus();
|
|
56
|
+
}
|
|
57
|
+
setSharedComboListener(syncFromShared);
|
|
51
58
|
pi.registerCommand?.('rtk', {
|
|
52
59
|
description: 'Toggle RTK compact shell-output guidance for this session',
|
|
53
60
|
handler: async (args, ctx) => {
|
|
@@ -49,12 +49,13 @@ function bridge() {
|
|
|
49
49
|
if (existing?.state)
|
|
50
50
|
return existing;
|
|
51
51
|
const initial = normalizedState((existing || COMBO_LEVELS.off));
|
|
52
|
-
return (globalThis[BRIDGE_KEY] = { state: initial,
|
|
52
|
+
return (globalThis[BRIDGE_KEY] = { state: initial, listeners: new Set() });
|
|
53
53
|
}
|
|
54
54
|
function publish(state) {
|
|
55
55
|
const shared = bridge();
|
|
56
56
|
shared.state = state;
|
|
57
|
-
shared.
|
|
57
|
+
for (const listener of shared.listeners)
|
|
58
|
+
listener(state);
|
|
58
59
|
return state;
|
|
59
60
|
}
|
|
60
61
|
export function normalizeInputCommand(value) {
|
|
@@ -148,7 +149,13 @@ export function activeModesSummary(state) {
|
|
|
148
149
|
return `caveman=${state.caveman.toUpperCase()}, rtk=${state.rtk.toUpperCase()}, ponytail=${state.ponytail.toUpperCase()}`;
|
|
149
150
|
}
|
|
150
151
|
export function setSharedComboListener(listener) {
|
|
151
|
-
|
|
152
|
+
// Additive: every sibling extension syncs its local mirror on publish, so
|
|
153
|
+
// a /tersio or /combo switch takes effect next turn with no session reload.
|
|
154
|
+
// A null listener clears all (no current callers; reserved for teardown).
|
|
155
|
+
if (typeof listener === 'function')
|
|
156
|
+
bridge().listeners.add(listener);
|
|
157
|
+
else
|
|
158
|
+
bridge().listeners.clear();
|
|
152
159
|
}
|
|
153
160
|
export function resetSharedComboState() {
|
|
154
161
|
return setSharedComboLevel('off');
|
|
@@ -87,11 +87,16 @@ export function importSessionTokens() {
|
|
|
87
87
|
walkJsonl(codexSessionsDir(), files, 2000);
|
|
88
88
|
}
|
|
89
89
|
let codexProvider = null;
|
|
90
|
+
const recent = [];
|
|
90
91
|
function ingest(model, usage, ts) {
|
|
91
92
|
addInto(totals, usage);
|
|
92
93
|
byModel[model] ??= zeroBreakdown();
|
|
93
94
|
addInto(byModel[model], usage);
|
|
94
95
|
byModelMessages[model] = (byModelMessages[model] ?? 0) + 1;
|
|
96
|
+
const num = (v) => (typeof v === 'number' && Number.isFinite(v) && v > 0 ? Math.floor(v) : 0);
|
|
97
|
+
const ms = ts === undefined ? NaN : typeof ts === 'number' ? ts : Date.parse(ts);
|
|
98
|
+
if (Number.isFinite(ms))
|
|
99
|
+
recent.push({ m: model, i: num(usage.input), o: num(usage.output), t: ms });
|
|
95
100
|
const day = ts !== undefined ? dayKey(ts) : null;
|
|
96
101
|
if (day) {
|
|
97
102
|
byDay[day] ??= zeroBreakdown();
|
|
@@ -153,7 +158,8 @@ export function importSessionTokens() {
|
|
|
153
158
|
catch { /* skip corrupt lines */ }
|
|
154
159
|
}
|
|
155
160
|
}
|
|
156
|
-
|
|
161
|
+
recent.sort((a, b) => b.t - a.t);
|
|
162
|
+
return { messages, totals, byModel, byDay, byDayModel, byTool, byModelMessages, costMeasured, recent: recent.slice(0, 25) };
|
|
157
163
|
}
|
|
158
164
|
// Lead binary of a shell string: first segment head past `cd` chains and
|
|
159
165
|
// VAR=x assignments (`cd /x && git status` → `git`). Falls back to `bash`.
|
|
@@ -215,3 +221,14 @@ export function readUsage() {
|
|
|
215
221
|
}
|
|
216
222
|
return rows;
|
|
217
223
|
}
|
|
224
|
+
// Delete the ledger file. Returns the rows cleared (0 when already absent).
|
|
225
|
+
// Only tersio-owned data lives here; session transcripts and the RTK
|
|
226
|
+
// database are never touched.
|
|
227
|
+
export function clearUsageLedger() {
|
|
228
|
+
const rows = readUsage().length;
|
|
229
|
+
try {
|
|
230
|
+
fs.unlinkSync(ledgerPath());
|
|
231
|
+
}
|
|
232
|
+
catch { /* already absent */ }
|
|
233
|
+
return rows;
|
|
234
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// /tersio unified root command — routes every tersio capability through one
|
|
2
2
|
// namespaced entrypoint. Aliases (/caveman, /rtk, /combo, /ai-addons) keep
|
|
3
3
|
// working: this router drives the same shared bridge + session entries the
|
|
4
|
-
// sibling extensions restore from
|
|
5
|
-
//
|
|
4
|
+
// sibling extensions restore from. Shared-state publishes sync their local
|
|
5
|
+
// mirrors live, so switches notify and take effect next turn — no reload.
|
|
6
6
|
import { COMBO_LEVELS, activeModesSummary, getSharedComboState, normalizeComboLevel, normalizeMode, reconcileSharedComboEntries, sessionEntries, setSharedComboLevel, setSharedComboMode, } from '../shared/session-state.js';
|
|
7
7
|
import os from 'node:os';
|
|
8
8
|
import path from 'node:path';
|
|
@@ -56,10 +56,9 @@ async function openGainDashboard(pi, ctx) {
|
|
|
56
56
|
}
|
|
57
57
|
export default function tersioCommandsExtension(pi) {
|
|
58
58
|
pi.setLabel?.('Tersio unified root command');
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
59
|
+
// Mode switches never reload the session: entries persist, the shared
|
|
60
|
+
// bridge publishes to every sibling mirror, and the change injects on the
|
|
61
|
+
// next turn like a normal input message. Only notify below.
|
|
63
62
|
pi.registerCommand?.('tersio', {
|
|
64
63
|
description: 'Tersio root: caveman|combo|rtk|ponytail|status|check|update|gain|usage|help',
|
|
65
64
|
handler: async (args, ctx) => {
|
|
@@ -86,7 +85,6 @@ export default function tersioCommandsExtension(pi) {
|
|
|
86
85
|
setSharedComboMode('caveman', mode);
|
|
87
86
|
appendUsage('toggle', `caveman=${mode}`);
|
|
88
87
|
ctx?.ui?.notify?.(`Caveman ${mode} on — Active: ${activeModesSummary(getSharedComboState())}.`, 'info');
|
|
89
|
-
await reload(ctx);
|
|
90
88
|
return;
|
|
91
89
|
}
|
|
92
90
|
if (sub === 'rtk') {
|
|
@@ -104,7 +102,6 @@ export default function tersioCommandsExtension(pi) {
|
|
|
104
102
|
setSharedComboMode('rtk', enabled);
|
|
105
103
|
appendUsage('toggle', `rtk=${rest}`);
|
|
106
104
|
ctx?.ui?.notify?.(`RTK ${rest} — Active: ${activeModesSummary(getSharedComboState())}.`, 'info');
|
|
107
|
-
await reload(ctx);
|
|
108
105
|
return;
|
|
109
106
|
}
|
|
110
107
|
if (sub === 'ponytail') {
|
|
@@ -117,7 +114,6 @@ export default function tersioCommandsExtension(pi) {
|
|
|
117
114
|
setSharedComboMode('ponytail', mode);
|
|
118
115
|
appendUsage('toggle', `ponytail=${mode}`);
|
|
119
116
|
ctx?.ui?.notify?.(`Ponytail ${mode} — Active: ${activeModesSummary(getSharedComboState())}.`, 'info');
|
|
120
|
-
await reload(ctx);
|
|
121
117
|
return;
|
|
122
118
|
}
|
|
123
119
|
if (sub === 'combo') {
|
|
@@ -140,7 +136,6 @@ export default function tersioCommandsExtension(pi) {
|
|
|
140
136
|
setSharedComboLevel(level);
|
|
141
137
|
appendUsage('toggle', `combo=${level}`);
|
|
142
138
|
ctx?.ui?.notify?.(`Combo ${level} on — ${activeModesSummary(getSharedComboState())} active for this session.`, 'info');
|
|
143
|
-
await reload(ctx);
|
|
144
139
|
return;
|
|
145
140
|
}
|
|
146
141
|
if (sub === 'check') {
|
package/package.json
CHANGED
package/tersio.js
CHANGED
|
@@ -2,30 +2,32 @@
|
|
|
2
2
|
// tersio.ts — Install Tersio (caveman/rtk/ponytail) add-ons on any OMP device.
|
|
3
3
|
// Usage: node tersio.js [install|update|reinstall|doctor|uninstall|version|help] [options]
|
|
4
4
|
// Requires: node/npm and omp CLI
|
|
5
|
-
import { PACKAGE_BIN, PACKAGE_VERSION, applyUpdate, commandArg, gain, dashboardExport, dashboardOpen, dashboardPort, doctor, showHelp, showVersion, uninstall, unknownCommand, update, usage, } from './cli/common.js';
|
|
5
|
+
import { PACKAGE_BIN, PACKAGE_VERSION, applyUpdate, commandArg, gain, dashboardExport, dashboardOpen, dashboardPort, doctor, reset, showHelp, showVersion, uninstall, unknownCommand, update, usage, } from './cli/common.js';
|
|
6
6
|
import { closeRL } from './cli/interactive.js';
|
|
7
7
|
import { runInstall } from './cli/install.js';
|
|
8
8
|
import { runDoctor } from './cli/doctor.js';
|
|
9
9
|
import { runLatestUpdate } from './cli/update.js';
|
|
10
10
|
import { runUninstall } from './cli/uninstall.js';
|
|
11
11
|
import { runUsage } from './cli/usage.js';
|
|
12
|
+
import { runReset } from './cli/reset.js';
|
|
12
13
|
import { runDashboard } from './cli/dashboard.js';
|
|
13
14
|
function printHelp() {
|
|
14
15
|
console.log(`Usage: ${PACKAGE_BIN} [command] [options]
|
|
15
16
|
|
|
16
17
|
Commands:
|
|
17
|
-
install Install the add-ons (user scope
|
|
18
|
+
install Install the add-ons (user scope: all OMP sessions)
|
|
18
19
|
update Refresh the CLI and add-ons (RTK binary, Caveman rule, Ponytail)
|
|
19
20
|
reinstall Clean and reinstall the user-scope add-ons
|
|
20
21
|
doctor Check the current installation
|
|
21
22
|
usage Ledger-backed usage + savings report
|
|
22
23
|
gain Open the gain dashboard (localhost only)
|
|
24
|
+
reset Clear tersio statistics (usage ledger)
|
|
23
25
|
uninstall Remove the managed extensions
|
|
24
26
|
version Print the package version
|
|
25
27
|
help Show this help
|
|
26
28
|
|
|
27
29
|
Options:
|
|
28
|
-
--scope user
|
|
30
|
+
--scope user (legacy; accepted and ignored — user scope is the only scope)
|
|
29
31
|
--keep-ponytail (uninstall: keep the Ponytail plugin — removed by default)
|
|
30
32
|
--remove-rtk (uninstall: also remove the RTK binary)
|
|
31
33
|
--combo-default off|medium|balanced|max (implies caveman, rtk, ponytail)
|
|
@@ -79,6 +81,11 @@ async function main() {
|
|
|
79
81
|
closeRL();
|
|
80
82
|
return;
|
|
81
83
|
}
|
|
84
|
+
if (reset) {
|
|
85
|
+
await runReset();
|
|
86
|
+
closeRL();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
82
89
|
if (uninstall) {
|
|
83
90
|
await runUninstall();
|
|
84
91
|
closeRL();
|