@krtclcdy/tersio 2.25.4 → 2.25.6

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.
Files changed (77) hide show
  1. package/README.md +27 -63
  2. package/dashboard/dist/index.html +18 -16
  3. package/dist/cli/backups.js +187 -0
  4. package/dist/cli/banner.js +4 -3
  5. package/dist/cli/common.js +29 -13
  6. package/dist/cli/currency.js +1 -1
  7. package/dist/cli/dashboard.js +168 -110
  8. package/dist/cli/doctor-fix.js +232 -50
  9. package/dist/cli/doctor.js +281 -32
  10. package/dist/cli/hosts.js +29 -9
  11. package/dist/cli/install.js +204 -69
  12. package/dist/cli/interactive.js +3 -3
  13. package/dist/cli/manifest.js +125 -17
  14. package/dist/cli/profile.js +14 -9
  15. package/dist/cli/rtk-wiring.js +1 -1
  16. package/dist/cli/settings.js +20 -5
  17. package/dist/cli/uninstall.js +24 -8
  18. package/dist/cli/update.js +9 -10
  19. package/dist/cli/usage.js +75 -7
  20. package/dist/extensions/{caveman-session → caveman}/index.js +4 -2
  21. package/{extensions/caveman-session → dist/extensions/caveman}/rule-megacave.md +5 -3
  22. package/{extensions/caveman-session → dist/extensions/caveman}/rule-ultra.md +4 -4
  23. package/dist/extensions/caveman/rule.md +89 -0
  24. package/dist/extensions/{combo-toggle → combo}/index.js +62 -21
  25. package/dist/extensions/{tersio-commands → commands}/index.js +2 -2
  26. package/dist/extensions/pstack/index.js +119 -0
  27. package/dist/extensions/pstack/rule.md +65 -0
  28. package/dist/extensions/{rtk-filter/index.js → rtk/filter.js} +1 -2
  29. package/dist/extensions/{rtk-session/index.js → rtk/session.js} +5 -5
  30. package/dist/extensions/{opencode/server.js → server.js} +98 -14
  31. package/dist/extensions/shared/agent-stats.js +1198 -0
  32. package/dist/extensions/shared/brand-marks.js +153 -0
  33. package/dist/extensions/shared/carbon.js +1 -1
  34. package/dist/extensions/shared/plugin-settings.js +12 -7
  35. package/dist/extensions/shared/pricing.js +86 -30
  36. package/dist/extensions/shared/rtk-gain.js +4 -9
  37. package/dist/extensions/shared/session-state.js +15 -10
  38. package/dist/extensions/shared/sqlite.js +168 -0
  39. package/dist/extensions/shared/status.js +8 -8
  40. package/dist/extensions/shared/types.js +1 -1
  41. package/dist/extensions/shared/usage-ledger.js +113 -39
  42. package/dist/extensions/shared/usage-store.js +191 -48
  43. package/dist/extensions/{lib → shared}/utils.js +6 -6
  44. package/dist/extensions/timing/index.js +31 -0
  45. package/dist/extensions/{ai-addons-updater → updater}/index.js +13 -6
  46. package/dist/probe-catalog.js +48 -0
  47. package/dist/probe-catalog188.js +44 -0
  48. package/dist/tersio.js +9 -4
  49. package/extensions/{caveman-session → caveman}/index.ts +4 -2
  50. package/{dist/extensions/caveman-session → extensions/caveman}/rule-megacave.md +5 -3
  51. package/{dist/extensions/caveman-session → extensions/caveman}/rule-ultra.md +4 -4
  52. package/extensions/caveman/rule.md +89 -0
  53. package/extensions/{combo-toggle → combo}/index.ts +62 -19
  54. package/extensions/{tersio-commands → commands}/index.ts +2 -2
  55. package/extensions/pstack/index.ts +128 -0
  56. package/extensions/pstack/rule.md +65 -0
  57. package/extensions/{rtk-filter/index.ts → rtk/filter.ts} +1 -2
  58. package/extensions/{rtk-session/index.ts → rtk/session.ts} +5 -5
  59. package/extensions/{opencode/server.ts → server.ts} +81 -16
  60. package/extensions/shared/agent-stats.ts +1605 -0
  61. package/extensions/shared/brand-marks.ts +163 -0
  62. package/extensions/shared/carbon.ts +1 -1
  63. package/extensions/shared/plugin-settings.ts +13 -7
  64. package/extensions/shared/pricing.ts +95 -32
  65. package/extensions/shared/rtk-gain.ts +4 -9
  66. package/extensions/shared/session-state.ts +18 -13
  67. package/extensions/shared/sqlite.ts +168 -0
  68. package/extensions/shared/status.ts +9 -8
  69. package/extensions/shared/types.ts +2 -1
  70. package/extensions/shared/usage-ledger.ts +134 -46
  71. package/extensions/shared/usage-store.ts +202 -50
  72. package/extensions/{lib → shared}/utils.ts +6 -6
  73. package/extensions/timing/index.ts +45 -0
  74. package/extensions/{ai-addons-updater → updater}/index.ts +15 -9
  75. package/package.json +27 -18
  76. package/dist/extensions/caveman-session/rule.md +0 -79
  77. package/extensions/caveman-session/rule.md +0 -79
@@ -0,0 +1,187 @@
1
+ // Data snapshots: a manifest directory, not one file; older lone `usage-<stamp>.db` installs still restore.
2
+ import { promises as fs } from 'node:fs';
3
+ import { readdirSync, readFileSync, statSync } from 'node:fs';
4
+ import path from 'node:path';
5
+ import { sqliteExec } from '../extensions/shared/sqlite.js';
6
+ import { agentDbPath, statsDbPath } from '../extensions/shared/agent-stats.js';
7
+ import { rtkDbPath } from '../extensions/shared/rtk-gain.js';
8
+ import { pricesCachePath } from '../extensions/shared/pricing.js';
9
+ import { usageDbPath } from '../extensions/shared/usage-store.js';
10
+ import { resetMarkerPath } from '../extensions/shared/usage-ledger.js';
11
+ import { tersioHome } from '../extensions/shared/utils.js';
12
+ /** Snapshots kept on disk, oldest pruned first; six fills past one screen so the pane scrolls. */
13
+ export const BACKUP_KEEP = 6;
14
+ export function backupDir() {
15
+ return path.join(tersioHome(), 'backups');
16
+ }
17
+ function manifestPath(dir) {
18
+ return path.join(dir, 'manifest.json');
19
+ }
20
+ /** Every file this build knows how to snapshot, whether or not it exists yet. */
21
+ export function dataSources() {
22
+ const sources = [
23
+ ['Usage ledger', usageDbPath()],
24
+ ['Reset watermark', resetMarkerPath()],
25
+ ['Settings', path.join(tersioHome(), 'settings.json')],
26
+ ['Price cache', pricesCachePath()],
27
+ ['Model price cache', path.join(tersioHome(), 'prices-modelsdev.json')],
28
+ ['Agent stats', statsDbPath()],
29
+ ['Agent quota windows', agentDbPath()],
30
+ ['Command history', rtkDbPath()],
31
+ ];
32
+ return sources.map(([label, source]) => ({ label, source, bytes: sizeOf(source) }));
33
+ }
34
+ /** Byte size of a file, zero when it is absent or unreadable. */
35
+ function sizeOf(file) {
36
+ try {
37
+ return statSync(file).size;
38
+ }
39
+ catch {
40
+ return 0;
41
+ }
42
+ }
43
+ /** Snapshot label for a legacy single-file backup, so the list reads the same for both. */
44
+ function legacyLabel(name) {
45
+ return name.startsWith('usage-') ? 'Usage ledger' : 'Snapshot';
46
+ }
47
+ /** Newest first: the directories this build wrote, then the lone `usage-*.db` files. */
48
+ export function listDataBackups() {
49
+ let names = [];
50
+ try {
51
+ names = readdirSync(backupDir());
52
+ }
53
+ catch {
54
+ return [];
55
+ }
56
+ const dirs = [];
57
+ const legacy = [];
58
+ for (const name of names) {
59
+ const full = path.join(backupDir(), name);
60
+ let stat;
61
+ try {
62
+ stat = statSync(full);
63
+ }
64
+ catch {
65
+ continue;
66
+ }
67
+ if (stat.isDirectory()) {
68
+ const files = readManifest(full);
69
+ if (files.length === 0)
70
+ continue;
71
+ dirs.push({ name, path: full, mtime: stat.mtimeMs, bytes: totalOf(full), files, legacy: false });
72
+ continue;
73
+ }
74
+ if (/^usage-\d{14}\.db$/.test(name)) {
75
+ legacy.push({
76
+ name,
77
+ path: full,
78
+ mtime: stat.mtimeMs,
79
+ bytes: stat.size,
80
+ files: [{ label: legacyLabel(name), source: usageDbPath(), bytes: stat.size }],
81
+ legacy: true,
82
+ });
83
+ }
84
+ }
85
+ return [...dirs, ...legacy].toSorted((a, b) => b.mtime - a.mtime);
86
+ }
87
+ function readManifest(dir) {
88
+ try {
89
+ const raw = JSON.parse(readFileSync(manifestPath(dir), 'utf8'));
90
+ if (!Array.isArray(raw))
91
+ return [];
92
+ return raw
93
+ .map((entry) => {
94
+ const record = (entry ?? {});
95
+ const source = String(record.source ?? '');
96
+ const name = path.basename(source);
97
+ const file = path.join(dir, name);
98
+ const bytes = sizeOf(file);
99
+ if (!source || bytes === 0)
100
+ return null;
101
+ return { label: String(record.label ?? name), source, bytes };
102
+ })
103
+ .filter((entry) => entry !== null);
104
+ }
105
+ catch {
106
+ return [];
107
+ }
108
+ }
109
+ /** Disk size of a snapshot, counting the manifest. */
110
+ function totalOf(dir) {
111
+ return sizeOf(manifestPath(dir)) + readdirSync(dir).reduce((sum, name) => sum + sizeOf(path.join(dir, name)), 0);
112
+ }
113
+ /** Put the sqlite ledger's write-ahead log back in the main file, so a copy is one whole database. */
114
+ function checkpointLedger() {
115
+ try {
116
+ sqliteExec(usageDbPath(), 'PRAGMA wal_checkpoint(TRUNCATE);');
117
+ }
118
+ catch {
119
+ /* no SQLite on this host, or no ledger: the plain file copy is the best available */
120
+ }
121
+ }
122
+ /** Take a snapshot: one copy of every existing file under a stamp dir, keeping the newest {@link BACKUP_KEEP}. */
123
+ export async function createDataBackup() {
124
+ // The stamp keeps milliseconds: two snapshots in the same second must not land in one directory.
125
+ const stamp = new Date().toISOString().replace(/[-:TZ.]/g, '');
126
+ const dir = path.join(backupDir(), `data-${stamp}`);
127
+ checkpointLedger();
128
+ await fs.mkdir(dir, { recursive: true });
129
+ const files = [];
130
+ for (const source of dataSources()) {
131
+ if (source.bytes === 0)
132
+ continue;
133
+ const target = path.join(dir, path.basename(source.source));
134
+ await fs.copyFile(source.source, target);
135
+ files.push({ ...source, bytes: sizeOf(target) });
136
+ }
137
+ await fs.writeFile(manifestPath(dir), JSON.stringify(files.map(({ label, source }) => ({ label, source }))), 'utf8');
138
+ await pruneBackups();
139
+ return { name: path.basename(dir), path: dir, mtime: Date.now(), bytes: files.reduce((sum, f) => sum + f.bytes, 0), files, legacy: false };
140
+ }
141
+ /** Keep the newest {@link BACKUP_KEEP} snapshots and drop the rest, legacy files included. */
142
+ export async function pruneBackups() {
143
+ const all = listDataBackups();
144
+ for (const old of all.slice(BACKUP_KEEP)) {
145
+ await fs.rm(old.path, { recursive: true, force: true }).catch(() => { });
146
+ }
147
+ }
148
+ /** A snapshot name resolves to a path inside the backup directory, or null when it does not. */
149
+ function resolveBackup(name) {
150
+ if (!name || path.basename(name) !== name || /[\\/]/.test(name))
151
+ return null;
152
+ const found = listDataBackups().find((entry) => entry.name === name);
153
+ return found ?? null;
154
+ }
155
+ /** Restore: files the snapshot holds overwrite their live source; files it lacks stay put, so nothing is lost. */
156
+ export async function restoreDataBackup(name) {
157
+ const found = resolveBackup(name);
158
+ if (!found)
159
+ return false;
160
+ if (found.legacy) {
161
+ await fs.mkdir(path.dirname(usageDbPath()), { recursive: true });
162
+ await fs.copyFile(found.path, usageDbPath());
163
+ return true;
164
+ }
165
+ for (const file of found.files) {
166
+ const from = path.join(found.path, path.basename(file.source));
167
+ await fs.mkdir(path.dirname(file.source), { recursive: true });
168
+ await fs.copyFile(from, file.source);
169
+ }
170
+ return true;
171
+ }
172
+ /** Remove one snapshot. Only ever touches a path inside the backup directory. */
173
+ export async function deleteDataBackup(name) {
174
+ const found = resolveBackup(name);
175
+ if (!found)
176
+ return false;
177
+ await fs.rm(found.path, { recursive: true, force: true });
178
+ return true;
179
+ }
180
+ /** Total size of every snapshot on disk, for the pane's footer. */
181
+ export function backupDiskBytes() {
182
+ return listDataBackups().reduce((sum, entry) => sum + entry.bytes, 0);
183
+ }
184
+ /** The home directory the snapshots live under, for the pane's path line. */
185
+ export function backupHome() {
186
+ return tersioHome();
187
+ }
@@ -1,4 +1,4 @@
1
- // cli/banner.ts — mini pixel-scissor welcome. TTY-only and skipped on --dry-run, so piped output stays byte-identical.
1
+ // cli/banner.ts — mini pixel-scissor welcome. TTY-only, skipped on --dry-run; piped output stays byte-identical.
2
2
  import { intro as clackIntro, note as clackNote } from '@clack/prompts';
3
3
  import { PACKAGE_VERSION, dryRun } from './common.js';
4
4
  function tty() {
@@ -81,9 +81,10 @@ function printWelcome() {
81
81
  'tersio update refresh CLI + add-ons',
82
82
  'tersio doctor verify the install',
83
83
  ];
84
- // Below 26 rows the note scrolls off the top and slices the art.
84
+ // Below 26 rows the note scrolls off the top and slices the art, and the screen must also hold the
85
+ // update prompt and every option, so the tips take one line there.
85
86
  if (process.stdout.rows < 26) {
86
- clackNote(tips.map((t) => ` ${t}`).join('\n'));
87
+ clackNote(` ${tips.join(' · ')}`);
87
88
  return;
88
89
  }
89
90
  const art = bannerLines();
@@ -5,7 +5,7 @@ import path from 'node:path';
5
5
  import { promisify } from 'node:util';
6
6
  import { createRequire } from 'node:module';
7
7
  import { fileURLToPath } from 'node:url';
8
- import { findPackageRoot, readTextIfExists } from '../extensions/lib/utils.js';
8
+ import { findPackageRoot, readTextIfExists } from '../extensions/shared/utils.js';
9
9
  import { parseCurrencyFlag, readStoredCurrency } from './currency.js';
10
10
  const IS_WINDOWS = process.platform === 'win32';
11
11
  const RTK_BINARY_NAME = IS_WINDOWS ? 'rtk.exe' : 'rtk';
@@ -19,14 +19,15 @@ const PACKAGE_BIN = 'tersio';
19
19
  const { version: PACKAGE_VERSION } = createRequire(import.meta.url)(path.join(findPackageRoot(path.dirname(fileURLToPath(import.meta.url))), 'package.json'));
20
20
  const CAVEMAN_DEFAULTS = new Set(['off', 'lite', 'full', 'ultra', 'megacave-lite', 'megacave-full', 'megacave-ultra']);
21
21
  const PONYTAIL_DEFAULTS = new Set(['off', 'lite', 'full', 'ultra']);
22
+ const PSTACK_DEFAULTS = new Set(['off', 'on']);
22
23
  const RTK_DEFAULTS = new Set(['on', 'off']);
23
24
  const DIAG_SCHEDULES = new Set(['manual', 'daily', 'weekly', 'monthly']);
24
- // Mirrors COMBO_LEVELS in extensions/shared/session-state.ts (rtk as a boolean here), so the combo preset implies all three modes.
25
+ // Mirrors COMBO_LEVELS in extensions/shared/session-state.ts, so the combo preset implies all four modes.
25
26
  const COMBO_PRESET_MODES = {
26
- off: { caveman: 'off', rtk: false, ponytail: 'off' },
27
- medium: { caveman: 'lite', rtk: true, ponytail: 'lite' },
28
- balanced: { caveman: 'full', rtk: true, ponytail: 'full' },
29
- max: { caveman: 'ultra', rtk: true, ponytail: 'ultra' },
27
+ off: { caveman: 'off', rtk: false, ponytail: 'off', pstack: 'off' },
28
+ medium: { caveman: 'lite', rtk: true, ponytail: 'lite', pstack: 'on' },
29
+ balanced: { caveman: 'full', rtk: true, ponytail: 'full', pstack: 'on' },
30
+ max: { caveman: 'ultra', rtk: true, ponytail: 'ultra', pstack: 'on' },
30
31
  };
31
32
  const COMBO_DEFAULTS = new Set(Object.keys(COMBO_PRESET_MODES));
32
33
  function parseEnum(value, valid, flag) {
@@ -46,12 +47,11 @@ function flagValue(name) {
46
47
  const prefix = `${name}=`;
47
48
  return args.find((arg) => arg.startsWith(prefix))?.slice(prefix.length);
48
49
  }
49
- // --- CLI flags ---
50
- const COMMANDS = { install: true, update: true, doctor: true, uninstall: true, usage: true, dashboard: true, reset: true, settings: true, version: true, help: true };
50
+ const COMMANDS = { install: true, reinstall: true, update: true, doctor: true, uninstall: true, usage: true, dashboard: true, reset: true, settings: true, version: true, help: true };
51
51
  const args = process.argv.slice(2);
52
52
  const commandArg = args.find((arg) => !arg.startsWith('-'));
53
53
  const command = commandArg?.toLowerCase() || null;
54
- // `tersio settings diagnosis` jumps to one prompt instead of walking the chain. Index-based, so flag values never count as positionals.
54
+ // `tersio settings diagnosis` jumps to one prompt. Index-based, so flag values never count as positionals.
55
55
  const settingArg = (() => {
56
56
  const at = args.indexOf(commandArg ?? '');
57
57
  const next = at >= 0 ? args[at + 1] : undefined;
@@ -61,6 +61,7 @@ const settingArg = (() => {
61
61
  })();
62
62
  const unknownCommand = command !== null && !COMMANDS[command];
63
63
  const install = command === 'install';
64
+ const reinstall = command === 'reinstall';
64
65
  const update = command === 'update';
65
66
  const showVersion = command === 'version' || args.includes('--version') || args.includes('-v');
66
67
  const showHelp = command === 'help' || args.includes('--help') || args.includes('-h');
@@ -96,9 +97,10 @@ if (legacyScope !== null && legacyScope !== 'user') {
96
97
  const comboDefaultFlag = parseEnum(flagValue('--combo-default'), COMBO_DEFAULTS, '--combo-default');
97
98
  const cavemanDefaultFlag = parseEnum(flagValue('--caveman-default'), CAVEMAN_DEFAULTS, '--caveman-default');
98
99
  const ponytailDefaultFlag = parseEnum(flagValue('--ponytail-default'), PONYTAIL_DEFAULTS, '--ponytail-default');
100
+ const pstackDefaultFlag = parseEnum(flagValue('--pstack-default'), PSTACK_DEFAULTS, '--pstack-default');
99
101
  const rtkDefaultFlag = parseEnum(flagValue('--rtk-default'), RTK_DEFAULTS, '--rtk-default');
100
102
  const diagScheduleFlag = parseEnum(flagValue('--diag-schedule'), DIAG_SCHEDULES, '--diag-schedule');
101
- const profileFlagsGiven = [comboDefaultFlag, cavemanDefaultFlag, rtkDefaultFlag, ponytailDefaultFlag]
103
+ const profileFlagsGiven = [comboDefaultFlag, cavemanDefaultFlag, rtkDefaultFlag, ponytailDefaultFlag, pstackDefaultFlag]
102
104
  .some((flag) => flag !== undefined);
103
105
  // Repeatable `--subagent-marker`; overrides the built-in host prompt marker.
104
106
  const subagentMarkerFlags = args
@@ -138,7 +140,7 @@ async function writeIfChanged(dest, content, options = {}) {
138
140
  console.log(` [write] ${dest}`);
139
141
  return true;
140
142
  }
141
- // omp ships 'extensions: null', and list items under a null scalar break YAML: normalize null/~/[]/empty to a mapping key first.
143
+ // omp ships 'extensions: null'; normalize it to a mapping key first, or YAML rejects the list items.
142
144
  const EXTENSIONS_KEY_RE = /^\s*extensions\s*:/i;
143
145
  const EXTENSIONS_NULL_RE = /^\s*extensions\s*:\s*(?:\[\s*\]|null|~)?\s*$/i;
144
146
  function normalizeExtensionsKey(lines) {
@@ -160,7 +162,7 @@ async function ensureExtensionInConfig(configPath, extensionPath, label, options
160
162
  const line = ` - ${normalizedPath}`;
161
163
  const raw = await readTextIfExists(configPath);
162
164
  let lines = (raw || '').split('\n');
163
- // Drop legacy compiled twins (same dir/name, .js): OMP must never load both copies. Matched on the trailing path so relative entries are caught too.
165
+ // Drop legacy compiled twins (.js beside the name): OMP must never load both. Matched on the trailing path.
164
166
  if (normalizedPath.endsWith('.ts')) {
165
167
  const legacyTail = `${normalizedPath.slice(0, -3)}.js`.split('/').slice(-2).join('/');
166
168
  lines = lines.filter((l) => !l.trim().replace(/^\.\//, '').endsWith(legacyTail));
@@ -239,6 +241,12 @@ function readPonytailConfig() {
239
241
  : path.join(HOME, '.config', 'ponytail');
240
242
  return { dir, path: path.join(dir, 'config.json') };
241
243
  }
244
+ /** This package's ponytail range, or null; repair pins it, since npm installs the published range. */
245
+ async function bundledPonytailSpec() {
246
+ const root = findPackageRoot(path.dirname(fileURLToPath(import.meta.url)));
247
+ const text = await readTextIfExists(path.join(root, 'package.json'));
248
+ return parseJsonObject(text)?.dependencies?.['@dietrichgebert/ponytail'] ?? null;
249
+ }
242
250
  function parseJsonObject(text) {
243
251
  if (!text)
244
252
  return null;
@@ -280,6 +288,14 @@ async function ensurePonytailConfigValue(key, value, options = {}) {
280
288
  return true;
281
289
  }, `would set Ponytail ${key}=${value}`, `Set Ponytail ${key}=${value}`, options);
282
290
  }
291
+ // npm prefers a nested copy over the pin when the plugin's range excludes it; only `overrides` reaches it.
292
+ function ponytailOverride(pkg) {
293
+ const overrides = pkg.overrides;
294
+ if (typeof overrides !== 'object' || overrides === null)
295
+ return null;
296
+ const value = overrides['@dietrichgebert/ponytail'];
297
+ return typeof value === 'string' ? value : null;
298
+ }
283
299
  // Read plugins/package.json tolerantly; a corrupt or missing file starts fresh with installer-managed defaults.
284
300
  async function readPluginsPackage(pkgPath) {
285
301
  const pkg = parseJsonObject(await readTextIfExists(pkgPath)) ?? {};
@@ -300,4 +316,4 @@ function relTime(ageMs) {
300
316
  const weeks = Math.floor(days / 7);
301
317
  return `${weeks} week${weeks === 1 ? '' : 's'} ago`;
302
318
  }
303
- export { IS_WINDOWS, RTK_BINARY_NAME, HOME, OMP_AGENT_DIR, OMP_PLUGINS_DIR, BUN_BIN_DIR, OMP_BIN, PACKAGE_NAME, PACKAGE_BIN, PACKAGE_VERSION, CAVEMAN_DEFAULTS, PONYTAIL_DEFAULTS, RTK_DEFAULTS, DIAG_SCHEDULES, COMBO_PRESET_MODES, COMBO_DEFAULTS, parseEnum, flagValue, COMMANDS, commandArg, command, settingArg, unknownCommand, install, update, showVersion, showHelp, applyUpdate, args, dryRun, yes, verbose, doctor, fix, uninstall, usage, dashboard, reset, settings, allowUnverified, dashboardPort, dashboardOpen, dashboardExport, currency, currencyGiven, removePonytail, keepPonytail, removeRtk, comboDefaultFlag, cavemanDefaultFlag, ponytailDefaultFlag, rtkDefaultFlag, diagScheduleFlag, profileFlagsGiven, subagentMarkerFlags, subagentMarkersGiven, debug, execFileP, execP, writeIfChanged, normalizeExtensionsKey, EXTENSIONS_KEY_RE, writeConfigLines, ensureExtensionInConfig, removeExtensionFromConfig, readPonytailConfig, parseJsonObject, parsePonytailConfig, patchPonytailConfig, ensurePonytailConfigValue, readPluginsPackage, relTime, };
319
+ export { IS_WINDOWS, RTK_BINARY_NAME, HOME, OMP_AGENT_DIR, OMP_PLUGINS_DIR, BUN_BIN_DIR, OMP_BIN, PACKAGE_NAME, PACKAGE_BIN, PACKAGE_VERSION, CAVEMAN_DEFAULTS, PONYTAIL_DEFAULTS, PSTACK_DEFAULTS, RTK_DEFAULTS, DIAG_SCHEDULES, COMBO_PRESET_MODES, COMBO_DEFAULTS, parseEnum, flagValue, COMMANDS, commandArg, command, settingArg, unknownCommand, install, reinstall, update, showVersion, showHelp, applyUpdate, args, dryRun, yes, verbose, doctor, fix, uninstall, usage, dashboard, reset, settings, allowUnverified, dashboardPort, dashboardOpen, dashboardExport, currency, currencyGiven, removePonytail, keepPonytail, removeRtk, comboDefaultFlag, cavemanDefaultFlag, ponytailDefaultFlag, pstackDefaultFlag, rtkDefaultFlag, diagScheduleFlag, profileFlagsGiven, subagentMarkerFlags, subagentMarkersGiven, debug, execFileP, execP, writeIfChanged, normalizeExtensionsKey, writeConfigLines, ensureExtensionInConfig, removeExtensionFromConfig, readPonytailConfig, parseJsonObject, parsePonytailConfig, patchPonytailConfig, ensurePonytailConfigValue, readPluginsPackage, relTime, bundledPonytailSpec, ponytailOverride, };
@@ -1,4 +1,4 @@
1
- // cli/currency.ts — USD-base currency helpers on offline snapshot rates. Same 10 currencies as the dashboard, which prefers live frankfurter figures.
1
+ // cli/currency.ts — USD-base helpers on offline snapshot rates. Same 10 currencies as the dashboard's live figures.
2
2
  import { existsSync, readFileSync } from 'node:fs';
3
3
  import { tersioSettingsFile } from '../extensions/shared/plugin-settings.js';
4
4
  const CURRENCY_CODES = [