@krtclcdy/tersio 2.25.3 → 2.25.5
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 +14 -31
- package/dashboard/dist/index.html +18 -16
- package/dist/cli/backups.js +194 -0
- package/dist/cli/common.js +2 -2
- package/dist/cli/dashboard.js +125 -98
- package/dist/cli/doctor-fix.js +42 -45
- package/dist/cli/doctor.js +70 -8
- package/dist/cli/hosts.js +4 -4
- package/dist/cli/install.js +106 -38
- package/dist/cli/manifest.js +45 -16
- package/dist/cli/profile.js +1 -1
- package/dist/cli/uninstall.js +21 -5
- package/dist/cli/update.js +3 -3
- package/dist/cli/usage.js +75 -4
- package/dist/extensions/{caveman-session → caveman}/index.js +10 -3
- package/dist/extensions/{combo-toggle → combo}/index.js +3 -3
- package/dist/extensions/{tersio-commands → commands}/index.js +1 -1
- package/dist/extensions/rtk/filter.js +87 -0
- package/dist/extensions/{rtk-session/index.js → rtk/session.js} +4 -3
- package/dist/extensions/{opencode/server.js → server.js} +52 -8
- package/dist/extensions/shared/agent-stats.js +1262 -0
- package/dist/extensions/shared/brand-marks.js +154 -0
- package/dist/extensions/shared/host.js +18 -12
- package/dist/extensions/shared/plugin-settings.js +1 -1
- package/dist/extensions/shared/pricing.js +91 -27
- package/dist/extensions/shared/session-state.js +3 -1
- package/dist/extensions/shared/usage-ledger.js +79 -17
- package/dist/extensions/shared/usage-store.js +106 -22
- package/dist/extensions/{lib → shared}/utils.js +1 -1
- package/dist/extensions/{ai-addons-updater → updater}/index.js +15 -5
- package/dist/probe-catalog.js +48 -0
- package/dist/probe-catalog188.js +44 -0
- package/dist/tersio.js +2 -2
- package/extensions/{caveman-session → caveman}/index.ts +10 -3
- package/extensions/{combo-toggle → combo}/index.ts +3 -3
- package/extensions/{tersio-commands → commands}/index.ts +1 -1
- package/extensions/rtk/filter.ts +90 -0
- package/extensions/{rtk-session/index.ts → rtk/session.ts} +4 -3
- package/extensions/{opencode/server.ts → server.ts} +46 -9
- package/extensions/shared/agent-stats.ts +1657 -0
- package/extensions/shared/brand-marks.ts +163 -0
- package/extensions/shared/host.ts +26 -13
- package/extensions/shared/plugin-settings.ts +1 -1
- package/extensions/shared/pricing.ts +107 -28
- package/extensions/shared/session-state.ts +3 -1
- package/extensions/shared/types.ts +10 -2
- package/extensions/shared/usage-ledger.ts +94 -19
- package/extensions/shared/usage-store.ts +106 -22
- package/extensions/{lib → shared}/utils.ts +1 -1
- package/extensions/{ai-addons-updater → updater}/index.ts +17 -7
- package/package.json +13 -12
- package/dist/cli/headroom-install.js +0 -266
- package/dist/cli/headroom.js +0 -492
- package/dist/extensions/headroom-session/index.js +0 -93
- package/dist/extensions/shared/headroom.js +0 -131
- /package/dist/extensions/{caveman-session → caveman}/rule-megacave.md +0 -0
- /package/dist/extensions/{caveman-session → caveman}/rule-ultra.md +0 -0
- /package/dist/extensions/{caveman-session → caveman}/rule.md +0 -0
- /package/extensions/{caveman-session → caveman}/rule-megacave.md +0 -0
- /package/extensions/{caveman-session → caveman}/rule-ultra.md +0 -0
- /package/extensions/{caveman-session → caveman}/rule.md +0 -0
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
// The Headroom bundle: Tersio installs, updates, and removes the add-on so the user never runs a Python tool by hand.
|
|
2
|
-
// A Tersio-owned venv at ~/.tersio/headroom stays inside our home, so uninstall is one directory removal.
|
|
3
|
-
import { existsSync } from 'node:fs';
|
|
4
|
-
import { promises as fs } from 'node:fs';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import { execP } from './common.js';
|
|
7
|
-
import { sayTagged } from './interactive.js';
|
|
8
|
-
import { findFile, headroomBundleDir, headroomBundledBinary, httpsDownload, httpsGet, parseChecksum, sha256File, } from '../extensions/lib/utils.js';
|
|
9
|
-
import { HEADROOM_TESTED_VERSION, parseHeadroomVersion } from '../extensions/shared/headroom.js';
|
|
10
|
-
/** The extra that carries the proxy. No `[ml]`: torch plus model weights are opt-in, later. */
|
|
11
|
-
const HEADROOM_SPEC = 'headroom-ai[proxy]';
|
|
12
|
-
/** uv resolves and, when needed, downloads a managed CPython for this request. */
|
|
13
|
-
const PYTHON_REQUEST = '>=3.10';
|
|
14
|
-
const UV_RELEASE_BASE = 'https://github.com/astral-sh/uv/releases/latest/download';
|
|
15
|
-
const IS_WINDOWS = process.platform === 'win32';
|
|
16
|
-
/** CI and tests set this so an install never reaches the network. Not a user-facing option. */
|
|
17
|
-
function bundleDisabled() {
|
|
18
|
-
return process.env.TERSIO_SKIP_HEADROOM_BUNDLE === '1';
|
|
19
|
-
}
|
|
20
|
-
function skippedResult() {
|
|
21
|
-
sayTagged(' [skip] Headroom bundle skipped (TERSIO_SKIP_HEADROOM_BUNDLE=1)');
|
|
22
|
-
return { ok: true, bin: null, version: null, detail: 'skipped', skipped: true };
|
|
23
|
-
}
|
|
24
|
-
// --- layout ----------------------------------------------------------------------------
|
|
25
|
-
export function headroomVenvDir() {
|
|
26
|
-
return path.join(headroomBundleDir(), 'venv');
|
|
27
|
-
}
|
|
28
|
-
function headroomVenvPython() {
|
|
29
|
-
return IS_WINDOWS
|
|
30
|
-
? path.join(headroomVenvDir(), 'Scripts', 'python.exe')
|
|
31
|
-
: path.join(headroomVenvDir(), 'bin', 'python');
|
|
32
|
-
}
|
|
33
|
-
/** The bundled entrypoint. `resolveHeadroomBinary` prefers it, so Tersio updates what Tersio installed. */
|
|
34
|
-
export const headroomBundledBin = headroomBundledBinary;
|
|
35
|
-
function uvManagedDir() {
|
|
36
|
-
return path.join(headroomBundleDir(), 'bin');
|
|
37
|
-
}
|
|
38
|
-
function uvManagedFile() {
|
|
39
|
-
return path.join(uvManagedDir(), IS_WINDOWS ? 'uv.exe' : 'uv');
|
|
40
|
-
}
|
|
41
|
-
/** Written only when Tersio downloaded uv itself, so uninstall knows what it may delete. */
|
|
42
|
-
function uvOwnedMarker() {
|
|
43
|
-
return path.join(headroomBundleDir(), 'uv-managed');
|
|
44
|
-
}
|
|
45
|
-
export function headroomBundleInstalled() {
|
|
46
|
-
return existsSync(headroomBundledBin());
|
|
47
|
-
}
|
|
48
|
-
const UV_PLATFORM_SPECS = {
|
|
49
|
-
'win32/x64': { triple: 'x86_64-pc-windows-msvc', ext: '.zip' },
|
|
50
|
-
'linux/x64': { triple: 'x86_64-unknown-linux-gnu', ext: '.tar.gz' },
|
|
51
|
-
'linux/arm64': { triple: 'aarch64-unknown-linux-gnu', ext: '.tar.gz' },
|
|
52
|
-
'darwin/x64': { triple: 'x86_64-apple-darwin', ext: '.tar.gz' },
|
|
53
|
-
'darwin/arm64': { triple: 'aarch64-apple-darwin', ext: '.tar.gz' },
|
|
54
|
-
};
|
|
55
|
-
export function uvPlatformSpec(platform = process.platform, arch = process.arch) {
|
|
56
|
-
return UV_PLATFORM_SPECS[`${platform}/${arch}`] ?? null;
|
|
57
|
-
}
|
|
58
|
-
async function onPath(name) {
|
|
59
|
-
try {
|
|
60
|
-
if (IS_WINDOWS) {
|
|
61
|
-
const found = await execP('where', [name], { timeout: 5000 });
|
|
62
|
-
return found.stdout.split(/\r?\n/).map((l) => l.trim()).find(Boolean) ?? null;
|
|
63
|
-
}
|
|
64
|
-
const found = await execP('sh', ['-c', `command -v ${name}`], { timeout: 5000 });
|
|
65
|
-
return found.stdout.trim() || null;
|
|
66
|
-
}
|
|
67
|
-
catch {
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
/** A usable uv: the Tersio copy first (so tests and CI are deterministic), then PATH. */
|
|
72
|
-
export async function resolveUv() {
|
|
73
|
-
if (existsSync(uvManagedFile()))
|
|
74
|
-
return uvManagedFile();
|
|
75
|
-
return onPath('uv');
|
|
76
|
-
}
|
|
77
|
-
async function extractArchive(archivePath, extractDir) {
|
|
78
|
-
await fs.mkdir(extractDir, { recursive: true });
|
|
79
|
-
let attempts;
|
|
80
|
-
if (archivePath.endsWith('.zip')) {
|
|
81
|
-
attempts = IS_WINDOWS
|
|
82
|
-
? [['powershell', ['Expand-Archive', '-Path', archivePath, '-DestinationPath', extractDir, '-Force']]]
|
|
83
|
-
: [['unzip', ['-o', archivePath, '-d', extractDir]]];
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
attempts = [['tar', ['xzf', archivePath, '-C', extractDir]]];
|
|
87
|
-
}
|
|
88
|
-
for (const [cmd, args] of attempts) {
|
|
89
|
-
try {
|
|
90
|
-
await execP(cmd, args, { timeout: 60000 });
|
|
91
|
-
return true;
|
|
92
|
-
}
|
|
93
|
-
catch { /* try the next extractor */ }
|
|
94
|
-
}
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
|
-
/** Downloads uv into the Tersio home, checksum-verified, when the machine has none. */
|
|
98
|
-
async function installUv(options) {
|
|
99
|
-
const spec = uvPlatformSpec();
|
|
100
|
-
if (!spec) {
|
|
101
|
-
sayTagged(` [fail] No uv build for ${process.platform}/${process.arch}.`);
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
if (options.dryRun) {
|
|
105
|
-
sayTagged(` [dry-run] would download uv-${spec.triple}${spec.ext} into ${uvManagedDir()}`);
|
|
106
|
-
return uvManagedFile();
|
|
107
|
-
}
|
|
108
|
-
const asset = `uv-${spec.triple}${spec.ext}`;
|
|
109
|
-
// The parent must exist before mkdtemp runs inside it.
|
|
110
|
-
await fs.mkdir(headroomBundleDir(), { recursive: true });
|
|
111
|
-
const tmpDir = await fs.mkdtemp(path.join(headroomBundleDir(), 'tmp-'));
|
|
112
|
-
try {
|
|
113
|
-
const archivePath = path.join(tmpDir, asset);
|
|
114
|
-
sayTagged(` [get] ${asset}`);
|
|
115
|
-
await httpsDownload(`${UV_RELEASE_BASE}/${asset}`, archivePath, { maxRedirects: 5 });
|
|
116
|
-
// Fails closed: an unverified executable would run with the user's privileges.
|
|
117
|
-
const checksums = await httpsGet(`${UV_RELEASE_BASE}/${asset}.sha256`).catch(() => null);
|
|
118
|
-
const expected = checksums ? parseChecksum(checksums, asset) : null;
|
|
119
|
-
if (!expected) {
|
|
120
|
-
sayTagged(` [fail] No checksum published for ${asset} — refusing to install uv.`);
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
if (await sha256File(archivePath) !== expected) {
|
|
124
|
-
sayTagged(` [fail] Checksum mismatch for ${asset} — refusing to install uv.`);
|
|
125
|
-
return null;
|
|
126
|
-
}
|
|
127
|
-
const extractDir = path.join(tmpDir, 'extracted');
|
|
128
|
-
if (!await extractArchive(archivePath, extractDir)) {
|
|
129
|
-
sayTagged(` [fail] Could not extract ${asset}`);
|
|
130
|
-
return null;
|
|
131
|
-
}
|
|
132
|
-
const found = await findFile(extractDir, IS_WINDOWS ? 'uv.exe' : 'uv');
|
|
133
|
-
if (!found) {
|
|
134
|
-
sayTagged(` [fail] Could not find uv in ${asset}`);
|
|
135
|
-
return null;
|
|
136
|
-
}
|
|
137
|
-
await fs.mkdir(uvManagedDir(), { recursive: true });
|
|
138
|
-
await fs.copyFile(found, uvManagedFile());
|
|
139
|
-
await fs.chmod(uvManagedFile(), 0o755).catch(() => { });
|
|
140
|
-
await fs.writeFile(uvOwnedMarker(), `${new Date().toISOString()}\n`, 'utf8');
|
|
141
|
-
return uvManagedFile();
|
|
142
|
-
}
|
|
143
|
-
catch (e) {
|
|
144
|
-
sayTagged(` [fail] Could not install uv: ${e.message}`);
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
|
-
finally {
|
|
148
|
-
await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => { });
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
async function ensureUv(options) {
|
|
152
|
-
const existing = await resolveUv();
|
|
153
|
-
if (existing)
|
|
154
|
-
return existing;
|
|
155
|
-
if (!options.quiet)
|
|
156
|
-
sayTagged(' [info] No uv found — Tersio installs its own copy under ~/.tersio/headroom');
|
|
157
|
-
return installUv(options);
|
|
158
|
-
}
|
|
159
|
-
// --- install / update ------------------------------------------------------------------
|
|
160
|
-
async function runUv(uv, args, options) {
|
|
161
|
-
if (options.dryRun) {
|
|
162
|
-
sayTagged(` [dry-run] would run: uv ${args.join(' ')}`);
|
|
163
|
-
return true;
|
|
164
|
-
}
|
|
165
|
-
if (options.verbose || !options.quiet)
|
|
166
|
-
sayTagged(` [run] uv ${args.join(' ')}`);
|
|
167
|
-
try {
|
|
168
|
-
const execOptions = { timeout: options.timeout ?? 600000 };
|
|
169
|
-
const result = await execP(uv, args, execOptions);
|
|
170
|
-
if (options.verbose && result.stdout.trim())
|
|
171
|
-
sayTagged(` ${result.stdout.trim().slice(-400)}`);
|
|
172
|
-
return true;
|
|
173
|
-
}
|
|
174
|
-
catch (e) {
|
|
175
|
-
const err = e;
|
|
176
|
-
sayTagged(` [fail] uv ${args[0]}: ${(err.stderr || err.message || '').trim().slice(0, 300)}`);
|
|
177
|
-
return false;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
/** Builds the venv if it is missing, then installs or upgrades the package inside it. */
|
|
181
|
-
async function provision(options, upgrade) {
|
|
182
|
-
const uv = await ensureUv(options);
|
|
183
|
-
if (!uv)
|
|
184
|
-
return { ok: false, bin: null, version: null, detail: 'uv unavailable' };
|
|
185
|
-
if (!existsSync(headroomVenvPython())) {
|
|
186
|
-
// uv fetches a managed CPython when the machine has no interpreter in range.
|
|
187
|
-
if (!await runUv(uv, ['venv', headroomVenvDir(), '--python', PYTHON_REQUEST], { ...options, timeout: 900000 })) {
|
|
188
|
-
return { ok: false, bin: null, version: null, detail: 'venv creation failed' };
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
const installArgs = ['pip', 'install', '--python', headroomVenvPython()];
|
|
192
|
-
if (upgrade)
|
|
193
|
-
installArgs.push('--upgrade');
|
|
194
|
-
installArgs.push(HEADROOM_SPEC);
|
|
195
|
-
if (!await runUv(uv, installArgs, options)) {
|
|
196
|
-
return { ok: false, bin: null, version: null, detail: `could not install ${HEADROOM_SPEC}` };
|
|
197
|
-
}
|
|
198
|
-
if (options.dryRun)
|
|
199
|
-
return { ok: true, bin: headroomBundledBin(), version: null, detail: 'dry run' };
|
|
200
|
-
const version = await bundledVersion();
|
|
201
|
-
if (!existsSync(headroomBundledBin())) {
|
|
202
|
-
return { ok: false, bin: null, version, detail: 'the package installed, but no headroom entrypoint appeared' };
|
|
203
|
-
}
|
|
204
|
-
return { ok: true, bin: headroomBundledBin(), version, detail: upgrade ? 'updated' : 'installed' };
|
|
205
|
-
}
|
|
206
|
-
async function bundledVersion() {
|
|
207
|
-
try {
|
|
208
|
-
const result = await execP(headroomBundledBin(), ['--version'], { timeout: 30000 });
|
|
209
|
-
return parseHeadroomVersion(result.stdout || result.stderr);
|
|
210
|
-
}
|
|
211
|
-
catch (e) {
|
|
212
|
-
const err = e;
|
|
213
|
-
return parseHeadroomVersion(err.stdout || err.stderr);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
/** Install the bundled Headroom. Idempotent: an existing venv is reused. */
|
|
217
|
-
export async function installHeadroom(options = {}) {
|
|
218
|
-
if (bundleDisabled())
|
|
219
|
-
return skippedResult();
|
|
220
|
-
if (headroomBundleInstalled() && !options.dryRun) {
|
|
221
|
-
const version = await bundledVersion();
|
|
222
|
-
return { ok: true, bin: headroomBundledBin(), version, detail: 'already installed' };
|
|
223
|
-
}
|
|
224
|
-
return provision(options, false);
|
|
225
|
-
}
|
|
226
|
-
/** Refresh the bundled Headroom to the newest release. */
|
|
227
|
-
export async function updateHeadroom(options = {}) {
|
|
228
|
-
if (bundleDisabled())
|
|
229
|
-
return skippedResult();
|
|
230
|
-
if (!headroomBundleInstalled() && !options.dryRun && !existsSync(headroomVenvPython())) {
|
|
231
|
-
return installHeadroom(options);
|
|
232
|
-
}
|
|
233
|
-
const result = await provision(options, true);
|
|
234
|
-
if (result.ok && result.version) {
|
|
235
|
-
const beyond = parseHeadroomVersion(result.version);
|
|
236
|
-
if (beyond && beyond !== HEADROOM_TESTED_VERSION && beyond > HEADROOM_TESTED_VERSION) {
|
|
237
|
-
sayTagged(` [warn] Headroom ${beyond} is newer than the tested ${HEADROOM_TESTED_VERSION}`);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
return result;
|
|
241
|
-
}
|
|
242
|
-
// --- remove ----------------------------------------------------------------------------
|
|
243
|
-
/** Removes the venv and anything else the bundle created, including a uv copy Tersio downloaded. */
|
|
244
|
-
export async function removeHeadroomBundle(options = {}) {
|
|
245
|
-
const root = headroomBundleDir();
|
|
246
|
-
if (!existsSync(root))
|
|
247
|
-
return true;
|
|
248
|
-
// Removal still happens under the skip flag: an uninstall must not leave the bundle behind.
|
|
249
|
-
if (options.dryRun) {
|
|
250
|
-
sayTagged(` [dry-run] would remove ${root}`);
|
|
251
|
-
return true;
|
|
252
|
-
}
|
|
253
|
-
try {
|
|
254
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
255
|
-
sayTagged(` [rm] ${root}`);
|
|
256
|
-
return true;
|
|
257
|
-
}
|
|
258
|
-
catch (e) {
|
|
259
|
-
sayTagged(` [fail] could not remove ${root}: ${e.message}`);
|
|
260
|
-
return false;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
/** True when the bundle holds a uv copy Tersio downloaded (as opposed to one the user had). */
|
|
264
|
-
export function headroomOwnsUv() {
|
|
265
|
-
return existsSync(uvOwnedMarker());
|
|
266
|
-
}
|