@krtclcdy/tersio 2.23.0 → 2.24.1
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 +56 -45
- package/cli/agent-hosts.js +42 -0
- package/cli/agents.js +266 -0
- package/cli/banner.js +2 -3
- package/cli/common.js +16 -20
- package/cli/currency.js +11 -19
- package/cli/dashboard.js +216 -40
- package/cli/doctor-fix.js +40 -26
- package/cli/doctor.js +41 -41
- package/cli/hosts.js +114 -0
- package/cli/install.js +284 -179
- package/cli/interactive.js +62 -2
- package/cli/layer.js +22 -0
- package/cli/manifest.js +33 -0
- package/cli/omp-layer.js +51 -0
- package/cli/pi-layer.js +37 -0
- package/cli/pi-wiring.js +92 -0
- package/cli/profile.js +48 -48
- package/cli/reset.js +5 -32
- package/cli/rtk-wiring.js +7 -17
- package/cli/settings.js +3 -8
- package/cli/uninstall.js +162 -52
- package/cli/update.js +19 -27
- package/cli/usage.js +15 -5
- package/dashboard/dist/index.html +18 -18
- package/extensions/ai-addons-updater/index.js +37 -29
- package/extensions/ai-addons-updater/index.ts +39 -38
- package/extensions/caveman-session/index.js +19 -34
- package/extensions/caveman-session/index.ts +20 -35
- package/extensions/combo-toggle/index.js +47 -49
- package/extensions/combo-toggle/index.ts +42 -58
- package/extensions/lib/utils.js +40 -9
- package/extensions/lib/utils.ts +40 -9
- package/extensions/rtk-session/index.js +34 -38
- package/extensions/rtk-session/index.ts +32 -41
- package/extensions/shared/carbon.js +3 -11
- package/extensions/shared/carbon.ts +4 -11
- package/extensions/shared/host.js +58 -0
- package/extensions/shared/host.ts +94 -0
- package/extensions/shared/omp-prompt.js +12 -0
- package/extensions/shared/plugin-settings.js +33 -30
- package/extensions/shared/plugin-settings.ts +35 -26
- package/extensions/shared/pricing.js +25 -16
- package/extensions/shared/pricing.ts +27 -17
- package/extensions/shared/rtk-gain.js +6 -22
- package/extensions/shared/rtk-gain.ts +6 -22
- package/extensions/shared/session-state.js +44 -36
- package/extensions/shared/session-state.ts +48 -35
- package/extensions/shared/types.ts +10 -3
- package/extensions/shared/usage-ledger.js +160 -51
- package/extensions/shared/usage-ledger.ts +170 -67
- package/extensions/shared/usage-store.js +178 -24
- package/extensions/shared/usage-store.ts +176 -25
- package/extensions/tersio-commands/index.js +3 -6
- package/extensions/tersio-commands/index.ts +3 -6
- package/package.json +6 -5
- package/tersio.js +4 -6
- package/extensions/shared/mode-reinforcement.js +0 -26
package/cli/install.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
// cli/install.ts — install
|
|
1
|
+
// cli/install.ts — install flow and all setup steps.
|
|
2
2
|
import { promises as fs } from 'node:fs';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import { BUN_BIN_DIR, COMBO_PRESET_MODES, IS_WINDOWS, OMP_AGENT_DIR, OMP_PLUGINS_DIR, OMP_BIN, PACKAGE_NAME, PACKAGE_VERSION, RTK_BINARY_NAME, applyUpdate, cavemanDefaultFlag, comboDefaultFlag, command, dryRun,
|
|
7
|
-
import { askInteractiveChoice, askInteractiveConfirm, closeRL, execNetwork, tty, withInteractiveSpinner, } from './interactive.js';
|
|
6
|
+
import { BUN_BIN_DIR, COMBO_PRESET_MODES, IS_WINDOWS, OMP_AGENT_DIR, OMP_PLUGINS_DIR, OMP_BIN, PACKAGE_NAME, PACKAGE_VERSION, RTK_BINARY_NAME, args, applyUpdate, cavemanDefaultFlag, comboDefaultFlag, command, dryRun, ponytailDefaultFlag, profileFlagsGiven, rtkDefaultFlag, verbose, yes, dashboardExport, dashboardPort, debug, ensurePonytailConfigValue, execP, parseJsonObject, readPluginsPackage, writeIfChanged, } from './common.js';
|
|
7
|
+
import { askInteractiveChoice, askInteractiveConfirm, closeRL, execNetwork, tty, withInteractiveSpinner, withInteractiveTask, sayTagged } from './interactive.js';
|
|
8
8
|
import { printWelcome } from './banner.js';
|
|
9
9
|
import { checkForUpdate, runLatestUpdate } from './update.js';
|
|
10
10
|
import { runUninstall } from './uninstall.js';
|
|
@@ -13,31 +13,19 @@ import { runReset } from './reset.js';
|
|
|
13
13
|
import { runUsage } from './usage.js';
|
|
14
14
|
import { runDashboard } from './dashboard.js';
|
|
15
15
|
import { wireRtkOmp } from './rtk-wiring.js';
|
|
16
|
-
import { CAVEMAN_REMOTE_RULE, RTK_RELEASE_API, fetchJson, findFile, httpsGet, httpsDownload, parseChecksum, readTextIfExists, rtkPlatformSpec, sha256File, } from '../extensions/lib/utils.js';
|
|
16
|
+
import { CAVEMAN_REMOTE_RULE, RTK_RELEASE_API, fetchJson, findFile, findHoistedPackage, httpsGet, httpsDownload, parseChecksum, piAgentDir, readTextIfExists, resolveRtkBinary, rtkPlatformSpec, sha256File, } from '../extensions/lib/utils.js';
|
|
17
17
|
import { storedProfile, writePluginSettings } from './profile.js';
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const CAVEMAN_RULE = path.join(EXT_DIR, 'caveman-session', 'rule.md');
|
|
23
|
-
const RTK_SESSION_INDEX = path.join(EXT_DIR, 'rtk-session', 'index.ts');
|
|
24
|
-
const UPDATER_INDEX = path.join(EXT_DIR, 'ai-addons-updater', 'index.ts');
|
|
25
|
-
const COMBO_TOGGLE_INDEX = path.join(EXT_DIR, 'combo-toggle', 'index.ts');
|
|
26
|
-
const TERSIO_COMMANDS_INDEX = path.join(EXT_DIR, 'tersio-commands', 'index.ts');
|
|
27
|
-
const SHARED_TYPES = path.join(EXT_DIR, 'shared', 'types.ts');
|
|
28
|
-
const LIB_UTILS = path.join(EXT_DIR, 'lib', 'utils.ts');
|
|
29
|
-
const SHARED_PLUGIN_SETTINGS = path.join(EXT_DIR, 'shared', 'plugin-settings.ts');
|
|
30
|
-
const SHARED_USAGE_LEDGER = path.join(EXT_DIR, 'shared', 'usage-ledger.ts');
|
|
31
|
-
const SHARED_PRICING = path.join(EXT_DIR, 'shared', 'pricing.ts');
|
|
32
|
-
const SHARED_CARBON = path.join(EXT_DIR, 'shared', 'carbon.ts');
|
|
18
|
+
import { runSettings } from './settings.js';
|
|
19
|
+
import { tersioSettingsFile } from '../extensions/shared/plugin-settings.js';
|
|
20
|
+
import { filesUnder, sourcePath } from './manifest.js';
|
|
21
|
+
import { detectHosts, hostHint, hostLabel, parseHostArg, piTersioSource } from './hosts.js';
|
|
33
22
|
async function stepPonytail(pluginsDir, options) {
|
|
34
23
|
if (!options.quiet)
|
|
35
24
|
console.log(' Ponytail — ensure bundled plugin');
|
|
36
25
|
await fs.mkdir(pluginsDir, { recursive: true });
|
|
37
26
|
const pkgPath = path.join(pluginsDir, 'package.json');
|
|
38
27
|
const pkg = await readPluginsPackage(pkgPath);
|
|
39
|
-
//
|
|
40
|
-
// dependency, so drop the legacy row. Runs after stepSelfPlugin.
|
|
28
|
+
// Drop the legacy Ponytail plugin row; it is a tersio dependency now.
|
|
41
29
|
let migrated = false;
|
|
42
30
|
if ('@dietrichgebert/ponytail' in pkg.dependencies) {
|
|
43
31
|
delete pkg.dependencies['@dietrichgebert/ponytail'];
|
|
@@ -45,9 +33,9 @@ async function stepPonytail(pluginsDir, options) {
|
|
|
45
33
|
}
|
|
46
34
|
if (options.dryRun) {
|
|
47
35
|
if (verbose && !options.quiet)
|
|
48
|
-
|
|
36
|
+
sayTagged(` [dry-run] would write ${pkgPath}`);
|
|
49
37
|
if (migrated && verbose && !options.quiet)
|
|
50
|
-
|
|
38
|
+
sayTagged(` [dry-run] would drop the separate @dietrichgebert/ponytail dependency (bundled with tersio)`);
|
|
51
39
|
}
|
|
52
40
|
else if (migrated) {
|
|
53
41
|
await fs.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
@@ -56,9 +44,7 @@ async function stepPonytail(pluginsDir, options) {
|
|
|
56
44
|
}
|
|
57
45
|
const ponytailExtPath = path.join(pluginsDir, 'node_modules', '@dietrichgebert', 'ponytail', 'pi-extension', 'index.js');
|
|
58
46
|
const probeExt = async () => (await readTextIfExists(ponytailExtPath)) !== null;
|
|
59
|
-
//
|
|
60
|
-
// network. Migration always reinstalls: the probe may hit the stale legacy
|
|
61
|
-
// copy, and only npm install prunes it into the tersio-owned one.
|
|
47
|
+
// Skip the network unless a refresh was asked; migration always reinstalls.
|
|
62
48
|
let ponytailExtExists = !options.reinstall && !options.dryRun && !migrated ? await probeExt() : false;
|
|
63
49
|
if (ponytailExtExists) {
|
|
64
50
|
debug('Bundled Ponytail pi-extension already installed; skipping network refresh');
|
|
@@ -70,8 +56,7 @@ async function stepPonytail(pluginsDir, options) {
|
|
|
70
56
|
ponytailExtExists = true;
|
|
71
57
|
}
|
|
72
58
|
else {
|
|
73
|
-
//
|
|
74
|
-
// Reinstall here only when the probe still misses.
|
|
59
|
+
// Reinstall only when the self-plugin step did not materialize it.
|
|
75
60
|
try {
|
|
76
61
|
await execNetwork('Installing bundled Ponytail', 'npm', ['install', '--no-audit', '--no-fund'], { cwd: pluginsDir, timeout: 180000 });
|
|
77
62
|
}
|
|
@@ -80,7 +65,7 @@ async function stepPonytail(pluginsDir, options) {
|
|
|
80
65
|
await execNetwork('Installing bundled Ponytail', 'bun', ['install'], { cwd: pluginsDir, timeout: 180000 });
|
|
81
66
|
}
|
|
82
67
|
catch (e) {
|
|
83
|
-
|
|
68
|
+
sayTagged(` [fail] Could not install bundled ponytail: ${e.message}`);
|
|
84
69
|
console.log(' [hint] Manual: cd ~/.omp/plugins && npm install --no-audit --no-fund');
|
|
85
70
|
}
|
|
86
71
|
}
|
|
@@ -97,9 +82,7 @@ async function stepPonytail(pluginsDir, options) {
|
|
|
97
82
|
// hideStatus=true keeps the upstream ponytail bar hidden; combo owns the bar.
|
|
98
83
|
await ensurePonytailConfigValue('hideStatus', true, options);
|
|
99
84
|
}
|
|
100
|
-
// Registers
|
|
101
|
-
// Settings → Plugins page (OMP enumerates plugins/package.json dependencies).
|
|
102
|
-
// Returns true when the package is verified in plugins/node_modules.
|
|
85
|
+
// Registers the package so OMP lists it under Settings → Plugins.
|
|
103
86
|
async function stepSelfPlugin(pluginsDir, options) {
|
|
104
87
|
if (!options.quiet)
|
|
105
88
|
console.log(' Tersio — register plugin');
|
|
@@ -110,14 +93,14 @@ async function stepSelfPlugin(pluginsDir, options) {
|
|
|
110
93
|
if (legacy in pkg.dependencies) {
|
|
111
94
|
delete pkg.dependencies[legacy];
|
|
112
95
|
if (!options.dryRun && !options.quiet)
|
|
113
|
-
|
|
96
|
+
sayTagged(` [migrate] dropped legacy ${legacy} dependency`);
|
|
114
97
|
}
|
|
115
98
|
}
|
|
116
99
|
if (options.dryRun) {
|
|
117
100
|
if (verbose && !options.quiet)
|
|
118
|
-
|
|
101
|
+
sayTagged(` [dry-run] would add ${PACKAGE_NAME}@^${PACKAGE_VERSION} to ${pkgPath}`);
|
|
119
102
|
if (verbose && !options.quiet)
|
|
120
|
-
|
|
103
|
+
sayTagged(` [dry-run] would run: npm install --no-audit --no-fund (in ${pluginsDir})`);
|
|
121
104
|
return false;
|
|
122
105
|
}
|
|
123
106
|
// pkg.dependencies[PACKAGE_NAME] was assigned above, so only reinstall skips this fast path.
|
|
@@ -138,14 +121,14 @@ async function stepSelfPlugin(pluginsDir, options) {
|
|
|
138
121
|
await execNetwork('Installing Tersio plugin dependencies', 'bun', ['install'], { cwd: pluginsDir, timeout: 180000 });
|
|
139
122
|
}
|
|
140
123
|
catch (e) {
|
|
141
|
-
|
|
142
|
-
|
|
124
|
+
sayTagged(` [fail] Could not install ${PACKAGE_NAME} into plugins dir: ${e.message}`);
|
|
125
|
+
sayTagged(` [hint] Manual: cd ~/.omp/plugins && npm install ${PACKAGE_NAME}@^${PACKAGE_VERSION} --save --no-audit --no-fund`);
|
|
143
126
|
return false;
|
|
144
127
|
}
|
|
145
128
|
}
|
|
146
129
|
const installedPkg = path.join(pluginsDir, 'node_modules', PACKAGE_NAME, 'package.json');
|
|
147
130
|
if ((await readTextIfExists(installedPkg)) === null) {
|
|
148
|
-
|
|
131
|
+
sayTagged(` [warn] ${PACKAGE_NAME} not found in plugins/node_modules after install`);
|
|
149
132
|
return false;
|
|
150
133
|
}
|
|
151
134
|
debug('tersio listed in OMP plugins');
|
|
@@ -155,7 +138,7 @@ function resolveRtkTriple() {
|
|
|
155
138
|
const triple = rtkPlatformSpec()?.triple;
|
|
156
139
|
if (triple)
|
|
157
140
|
return triple;
|
|
158
|
-
|
|
141
|
+
sayTagged(` [fail] Unsupported platform: ${process.platform}/${process.arch}`);
|
|
159
142
|
console.log(' [hint] Manual: https://github.com/rtk-ai/rtk/releases');
|
|
160
143
|
return null;
|
|
161
144
|
}
|
|
@@ -164,8 +147,8 @@ function findRtkAsset(release, triple) {
|
|
|
164
147
|
const asset = assets.find((a) => a.name === `rtk-${triple}.zip` || a.name === `rtk-${triple}.tar.gz`);
|
|
165
148
|
if (asset)
|
|
166
149
|
return asset;
|
|
167
|
-
|
|
168
|
-
|
|
150
|
+
sayTagged(` [fail] No rtk-${triple}.<zip|tar.gz> in release ${release.tag_name}`);
|
|
151
|
+
sayTagged(` [hint] Available: ${assets.map((a) => a.name).filter((n) => n.startsWith('rtk-')).join(', ')}`);
|
|
169
152
|
return null;
|
|
170
153
|
}
|
|
171
154
|
async function downloadRtkChecksums(release) {
|
|
@@ -190,16 +173,16 @@ async function verifyRtkArchive(archivePath, assetName, checksumsText, options =
|
|
|
190
173
|
const actual = await sha256File(archivePath);
|
|
191
174
|
if (!expected) {
|
|
192
175
|
if (!options.quiet)
|
|
193
|
-
|
|
176
|
+
sayTagged(` [warn] checksums.txt missing entry for ${assetName} — skipping verification`);
|
|
194
177
|
return true;
|
|
195
178
|
}
|
|
196
179
|
if (actual === expected) {
|
|
197
180
|
debug(`Checksum verified for ${assetName}`);
|
|
198
181
|
return true;
|
|
199
182
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
183
|
+
sayTagged(` [fail] Checksum mismatch for ${assetName}`);
|
|
184
|
+
sayTagged(` [fail] Expected: ${expected}`);
|
|
185
|
+
sayTagged(` [fail] Got: ${actual}`);
|
|
203
186
|
// Caller finally removes the temp dir.
|
|
204
187
|
return false;
|
|
205
188
|
}
|
|
@@ -233,98 +216,120 @@ async function extractRtkArchive(archivePath, extractDir) {
|
|
|
233
216
|
}
|
|
234
217
|
catch (e) {
|
|
235
218
|
debug(`tar xzf failed: ${shortError(e)}`);
|
|
236
|
-
|
|
219
|
+
sayTagged(` [fail] tar could not extract ${path.basename(archivePath)}`);
|
|
237
220
|
console.log(' [hint] Manual: https://github.com/rtk-ai/rtk/releases');
|
|
238
221
|
return false;
|
|
239
222
|
}
|
|
240
223
|
return true;
|
|
241
224
|
}
|
|
242
|
-
|
|
225
|
+
sayTagged(` [fail] Unknown archive format: ${archivePath}`);
|
|
243
226
|
return false;
|
|
244
227
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
228
|
+
// The binary is machine-wide and only OMP needs the wiring, so an install that
|
|
229
|
+
// finds one present only rebinds. `tersio update` still refreshes it.
|
|
230
|
+
async function stepRtk(binDir, options, target = 'omp') {
|
|
248
231
|
const binDest = path.join(binDir, RTK_BINARY_NAME);
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
232
|
+
const found = resolveRtkBinary();
|
|
233
|
+
const bind = async (binary) => {
|
|
234
|
+
if (target !== 'omp')
|
|
235
|
+
return;
|
|
236
|
+
if (!(await fileExists(binary))) {
|
|
237
|
+
console.log(' [skip] no rtk binary to wire — install rtk, then run: rtk init -g --agent omp');
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
await wireRtkOmp(binary, options);
|
|
241
|
+
};
|
|
242
|
+
if (found && !applyUpdate) {
|
|
243
|
+
// The path only under --verbose: a plain run, and a dry run, stay free of
|
|
244
|
+
// real user paths.
|
|
245
|
+
if (!options.quiet) {
|
|
246
|
+
const where = verbose ? ` (${found})` : '';
|
|
247
|
+
console.log(` RTK — already installed${where}${target === 'omp' ? ', binding into OMP' : ', nothing to bind on pi'}`);
|
|
248
|
+
}
|
|
249
|
+
await bind(found);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (!options.quiet)
|
|
253
|
+
console.log(` RTK — download binary and ${target === 'omp' ? 'wire into OMP' : 'put it on PATH'}`);
|
|
254
|
+
// A failed download must not skip wiring: a pre-existing binary serves the
|
|
255
|
+
// OMP hook just as well. Dry runs stay offline.
|
|
252
256
|
if (options.dryRun) {
|
|
253
257
|
if (verbose && !options.quiet)
|
|
254
|
-
|
|
255
|
-
await
|
|
258
|
+
sayTagged(` [dry-run] would download rtk binary and install to ${binDest}`);
|
|
259
|
+
await bind(found ?? binDest);
|
|
256
260
|
return;
|
|
257
261
|
}
|
|
258
262
|
try {
|
|
259
|
-
const release = await withInteractiveSpinner('Finding latest RTK release', () => fetchJson(RTK_RELEASE_API));
|
|
260
263
|
const triple = resolveRtkTriple();
|
|
261
264
|
if (!triple)
|
|
262
265
|
return;
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const [downloadedChecksums] = await Promise.all([
|
|
271
|
-
downloadRtkChecksums(release),
|
|
272
|
-
httpsDownload(asset.browser_download_url, archivePath),
|
|
273
|
-
]);
|
|
274
|
-
update('Verifying RTK checksum');
|
|
275
|
-
return downloadedChecksums;
|
|
276
|
-
});
|
|
277
|
-
if (!await verifyRtkArchive(archivePath, asset.name, checksumsText, options))
|
|
278
|
-
return;
|
|
279
|
-
const extractDir = path.join(tmpDir, 'extracted');
|
|
280
|
-
if (!await extractRtkArchive(archivePath, extractDir))
|
|
266
|
+
// One task with streamed sub-steps: release lookup, download, checksum,
|
|
267
|
+
// extract, install. Non-TTY keeps the plain lines each step already prints.
|
|
268
|
+
await withInteractiveTask('Installing RTK', async (update) => {
|
|
269
|
+
const release = await fetchJson(RTK_RELEASE_API);
|
|
270
|
+
const asset = findRtkAsset(release, triple);
|
|
271
|
+
if (!asset) {
|
|
272
|
+
sayTagged(` [fail] No rtk-${triple}.<zip|tar.gz> in release ${release.tag_name}`);
|
|
281
273
|
return;
|
|
282
|
-
const found = await findFile(extractDir, RTK_BINARY_NAME);
|
|
283
|
-
if (!found) {
|
|
284
|
-
console.log(` [fail] Could not find ${RTK_BINARY_NAME} in extracted archive`);
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
await fs.mkdir(path.dirname(binDest), { recursive: true });
|
|
288
|
-
await fs.copyFile(binDest, `${binDest}.bak`).catch(() => { });
|
|
289
|
-
await fs.copyFile(found, binDest);
|
|
290
|
-
if (!IS_WINDOWS) {
|
|
291
|
-
await fs.chmod(binDest, 0o755);
|
|
292
|
-
debug(`chmod 755 ${binDest}`);
|
|
293
274
|
}
|
|
275
|
+
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'omp-rtk-'));
|
|
294
276
|
try {
|
|
295
|
-
|
|
277
|
+
const archivePath = path.join(tmpDir, asset.name);
|
|
278
|
+
update(`Downloading ${asset.name}`);
|
|
279
|
+
const [checksumsText] = await Promise.all([
|
|
280
|
+
downloadRtkChecksums(release),
|
|
281
|
+
httpsDownload(asset.browser_download_url, archivePath),
|
|
282
|
+
]);
|
|
283
|
+
update('Verifying checksum');
|
|
284
|
+
if (!await verifyRtkArchive(archivePath, asset.name, checksumsText, options)) {
|
|
285
|
+
sayTagged(' [hint] RTK install skipped for this run');
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
update('Extracting');
|
|
289
|
+
const extractDir = path.join(tmpDir, 'extracted');
|
|
290
|
+
if (!await extractRtkArchive(archivePath, extractDir)) {
|
|
291
|
+
sayTagged(` [fail] Could not extract ${asset.name}`);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
update('Installing binary');
|
|
295
|
+
const found = await findFile(extractDir, RTK_BINARY_NAME);
|
|
296
|
+
if (!found) {
|
|
297
|
+
sayTagged(` [fail] Could not find ${RTK_BINARY_NAME} in extracted archive`);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
await fs.mkdir(path.dirname(binDest), { recursive: true });
|
|
301
|
+
await fs.copyFile(binDest, `${binDest}.bak`).catch(() => { });
|
|
302
|
+
await fs.copyFile(found, binDest);
|
|
303
|
+
if (!IS_WINDOWS) {
|
|
304
|
+
await fs.chmod(binDest, 0o755);
|
|
305
|
+
debug(`chmod 755 ${binDest}`);
|
|
306
|
+
}
|
|
307
|
+
try {
|
|
308
|
+
await execP(binDest, ['--version'], { timeout: 30000, shell: false });
|
|
309
|
+
}
|
|
310
|
+
catch {
|
|
311
|
+
sayTagged(` [hint] Verify manually: ${binDest} --version`);
|
|
312
|
+
}
|
|
296
313
|
}
|
|
297
|
-
|
|
298
|
-
|
|
314
|
+
finally {
|
|
315
|
+
await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => { });
|
|
299
316
|
}
|
|
300
|
-
}
|
|
301
|
-
finally {
|
|
302
|
-
await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => { });
|
|
303
|
-
}
|
|
317
|
+
});
|
|
304
318
|
}
|
|
305
319
|
catch (e) {
|
|
306
|
-
|
|
320
|
+
sayTagged(` [fail] RTK: ${e.message}`);
|
|
307
321
|
console.log(' [hint] Manual: https://github.com/rtk-ai/rtk/releases');
|
|
308
322
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
if (!(await fileExists(binDest))) {
|
|
314
|
-
console.log(' [skip] no rtk binary to wire — install rtk, then run: rtk init -g --agent omp');
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
// Binary alone never meters OMP sessions — wire rtk's tool_call
|
|
318
|
-
// extension so bash commands rewrite to rtk and land in history.db.
|
|
319
|
-
await wireRtkOmp(binDest, options);
|
|
323
|
+
// Wire rtk's tool_call extension: the binary alone never meters a session.
|
|
324
|
+
await bind(binDest);
|
|
320
325
|
}
|
|
321
|
-
// Copy
|
|
322
|
-
//
|
|
326
|
+
// Copy sources into the target extension dir. First entry is required; the
|
|
327
|
+
// rest are optional companions.
|
|
323
328
|
async function copySources(extDir, files, skipLabel, options) {
|
|
324
329
|
const src = await readTextIfExists(files[0][0]);
|
|
325
330
|
if (!src) {
|
|
326
331
|
if (!options.quiet && options.dryRun)
|
|
327
|
-
|
|
332
|
+
sayTagged(` [skip] ${skipLabel} not found in repo`);
|
|
328
333
|
return false;
|
|
329
334
|
}
|
|
330
335
|
await writeIfChanged(path.join(extDir, files[0][1]), src, options);
|
|
@@ -340,25 +345,18 @@ async function copySources(extDir, files, skipLabel, options) {
|
|
|
340
345
|
async function stepSharedSessionState(extDir, options) {
|
|
341
346
|
if (!options.quiet)
|
|
342
347
|
console.log(' Shared files — sync session bridge');
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
[SHARED_TYPES, path.join('shared', 'types.ts')],
|
|
346
|
-
[LIB_UTILS, path.join('lib', 'utils.ts')],
|
|
347
|
-
[SHARED_PLUGIN_SETTINGS, path.join('shared', 'plugin-settings.ts')],
|
|
348
|
-
[SHARED_USAGE_LEDGER, path.join('shared', 'usage-ledger.ts')],
|
|
349
|
-
[SHARED_PRICING, path.join('shared', 'pricing.ts')],
|
|
350
|
-
[SHARED_CARBON, path.join('shared', 'carbon.ts')],
|
|
351
|
-
], 'shared/session-state.js', options);
|
|
348
|
+
const shared = [...filesUnder('shared'), ...filesUnder('lib')];
|
|
349
|
+
await copySources(extDir, shared, 'shared/session-state.js', options);
|
|
352
350
|
}
|
|
353
351
|
async function stepRtkSession(extDir, options) {
|
|
354
352
|
if (!options.quiet)
|
|
355
353
|
console.log(' RTK session — install session mode');
|
|
356
|
-
await copySources(extDir,
|
|
354
|
+
await copySources(extDir, filesUnder('rtk-session'), 'rtk-session/index.ts', options);
|
|
357
355
|
}
|
|
358
|
-
// One
|
|
359
|
-
//
|
|
356
|
+
// One fetch serves the install; dry runs stay offline and preview the bundled
|
|
357
|
+
// rule's destination.
|
|
360
358
|
async function fetchCavemanRule(options) {
|
|
361
|
-
const bundled = await readTextIfExists(
|
|
359
|
+
const bundled = await readTextIfExists(sourcePath('caveman-session/rule.md'));
|
|
362
360
|
if (options.dryRun)
|
|
363
361
|
return bundled;
|
|
364
362
|
try {
|
|
@@ -369,7 +367,7 @@ async function fetchCavemanRule(options) {
|
|
|
369
367
|
debug(`Using bundled Caveman rule: ${e.message}`);
|
|
370
368
|
return bundled;
|
|
371
369
|
}
|
|
372
|
-
|
|
370
|
+
sayTagged(` [warn] Could not fetch caveman rule: ${e.message}`);
|
|
373
371
|
return null;
|
|
374
372
|
}
|
|
375
373
|
}
|
|
@@ -385,35 +383,32 @@ async function stepCaveman(extDir, rule, options) {
|
|
|
385
383
|
}
|
|
386
384
|
else if (rule === null) {
|
|
387
385
|
console.log(' [skip] Caveman rule.md unavailable');
|
|
388
|
-
|
|
386
|
+
sayTagged(` [hint] Manual: ${CAVEMAN_REMOTE_RULE}`);
|
|
389
387
|
}
|
|
390
388
|
else {
|
|
391
389
|
await writeIfChanged(ruleDest, rule, options);
|
|
392
390
|
}
|
|
393
|
-
await copySources(extDir,
|
|
391
|
+
await copySources(extDir, filesUnder('caveman-session').filter(([, to]) => to.endsWith('index.ts')), 'caveman-session/index.ts', options);
|
|
394
392
|
}
|
|
395
393
|
async function stepTersioCommands(extDir, options) {
|
|
396
394
|
if (!options.quiet)
|
|
397
395
|
console.log(' Tersio commands — install /tersio root command');
|
|
398
|
-
await copySources(extDir,
|
|
396
|
+
await copySources(extDir, filesUnder('tersio-commands'), 'tersio-commands/index.ts', options);
|
|
399
397
|
}
|
|
400
398
|
async function stepUpdater(extDir, options) {
|
|
401
|
-
await copySources(extDir,
|
|
399
|
+
await copySources(extDir, filesUnder('ai-addons-updater'), 'ai-addons-updater/index.ts', options);
|
|
402
400
|
}
|
|
403
401
|
async function stepCombo(extDir, options) {
|
|
404
402
|
if (!options.quiet)
|
|
405
403
|
console.log(' Combo — install preset switch');
|
|
406
|
-
await copySources(extDir,
|
|
404
|
+
await copySources(extDir, filesUnder('combo-toggle'), 'combo-toggle/index.ts', options);
|
|
407
405
|
}
|
|
408
|
-
async function resolveProfile(
|
|
409
|
-
// Seed from the
|
|
410
|
-
// user's configured defaults instead of resetting them to off.
|
|
406
|
+
async function resolveProfile(opts = {}) {
|
|
407
|
+
// Seed from the stored defaults so flag-less runs keep them.
|
|
411
408
|
const profile = await storedProfile();
|
|
412
|
-
//
|
|
413
|
-
//
|
|
414
|
-
|
|
415
|
-
// given, and never for --apply-update runs.
|
|
416
|
-
if (tty() && !profileFlagsGiven && !applyUpdate && (install || forceReinstall || reinstall)) {
|
|
409
|
+
// One numbered prompt for all three modes, for a real terminal user with no
|
|
410
|
+
// default flags. Never for --apply-update, and never for a script.
|
|
411
|
+
if (tty() && !profileFlagsGiven && !applyUpdate) {
|
|
417
412
|
const choice = await askInteractiveChoice('Session-start defaults — Combo preset', [
|
|
418
413
|
{ value: 'off', label: 'off' },
|
|
419
414
|
{ value: 'medium', label: 'medium', hint: 'caveman=lite, rtk=on, ponytail=lite' },
|
|
@@ -445,9 +440,7 @@ async function resolveProfile(forceReinstall = false, opts = {}) {
|
|
|
445
440
|
return profile;
|
|
446
441
|
}
|
|
447
442
|
let updatePromptDone = false;
|
|
448
|
-
// Bare `tersio` at a terminal
|
|
449
|
-
// update offer first (when pending), then a Clack menu over every command.
|
|
450
|
-
// Scripts, pipes, --yes, and --dry-run keep the old straight-to-install path.
|
|
443
|
+
// Bare `tersio` at a terminal picks a command; scripts and flags install.
|
|
451
444
|
async function runCommandMenu() {
|
|
452
445
|
printWelcome();
|
|
453
446
|
const newer = await checkForUpdate();
|
|
@@ -468,8 +461,8 @@ async function runCommandMenu() {
|
|
|
468
461
|
const choice = await askInteractiveChoice('Tersio — what next?', [
|
|
469
462
|
{ value: 'install', label: 'Install add-ons', hint: 'user scope + combo defaults' },
|
|
470
463
|
{ value: 'update', label: 'Update', hint: 'CLI version check, then refresh add-ons (RTK, Caveman rule, Ponytail)' },
|
|
471
|
-
{ value: 'reinstall', label: 'Reinstall', hint: 'clean and reinstall the add-ons, Ponytail package kept' },
|
|
472
464
|
{ value: 'doctor', label: 'Doctor', hint: 'verify the installation' },
|
|
465
|
+
{ value: 'settings', label: 'Settings', hint: 'default combo mode, currency, diagnosis schedule' },
|
|
473
466
|
{ value: 'usage', label: 'Usage', hint: 'token usage and savings report' },
|
|
474
467
|
{ value: 'dashboard', label: 'Dashboard', hint: 'open the report in your browser' },
|
|
475
468
|
{ value: 'reset', label: 'Reset statistics', hint: 'clear statistics; transcripts and RTK history stay' },
|
|
@@ -504,10 +497,6 @@ async function runCommandMenu() {
|
|
|
504
497
|
closeRL();
|
|
505
498
|
break;
|
|
506
499
|
}
|
|
507
|
-
case 'reinstall':
|
|
508
|
-
await runInstall({ reinstall: true });
|
|
509
|
-
closeRL();
|
|
510
|
-
break;
|
|
511
500
|
case 'doctor': {
|
|
512
501
|
const summary = await runDoctor();
|
|
513
502
|
if (!dryRun && summary.missing + summary.warn > 0) {
|
|
@@ -527,6 +516,10 @@ async function runCommandMenu() {
|
|
|
527
516
|
closeRL();
|
|
528
517
|
break;
|
|
529
518
|
}
|
|
519
|
+
case 'settings':
|
|
520
|
+
await runSettings();
|
|
521
|
+
closeRL();
|
|
522
|
+
break;
|
|
530
523
|
case 'usage':
|
|
531
524
|
await runUsage();
|
|
532
525
|
closeRL();
|
|
@@ -545,40 +538,127 @@ async function runCommandMenu() {
|
|
|
545
538
|
break;
|
|
546
539
|
}
|
|
547
540
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
541
|
+
// Which host this run targets. --host pins it; a TTY user picks; scripts,
|
|
542
|
+
// pipes, --yes, and --dry-run keep the pre-multi-host default of Oh My Pi.
|
|
543
|
+
let targetHost = 'omp';
|
|
544
|
+
// Hosts the menu advertises but cannot install yet. Listed disabled so the
|
|
545
|
+
// roadmap is visible without offering a target that does nothing.
|
|
546
|
+
const SOON_HOSTS = [
|
|
547
|
+
{ value: 'claude', label: 'Claude Code', hint: 'Coming soon' },
|
|
548
|
+
{ value: 'opencode', label: 'OpenCode', hint: 'Coming soon' },
|
|
549
|
+
{ value: 'codex', label: 'Codex', hint: 'Coming soon' },
|
|
550
|
+
];
|
|
551
|
+
async function chooseHost(title, only) {
|
|
552
|
+
const hosts = detectHosts().filter((host) => !only || only.includes(host.id));
|
|
553
|
+
const options = hosts.map((host) => ({
|
|
554
|
+
value: host.id,
|
|
555
|
+
label: hostLabel(host),
|
|
556
|
+
hint: hostHint(host),
|
|
557
|
+
}));
|
|
558
|
+
// A scoped menu (uninstall --host) lists only real hosts.
|
|
559
|
+
if (!only)
|
|
560
|
+
options.push(...SOON_HOSTS.map((h) => ({ ...h, disabled: true })));
|
|
561
|
+
const choice = await askInteractiveChoice(title, options, targetHost);
|
|
562
|
+
if (choice.status !== 'selected') {
|
|
563
|
+
closeRL();
|
|
564
|
+
process.exit(130);
|
|
565
|
+
}
|
|
566
|
+
return choice.value;
|
|
567
|
+
}
|
|
568
|
+
function hostEntry(id) {
|
|
569
|
+
return detectHosts().find((host) => host.id === id);
|
|
570
|
+
}
|
|
571
|
+
// pi auto-discovers `<agent-dir>/extensions/**/index.ts`, so this route writes
|
|
572
|
+
// the same tree as OMP's and registers nothing.
|
|
573
|
+
async function stepPiLayer(options) {
|
|
574
|
+
const agentDir = piAgentDir();
|
|
575
|
+
const extDir = path.join(agentDir, 'extensions');
|
|
576
|
+
const declared = piTersioSource(agentDir);
|
|
577
|
+
if (declared)
|
|
578
|
+
sayTagged(` [note] pi also has a tersio package (${declared}) — remove one, or both copies load: pi remove npm:${PACKAGE_NAME}`);
|
|
579
|
+
if (!options.quiet)
|
|
580
|
+
console.log(` Pi — write the extension tree (${extDir})`);
|
|
581
|
+
const cavemanRule = await fetchCavemanRule(options);
|
|
582
|
+
await stepSharedSessionState(extDir, options);
|
|
583
|
+
// OMP loads rtk-session from its plugin manifest, so only the pi tree copies it.
|
|
584
|
+
await copySources(extDir, filesUnder('rtk-session'), 'rtk-session/index.ts', options);
|
|
585
|
+
await stepCaveman(extDir, cavemanRule, options);
|
|
586
|
+
await stepCombo(extDir, options);
|
|
587
|
+
await stepTersioCommands(extDir, options);
|
|
588
|
+
await stepUpdater(extDir, options);
|
|
589
|
+
await stepPonytailForPi(agentDir, options);
|
|
590
|
+
}
|
|
591
|
+
// Ponytail for pi: install the package, or copy the bundled skills offline.
|
|
592
|
+
async function stepPonytailForPi(agentDir, options) {
|
|
593
|
+
if (!options.quiet)
|
|
594
|
+
console.log(' Ponytail — ensure the pi package');
|
|
595
|
+
if (!options.dryRun) {
|
|
596
|
+
try {
|
|
597
|
+
await execNetwork('Installing Ponytail for pi', 'pi', ['install', 'npm:@dietrichgebert/ponytail'], { timeout: 300000 });
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
catch (e) {
|
|
601
|
+
sayTagged(` [fail] pi install ponytail: ${shortError(e)}`);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
const skills = findHoistedPackage('@dietrichgebert/ponytail', path.dirname(fileURLToPath(import.meta.url)), 'skills');
|
|
605
|
+
if (!skills) {
|
|
606
|
+
console.log(' [skip] Ponytail package not found next to the CLI — skills not copied');
|
|
607
|
+
console.log(' [hint] The /ponytail command will not work; run: pi install npm:@dietrichgebert/ponytail');
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
const dest = path.join(agentDir, 'skills');
|
|
611
|
+
if (options.dryRun) {
|
|
612
|
+
if (verbose && !options.quiet)
|
|
613
|
+
sayTagged(` [dry-run] would copy Ponytail skills to ${dest}`);
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
for (const entry of await fs.readdir(skills, { withFileTypes: true })) {
|
|
617
|
+
if (!entry.isDirectory())
|
|
618
|
+
continue;
|
|
619
|
+
const to = path.join(dest, entry.name);
|
|
620
|
+
await fs.rm(to, { recursive: true, force: true });
|
|
621
|
+
await fs.cp(path.join(skills, entry.name), to, { recursive: true });
|
|
622
|
+
if (!options.quiet)
|
|
623
|
+
sayTagged(` [write] skill ${entry.name} → ${to}`);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
async function runInstall() {
|
|
627
|
+
// Without this guard a picked command re-enters here with command === null
|
|
628
|
+
// and loops straight back into the picker.
|
|
553
629
|
if (command === null && tty() && !yes && !updatePromptDone) {
|
|
554
630
|
await runCommandMenu();
|
|
555
631
|
return;
|
|
556
632
|
}
|
|
557
|
-
//
|
|
558
|
-
|
|
559
|
-
|
|
633
|
+
// Ask the host first: it decides the tree, the cleanup, and the defaults file.
|
|
634
|
+
let pinned;
|
|
635
|
+
try {
|
|
636
|
+
pinned = parseHostArg(args);
|
|
637
|
+
}
|
|
638
|
+
catch (e) {
|
|
639
|
+
console.error(`[fail] ${e.message}`);
|
|
640
|
+
process.exit(1);
|
|
641
|
+
}
|
|
642
|
+
if (pinned)
|
|
643
|
+
targetHost = pinned;
|
|
644
|
+
else if (tty() && !yes && !dryRun && !applyUpdate && command !== 'uninstall') {
|
|
645
|
+
targetHost = await chooseHost('Install Tersio into which agent?');
|
|
646
|
+
}
|
|
647
|
+
// apply-update stays silent: the parent printed the plan.
|
|
560
648
|
const quiet = applyUpdate;
|
|
561
649
|
if (!quiet)
|
|
562
650
|
printWelcome();
|
|
563
|
-
if (isReinstall) {
|
|
564
|
-
// removeRtk stays false: reinstall is about to replace the binary, and
|
|
565
|
-
// deleting it first would leave nothing to wire if the fresh download
|
|
566
|
-
// fails (rate limit, offline). rtk.ts is removed here and re-wired below.
|
|
567
|
-
await runUninstall({ yes: true, removePonytail: false, removeRtk: false });
|
|
568
|
-
}
|
|
569
651
|
if (dryRun && !quiet)
|
|
570
652
|
console.log('Preview — no changes will be written.\n');
|
|
571
653
|
if (!quiet) {
|
|
572
654
|
console.log(`Installing Tersio v${PACKAGE_VERSION}`);
|
|
573
655
|
}
|
|
574
|
-
//
|
|
575
|
-
// the apply-update payload, which is itself an update run.
|
|
656
|
+
// Nudge about a newer release; silent without a TTY.
|
|
576
657
|
if (tty() && !applyUpdate && command !== 'uninstall') {
|
|
577
658
|
const newer = await checkForUpdate();
|
|
578
659
|
if (typeof newer === 'string') {
|
|
579
|
-
//
|
|
580
|
-
//
|
|
581
|
-
// update and stops here — the fresh binary owns what follows.
|
|
660
|
+
// Update pending: offer it here instead of burying it above the install
|
|
661
|
+
// prompts. Yes runs the full update and stops.
|
|
582
662
|
if (command === null && !dryRun && !yes && !updatePromptDone) {
|
|
583
663
|
const answer = await askInteractiveConfirm(`tersio ${newer} is available (installed ${PACKAGE_VERSION}). Install it now?`);
|
|
584
664
|
if (answer.status === 'confirmed' && answer.value) {
|
|
@@ -598,22 +678,35 @@ async function runInstall(overrides = {}) {
|
|
|
598
678
|
}
|
|
599
679
|
}
|
|
600
680
|
// Resolve session defaults: flags > interactive prompt > defaults.
|
|
601
|
-
const profile = await resolveProfile(
|
|
681
|
+
const profile = await resolveProfile({ quiet });
|
|
602
682
|
const userDir = OMP_AGENT_DIR;
|
|
603
683
|
const userExtDir = path.join(userDir, 'extensions');
|
|
604
|
-
// apply-update
|
|
605
|
-
|
|
606
|
-
//
|
|
607
|
-
|
|
684
|
+
// apply-update refreshes every add-on like a reinstall.
|
|
685
|
+
const installOptions = { dryRun, verbose, yes, reinstall: applyUpdate, quiet };
|
|
686
|
+
// Point of no return: disk writes start here; pipes stay unattended.
|
|
687
|
+
if (!yes && !dryRun && !applyUpdate && tty()) {
|
|
688
|
+
const target = hostEntry(targetHost);
|
|
689
|
+
const where = targetHost === 'pi' ? path.join(piAgentDir(), 'extensions') : path.join(OMP_AGENT_DIR, 'extensions');
|
|
690
|
+
console.log(`\nWill install into ${target.label}:`);
|
|
691
|
+
console.log(` ${where}`);
|
|
692
|
+
console.log(' caveman · rtk · ponytail session modes');
|
|
693
|
+
console.log(` session defaults → ${tersioSettingsFile()}`);
|
|
694
|
+
const go = await askInteractiveConfirm(`Install Tersio into ${target.label}?`);
|
|
695
|
+
if (go.status !== 'confirmed' || !go.value) {
|
|
696
|
+
closeRL();
|
|
697
|
+
console.log('\nAborted. Nothing was changed.');
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
608
701
|
try {
|
|
609
702
|
const v = (await execP(OMP_BIN, ['--version'])).stdout.trim();
|
|
610
703
|
if (verbose && !quiet)
|
|
611
704
|
console.log(` omp ${v}`);
|
|
612
705
|
}
|
|
613
706
|
catch {
|
|
614
|
-
|
|
707
|
+
if (targetHost === 'omp')
|
|
708
|
+
console.log(' [fail] omp not found — ensure it\'s installed');
|
|
615
709
|
}
|
|
616
|
-
const cavemanRule = await fetchCavemanRule(options);
|
|
617
710
|
const failures = [];
|
|
618
711
|
const capture = async (label, work) => {
|
|
619
712
|
try {
|
|
@@ -621,21 +714,33 @@ async function runInstall(overrides = {}) {
|
|
|
621
714
|
}
|
|
622
715
|
catch (e) {
|
|
623
716
|
failures.push(label);
|
|
624
|
-
|
|
717
|
+
sayTagged(` [fail] ${label}: ${shortError(e)}`);
|
|
625
718
|
}
|
|
626
719
|
};
|
|
627
|
-
|
|
720
|
+
if (targetHost === 'pi') {
|
|
721
|
+
await capture('pi tree', () => stepPiLayer(installOptions));
|
|
722
|
+
await capture('rtk', () => stepRtk(BUN_BIN_DIR, installOptions, 'pi'));
|
|
723
|
+
await capture('settings', () => writePluginSettings(profile, installOptions));
|
|
724
|
+
if (failures.length > 0)
|
|
725
|
+
console.log(`\nDone with ${failures.length} failure(s) — see [fail] lines above.`);
|
|
726
|
+
else
|
|
727
|
+
console.log('\nDone — restart pi, then /combo medium.');
|
|
728
|
+
closeRL();
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
const cavemanRule = await fetchCavemanRule(installOptions);
|
|
732
|
+
await capture('shared', () => stepSharedSessionState(userExtDir, installOptions));
|
|
628
733
|
let selfPlugin = false;
|
|
629
|
-
await capture('self-plugin', async () => { selfPlugin = await stepSelfPlugin(OMP_PLUGINS_DIR,
|
|
630
|
-
await capture('ponytail', () => stepPonytail(OMP_PLUGINS_DIR,
|
|
631
|
-
await capture('rtk', () => stepRtk(BUN_BIN_DIR,
|
|
632
|
-
await capture('rtk session', () => stepRtkSession(userExtDir,
|
|
633
|
-
await capture('caveman', () => stepCaveman(path.join(OMP_PLUGINS_DIR, 'node_modules', '@krtclcdy', 'tersio', 'extensions'), cavemanRule,
|
|
634
|
-
await capture('combo', () => stepCombo(userExtDir,
|
|
635
|
-
await capture('commands', () => stepTersioCommands(userExtDir,
|
|
636
|
-
await capture('updater', () => stepUpdater(userExtDir,
|
|
734
|
+
await capture('self-plugin', async () => { selfPlugin = await stepSelfPlugin(OMP_PLUGINS_DIR, installOptions); });
|
|
735
|
+
await capture('ponytail', () => stepPonytail(OMP_PLUGINS_DIR, installOptions));
|
|
736
|
+
await capture('rtk', () => stepRtk(BUN_BIN_DIR, installOptions));
|
|
737
|
+
await capture('rtk session', () => stepRtkSession(userExtDir, installOptions));
|
|
738
|
+
await capture('caveman', () => stepCaveman(path.join(OMP_PLUGINS_DIR, 'node_modules', '@krtclcdy', 'tersio', 'extensions'), cavemanRule, installOptions));
|
|
739
|
+
await capture('combo', () => stepCombo(userExtDir, installOptions));
|
|
740
|
+
await capture('commands', () => stepTersioCommands(userExtDir, installOptions));
|
|
741
|
+
await capture('updater', () => stepUpdater(userExtDir, installOptions));
|
|
637
742
|
if (selfPlugin)
|
|
638
|
-
await capture('settings', () => writePluginSettings(profile,
|
|
743
|
+
await capture('settings', () => writePluginSettings(profile, installOptions));
|
|
639
744
|
if (quiet) {
|
|
640
745
|
if (failures.length > 0)
|
|
641
746
|
console.log(` add-ons: ${failures.length} failed (${failures.join(', ')}) — see [fail] lines above`);
|