@bytevion/cli 0.2.0 → 0.3.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/dist/base.js +8 -1
- package/dist/commands/opt/preset.js +8 -5
- package/dist/commands/opt/show.js +20 -6
- package/dist/commands/setup.js +115 -39
- package/dist/commands/usage.js +32 -8
- package/dist/hooks/init/home.js +17 -5
- package/dist/lib/friendly.d.ts +8 -0
- package/dist/lib/friendly.js +86 -0
- package/dist/lib/home.d.ts +16 -0
- package/dist/lib/home.js +97 -0
- package/dist/lib/output.d.ts +3 -0
- package/dist/lib/output.js +43 -0
- package/dist/lib/presets.d.ts +9 -0
- package/dist/lib/presets.js +40 -0
- package/dist/lib/ui.js +13 -1
- package/oclif.manifest.json +105 -103
- package/package.json +1 -1
package/dist/base.js
CHANGED
|
@@ -6,6 +6,7 @@ const api_1 = require("./lib/api");
|
|
|
6
6
|
const config_1 = require("./lib/config");
|
|
7
7
|
const credentials_1 = require("./lib/credentials");
|
|
8
8
|
const errors_1 = require("./lib/errors");
|
|
9
|
+
const friendly_1 = require("./lib/friendly");
|
|
9
10
|
class BaseCommand extends core_1.Command {
|
|
10
11
|
static enableJsonFlag = true;
|
|
11
12
|
static baseFlags = {
|
|
@@ -41,7 +42,13 @@ class BaseCommand extends core_1.Command {
|
|
|
41
42
|
}
|
|
42
43
|
async catch(err) {
|
|
43
44
|
if (err instanceof errors_1.ByteError) {
|
|
44
|
-
|
|
45
|
+
// --json callers want the machine-readable oclif error; humans get a panel that
|
|
46
|
+
// says what broke, why, and the exact next command — then we exit with the code.
|
|
47
|
+
if (this.jsonEnabled()) {
|
|
48
|
+
return this.error(err.message, { code: err.code, exit: err.exit });
|
|
49
|
+
}
|
|
50
|
+
this.logToStderr((0, friendly_1.renderErrorPanel)((0, friendly_1.friendlyError)(err)));
|
|
51
|
+
return this.exit(err.exit);
|
|
45
52
|
}
|
|
46
53
|
return super.catch(err);
|
|
47
54
|
}
|
|
@@ -2,21 +2,24 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const core_1 = require("@oclif/core");
|
|
4
4
|
const base_1 = require("../../base");
|
|
5
|
+
const presets_1 = require("../../lib/presets");
|
|
5
6
|
class OptPreset extends base_1.BaseCommand {
|
|
6
|
-
static description = 'Apply an optimization
|
|
7
|
+
static description = 'Apply an optimization mode for the whole org (maximum, balanced, max_savings, lowest_latency, reliability_first).';
|
|
7
8
|
static args = {
|
|
8
9
|
preset: core_1.Args.string({
|
|
9
|
-
description: '
|
|
10
|
+
description: 'Mode to apply',
|
|
10
11
|
required: true,
|
|
11
|
-
options:
|
|
12
|
+
options: presets_1.PRESET_VALUES,
|
|
12
13
|
}),
|
|
13
14
|
};
|
|
14
15
|
async run() {
|
|
15
16
|
const { args, flags } = await this.parse(OptPreset);
|
|
16
17
|
this.requireToken(flags);
|
|
17
18
|
const res = await this.api(flags).optPatch({ default_mode: args.preset });
|
|
18
|
-
if (!this.jsonEnabled())
|
|
19
|
-
|
|
19
|
+
if (!this.jsonEnabled()) {
|
|
20
|
+
const card = (0, presets_1.presetCard)(args.preset);
|
|
21
|
+
this.log(`Optimization mode set to "${args.preset}".${card ? ` ${card.blurb}` : ''}`);
|
|
22
|
+
}
|
|
20
23
|
return res;
|
|
21
24
|
}
|
|
22
25
|
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const picocolors_1 = __importDefault(require("picocolors"));
|
|
3
7
|
const base_1 = require("../../base");
|
|
8
|
+
const presets_1 = require("../../lib/presets");
|
|
4
9
|
class OptShow extends base_1.BaseCommand {
|
|
5
|
-
static description = 'Show the current optimization
|
|
10
|
+
static description = 'Show the current optimization mode and every layer running on your requests.';
|
|
6
11
|
async run() {
|
|
7
12
|
const { flags } = await this.parse(OptShow);
|
|
8
13
|
this.requireToken(flags);
|
|
@@ -10,14 +15,23 @@ class OptShow extends base_1.BaseCommand {
|
|
|
10
15
|
if (this.jsonEnabled())
|
|
11
16
|
return res;
|
|
12
17
|
const settings = res.settings ?? res;
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
const mode = settings.default_mode ?? '-';
|
|
19
|
+
const card = (0, presets_1.presetCard)(mode);
|
|
20
|
+
this.log('');
|
|
21
|
+
this.log(` ${picocolors_1.default.bold('Optimization mode')} ${picocolors_1.default.cyan(mode)}${card ? picocolors_1.default.dim(` ${card.hint}`) : ''}`);
|
|
22
|
+
this.log(` ${picocolors_1.default.bold('Cache mode')} ${picocolors_1.default.cyan(settings.cache_mode ?? '-')}`);
|
|
15
23
|
const layers = Array.isArray(res.layers) ? res.layers : [];
|
|
16
24
|
if (layers.length) {
|
|
17
|
-
const
|
|
18
|
-
this.log(`
|
|
25
|
+
const on = layers.filter((l) => l.enabled).length;
|
|
26
|
+
this.log(` ${picocolors_1.default.bold('Active layers')} ${picocolors_1.default.green(String(on))}${picocolors_1.default.dim(`/${layers.length}`)} running on every request`);
|
|
27
|
+
this.log('');
|
|
28
|
+
for (const l of layers) {
|
|
29
|
+
const name = String(l.title ?? l.plain_label ?? l.key ?? 'layer');
|
|
30
|
+
this.log(l.enabled ? ` ${picocolors_1.default.green('●')} ${name}` : ` ${picocolors_1.default.dim('○')} ${picocolors_1.default.dim(name)}`);
|
|
31
|
+
}
|
|
19
32
|
}
|
|
20
|
-
this.log('
|
|
33
|
+
this.log('');
|
|
34
|
+
this.log(picocolors_1.default.dim(' Run with --json for the full settings object.'));
|
|
21
35
|
return res;
|
|
22
36
|
}
|
|
23
37
|
}
|
package/dist/commands/setup.js
CHANGED
|
@@ -37,6 +37,7 @@ const core_1 = require("@oclif/core");
|
|
|
37
37
|
const base_1 = require("../base");
|
|
38
38
|
const auth_1 = require("../lib/auth");
|
|
39
39
|
const credentials_1 = require("../lib/credentials");
|
|
40
|
+
const presets_1 = require("../lib/presets");
|
|
40
41
|
const providers_1 = require("../lib/providers");
|
|
41
42
|
const tty_1 = require("../lib/tty");
|
|
42
43
|
const ui = __importStar(require("../lib/ui"));
|
|
@@ -50,9 +51,9 @@ class Setup extends base_1.BaseCommand {
|
|
|
50
51
|
'provider-base-url': core_1.Flags.string({ description: 'Custom provider base URL (for --provider custom)' }),
|
|
51
52
|
mode: core_1.Flags.string({ description: 'Gateway mode override (byte_compatible/byte_messages/byte_generative)' }),
|
|
52
53
|
preset: core_1.Flags.string({
|
|
53
|
-
description: 'Optimization
|
|
54
|
-
options: ['balanced', 'max_savings', 'lowest_latency', 'reliability_first'],
|
|
55
|
-
default: '
|
|
54
|
+
description: 'Optimization mode',
|
|
55
|
+
options: ['maximum', 'balanced', 'max_savings', 'lowest_latency', 'reliability_first'],
|
|
56
|
+
default: 'maximum',
|
|
56
57
|
}),
|
|
57
58
|
'key-name': core_1.Flags.string({ description: 'Name for the Byte key', default: 'cli' }),
|
|
58
59
|
'skip-provider': core_1.Flags.boolean({ description: 'Skip connecting a provider' }),
|
|
@@ -82,37 +83,46 @@ class Setup extends base_1.BaseCommand {
|
|
|
82
83
|
await (0, auth_1.deviceLogin)({ baseUrl: base, profile });
|
|
83
84
|
}
|
|
84
85
|
// 2) Connect a provider
|
|
85
|
-
let
|
|
86
|
+
let provider = { status: 'skipped' };
|
|
86
87
|
if (!flags['skip-provider']) {
|
|
87
88
|
const existing = await api.providersList().catch(() => ({ connections: [] }));
|
|
88
89
|
const conns = existing.connections ?? (Array.isArray(existing) ? existing : []);
|
|
89
90
|
let addNew = true;
|
|
90
91
|
if (conns.length) {
|
|
91
|
-
addNew =
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
addNew =
|
|
93
|
+
(await ui.select({
|
|
94
|
+
message: 'Provider',
|
|
95
|
+
options: [
|
|
96
|
+
{ value: 'existing', label: `Use existing connection (${conns.length})` },
|
|
97
|
+
{ value: 'new', label: 'Connect a new provider' },
|
|
98
|
+
],
|
|
99
|
+
})) === 'new';
|
|
100
|
+
if (!addNew)
|
|
101
|
+
provider = { status: 'connected' };
|
|
98
102
|
}
|
|
99
103
|
if (addNew) {
|
|
100
|
-
|
|
104
|
+
provider = await this.connectProvider(api, flags);
|
|
101
105
|
}
|
|
102
106
|
}
|
|
103
|
-
// 3)
|
|
107
|
+
// 3) Choose the optimization mode
|
|
108
|
+
const preset = await ui.select({
|
|
109
|
+
message: 'Optimization mode',
|
|
110
|
+
options: presets_1.PRESET_CARDS.map((c) => ({ value: c.value, label: c.label, hint: c.hint })),
|
|
111
|
+
initialValue: presets_1.PRESET_CARDS.some((c) => c.value === flags.preset) ? flags.preset : 'maximum',
|
|
112
|
+
});
|
|
113
|
+
// 4) Create a Byte key
|
|
104
114
|
let byteKey = (0, credentials_1.getByteKey)(profile);
|
|
105
115
|
if (!byteKey) {
|
|
106
116
|
const name = await ui.text({ message: 'Name this Byte key', defaultValue: flags['key-name'] || 'cli' });
|
|
107
|
-
const res = await api.keysCreate({ name: name || 'cli', preset
|
|
108
|
-
|
|
109
|
-
(0, credentials_1.setByteKey)(profile,
|
|
110
|
-
await ui.revealSecret('Your Byte API key',
|
|
117
|
+
const res = await api.keysCreate({ name: name || 'cli', preset });
|
|
118
|
+
byteKey = String(res.key);
|
|
119
|
+
(0, credentials_1.setByteKey)(profile, byteKey);
|
|
120
|
+
await ui.revealSecret('Your Byte API key', byteKey);
|
|
111
121
|
}
|
|
112
|
-
//
|
|
113
|
-
await api.optPatch({ default_mode:
|
|
114
|
-
await ui.log.success(`Optimizations: ${
|
|
115
|
-
//
|
|
122
|
+
// 5) Apply the optimization mode to the org
|
|
123
|
+
await api.optPatch({ default_mode: preset }).catch(() => undefined);
|
|
124
|
+
await ui.log.success(`Optimizations: ${preset} mode applied — every layer this mode enables now runs on each request.`);
|
|
125
|
+
// 6) Connect a tool
|
|
116
126
|
const connect = await ui.select({
|
|
117
127
|
message: 'Connect a coding tool now?',
|
|
118
128
|
options: [
|
|
@@ -126,11 +136,19 @@ class Setup extends base_1.BaseCommand {
|
|
|
126
136
|
],
|
|
127
137
|
});
|
|
128
138
|
if (connect !== 'skip') {
|
|
129
|
-
const argv = [connect, '--write', '--yes', ...(
|
|
139
|
+
const argv = [connect, '--write', '--yes', ...(provider.alias ? ['--model', provider.alias] : [])];
|
|
130
140
|
await this.config.runCommand('integrate', argv);
|
|
131
141
|
}
|
|
132
|
-
|
|
133
|
-
|
|
142
|
+
// Accurate close-out — never claim a provider is wired when it is not.
|
|
143
|
+
const tips = [`${ui.theme.accent('byte run "hello from byte"')}`, `${ui.theme.accent('byte usage')}`];
|
|
144
|
+
if (provider.status === 'saved') {
|
|
145
|
+
await ui.note(`Your provider is saved but no model loaded yet. Fix the key/URL, then run ${ui.theme.accent('byte providers test')}.`, 'One thing left');
|
|
146
|
+
}
|
|
147
|
+
else if (provider.status === 'skipped' && !flags['skip-provider']) {
|
|
148
|
+
tips.unshift(`${ui.theme.accent('byte providers add')}`);
|
|
149
|
+
}
|
|
150
|
+
await ui.outro(`${ui.theme.ok('Setup complete.')} Next: ${tips.join(' · ')}`);
|
|
151
|
+
return { status: 'setup_complete', profile, model: provider.alias, provider: provider.status, preset };
|
|
134
152
|
}
|
|
135
153
|
async connectProvider(api, flags) {
|
|
136
154
|
const providerId = await ui.select({
|
|
@@ -151,27 +169,67 @@ class Setup extends base_1.BaseCommand {
|
|
|
151
169
|
const key = await ui.password({ message: `${preset.label} API key`, validate: (v) => (v.trim() ? undefined : 'Required') });
|
|
152
170
|
const spin = await ui.spinner();
|
|
153
171
|
spin.start(`Connecting ${preset.label}…`);
|
|
172
|
+
let res;
|
|
154
173
|
try {
|
|
155
|
-
|
|
156
|
-
const models = res.fetch_result?.models ?? res.models ?? [];
|
|
157
|
-
spin.stop(`Connected ${preset.label} — ${models.length} model(s) imported.`);
|
|
158
|
-
if (models.length) {
|
|
159
|
-
return ui.select({
|
|
160
|
-
message: 'Default model',
|
|
161
|
-
options: models.slice(0, 50).map((m) => ({ value: m.byte_alias, label: m.byte_alias, hint: m.model_id })),
|
|
162
|
-
});
|
|
163
|
-
}
|
|
174
|
+
res = await api.providersAdd({ api_key: key, gateway_mode: flags.mode || preset.gateway_mode, base_url: baseUrl, auto_fetch: true });
|
|
164
175
|
}
|
|
165
176
|
catch (err) {
|
|
166
|
-
spin.stop('
|
|
177
|
+
spin.stop('Could not save the provider connection.');
|
|
167
178
|
await ui.log.error(err?.message ?? String(err));
|
|
179
|
+
return { status: 'failed' };
|
|
180
|
+
}
|
|
181
|
+
const connId = res.id ?? res.connection?.id;
|
|
182
|
+
let fetchResult = res.fetch_result ?? {};
|
|
183
|
+
let models = res.models ?? fetchResult.models ?? [];
|
|
184
|
+
// Retry / keep / skip loop — a failed model import never silently passes as success.
|
|
185
|
+
for (;;) {
|
|
186
|
+
const failed = fetchResult.status === 'error' || Boolean(res.fetch_error) || Boolean(fetchResult.last_error);
|
|
187
|
+
if (!failed || models.length) {
|
|
188
|
+
spin.stop(`Connected ${preset.label} — ${models.length} model(s) imported.`);
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
spin.stop(`Saved ${preset.label}, but its models could not be loaded.`);
|
|
192
|
+
await ui.log.warn(String(fetchResult.last_error || res.fetch_error || 'The provider URL or key looks unreachable right now.'));
|
|
193
|
+
const choice = await ui.select({
|
|
194
|
+
message: 'What would you like to do?',
|
|
195
|
+
options: [
|
|
196
|
+
{ value: 'retry', label: 'Re-enter the key and retry' },
|
|
197
|
+
{ value: 'keep', label: 'Keep it — I will pick a model later' },
|
|
198
|
+
{ value: 'skip', label: 'Skip the provider for now' },
|
|
199
|
+
],
|
|
200
|
+
});
|
|
201
|
+
if (choice !== 'retry' || !connId)
|
|
202
|
+
return { status: choice === 'skip' ? 'skipped' : 'saved' };
|
|
203
|
+
const retryKey = await ui.password({ message: `${preset.label} API key`, validate: (v) => (v.trim() ? undefined : 'Required') });
|
|
204
|
+
spin.start('Retrying…');
|
|
205
|
+
try {
|
|
206
|
+
await api.providersRotate(connId, retryKey);
|
|
207
|
+
const test = await api.providersTest(connId);
|
|
208
|
+
models = test.models ?? [];
|
|
209
|
+
fetchResult = { status: models.length ? 'ok' : 'error', last_error: models.length ? null : 'No models returned.' };
|
|
210
|
+
res = { ...res, fetch_error: undefined };
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
models = [];
|
|
214
|
+
fetchResult = { status: 'error', last_error: err?.message ?? String(err) };
|
|
215
|
+
res = { ...res, fetch_error: undefined };
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (models.length) {
|
|
219
|
+
const alias = await ui.select({
|
|
220
|
+
message: 'Default model',
|
|
221
|
+
options: models.slice(0, 50).map((m) => ({ value: m.byte_alias, label: m.byte_alias, hint: m.model_id })),
|
|
222
|
+
});
|
|
223
|
+
return { alias, status: 'connected' };
|
|
168
224
|
}
|
|
169
|
-
return
|
|
225
|
+
return { status: 'saved' };
|
|
170
226
|
}
|
|
171
227
|
async runNonInteractive(flags, profile, base) {
|
|
172
228
|
this.requireToken(flags);
|
|
173
229
|
const api = this.api(flags);
|
|
174
230
|
let imported = 0;
|
|
231
|
+
let providerStatus = flags['skip-provider'] ? 'skipped' : 'skipped';
|
|
232
|
+
let providerError;
|
|
175
233
|
let chosenAlias;
|
|
176
234
|
const key = flags['provider-key'];
|
|
177
235
|
if (key && !flags['skip-provider']) {
|
|
@@ -182,20 +240,38 @@ class Setup extends base_1.BaseCommand {
|
|
|
182
240
|
return this.error('Provide --provider <preset> or --provider-base-url for the upstream provider.', { exit: 2 });
|
|
183
241
|
}
|
|
184
242
|
const res = await api.providersAdd({ api_key: key, gateway_mode: mode, base_url: baseUrl, auto_fetch: true });
|
|
185
|
-
|
|
186
|
-
|
|
243
|
+
const fetchResult = res.fetch_result ?? {};
|
|
244
|
+
imported = fetchResult.imported ?? res.imported ?? 0;
|
|
245
|
+
chosenAlias = res.models?.[0]?.byte_alias;
|
|
246
|
+
if (fetchResult.status === 'error' || res.fetch_error) {
|
|
247
|
+
providerStatus = 'saved';
|
|
248
|
+
providerError = String(fetchResult.last_error || res.fetch_error);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
providerStatus = 'connected';
|
|
252
|
+
}
|
|
187
253
|
}
|
|
188
254
|
await api.optPatch({ default_mode: flags.preset }).catch(() => undefined);
|
|
189
255
|
const keyRes = await api.keysCreate({ name: flags['key-name'] || 'cli', preset: flags.preset });
|
|
190
256
|
(0, credentials_1.setByteKey)(profile, keyRes.key);
|
|
191
257
|
if (flags.connect) {
|
|
192
|
-
|
|
258
|
+
const argv = [flags.connect, '--write', '--yes', ...(chosenAlias ? ['--model', chosenAlias] : [])];
|
|
259
|
+
await this.config.runCommand('integrate', argv);
|
|
193
260
|
}
|
|
194
261
|
if (!this.jsonEnabled()) {
|
|
195
262
|
this.log(`Byte key: ${keyRes.key}`);
|
|
196
263
|
this.log(`Provider models imported: ${imported}`);
|
|
264
|
+
if (providerStatus === 'saved')
|
|
265
|
+
this.log(`Provider saved but models not loaded: ${providerError ?? 'unreachable'}. Run \`byte providers test\`.`);
|
|
197
266
|
}
|
|
198
|
-
return {
|
|
267
|
+
return {
|
|
268
|
+
byte_key: keyRes.key,
|
|
269
|
+
provider_models_imported: imported,
|
|
270
|
+
provider: providerStatus,
|
|
271
|
+
provider_error: providerError,
|
|
272
|
+
preset: flags.preset,
|
|
273
|
+
model: chosenAlias,
|
|
274
|
+
};
|
|
199
275
|
}
|
|
200
276
|
}
|
|
201
277
|
exports.default = Setup;
|
package/dist/commands/usage.js
CHANGED
|
@@ -16,16 +16,40 @@ class Usage extends base_1.BaseCommand {
|
|
|
16
16
|
return res;
|
|
17
17
|
const points = res.series ?? res.points ?? (Array.isArray(res) ? res : []);
|
|
18
18
|
if (!points.length) {
|
|
19
|
-
this.log('No usage recorded yet.');
|
|
19
|
+
this.log('No usage recorded yet. Send a request through your Byte key, then check back.');
|
|
20
20
|
return res;
|
|
21
21
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
const num = (v) => {
|
|
23
|
+
const n = typeof v === 'number' ? v : Number(v);
|
|
24
|
+
return Number.isFinite(n) ? n : 0;
|
|
25
|
+
};
|
|
26
|
+
const tokensOf = (p) => num(p.tokens_in) + num(p.tokens_out);
|
|
27
|
+
const rows = points.map((p) => [
|
|
28
|
+
(0, output_1.fmtBucket)(p.timestamp ?? p.bucket ?? p.date),
|
|
29
|
+
(0, output_1.fmtNum)(num(p.requests)),
|
|
30
|
+
(0, output_1.fmtNum)(num(p.cached)),
|
|
31
|
+
(0, output_1.fmtNum)(tokensOf(p)),
|
|
32
|
+
(0, output_1.fmtUsd)(num(p.cost_byte_usd ?? p.cost_usd)),
|
|
33
|
+
(0, output_1.fmtUsd)(num(p.savings_usd)),
|
|
34
|
+
]);
|
|
35
|
+
const totals = {
|
|
36
|
+
requests: points.reduce((a, p) => a + num(p.requests), 0),
|
|
37
|
+
cached: points.reduce((a, p) => a + num(p.cached), 0),
|
|
38
|
+
tokens: points.reduce((a, p) => a + tokensOf(p), 0),
|
|
39
|
+
cost: points.reduce((a, p) => a + num(p.cost_byte_usd ?? p.cost_usd), 0),
|
|
40
|
+
savings: points.reduce((a, p) => a + num(p.savings_usd), 0),
|
|
41
|
+
raw: points.reduce((a, p) => a + num(p.cost_raw_usd), 0),
|
|
42
|
+
};
|
|
43
|
+
rows.push(['Total', (0, output_1.fmtNum)(totals.requests), (0, output_1.fmtNum)(totals.cached), (0, output_1.fmtNum)(totals.tokens), (0, output_1.fmtUsd)(totals.cost), (0, output_1.fmtUsd)(totals.savings)]);
|
|
44
|
+
this.log((0, output_1.renderTable)(['Bucket', 'Requests', 'Cached', 'Tokens', 'Cost', 'Savings'], rows));
|
|
45
|
+
const plain = Boolean(process.env.NO_COLOR) || process.env.BYTE_PLAIN === '1';
|
|
46
|
+
const spark = (0, output_1.sparkline)(points.map((p) => num(p.savings_usd)), plain);
|
|
47
|
+
if (spark)
|
|
48
|
+
this.log(`\nSavings trend ${spark}`);
|
|
49
|
+
const savedPct = totals.raw > 0 ? (0, output_1.pct)(totals.savings / totals.raw) : null;
|
|
50
|
+
const hitRate = totals.requests > 0 ? (0, output_1.pct)(totals.cached / totals.requests) : null;
|
|
51
|
+
this.log(`Saved ${(0, output_1.fmtUsd)(totals.savings)}${savedPct ? ` (${savedPct})` : ''} across ${(0, output_1.fmtNum)(totals.requests)} requests` +
|
|
52
|
+
`${hitRate ? `, cache hit rate ${hitRate}` : ''}.`);
|
|
29
53
|
return res;
|
|
30
54
|
}
|
|
31
55
|
}
|
package/dist/hooks/init/home.js
CHANGED
|
@@ -33,13 +33,15 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const api_1 = require("../../lib/api");
|
|
36
37
|
const config_1 = require("../../lib/config");
|
|
37
38
|
const credentials_1 = require("../../lib/credentials");
|
|
39
|
+
const home_1 = require("../../lib/home");
|
|
38
40
|
const tty_1 = require("../../lib/tty");
|
|
39
41
|
const ui = __importStar(require("../../lib/ui"));
|
|
40
42
|
// oclif runs init hooks before it would print help. Bare `byte` (no command) in a real
|
|
41
|
-
// terminal opens an interactive
|
|
42
|
-
// returns and lets oclif behave normally.
|
|
43
|
+
// terminal opens the live home dashboard + an interactive menu; anything else (a command,
|
|
44
|
+
// a pipe, CI, --help) returns and lets oclif behave normally.
|
|
43
45
|
const hook = async function (opts) {
|
|
44
46
|
if (opts.id !== undefined)
|
|
45
47
|
return;
|
|
@@ -50,9 +52,19 @@ const hook = async function (opts) {
|
|
|
50
52
|
return;
|
|
51
53
|
const profile = (0, config_1.profileName)();
|
|
52
54
|
const prof = (0, config_1.getProfile)(profile);
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
55
|
+
const token = (0, credentials_1.getToken)(profile);
|
|
56
|
+
const signedIn = Boolean(token);
|
|
57
|
+
const byteKey = (0, credentials_1.getByteKey)(profile);
|
|
58
|
+
// Live, branded home dashboard. Every cell is best-effort: an unreachable service or a
|
|
59
|
+
// signed-out profile degrades to a dim placeholder rather than blocking the screen.
|
|
60
|
+
let data = { savingsSeries: [], savedTotal: 0, tokensTotal: 0, byteKey, gateway: 'unknown' };
|
|
61
|
+
if (signedIn) {
|
|
62
|
+
const api = new api_1.ByteApi(prof.base_url || config_1.DEFAULT_BASE_URL, token);
|
|
63
|
+
const loaded = await (0, home_1.loadHomeData)(api, byteKey, profile).catch(() => undefined);
|
|
64
|
+
if (loaded)
|
|
65
|
+
data = { ...data, ...loaded };
|
|
66
|
+
}
|
|
67
|
+
this.log(`\n${(0, home_1.renderHome)(data, signedIn)}\n`);
|
|
56
68
|
const choice = await ui.select({
|
|
57
69
|
message: 'What would you like to do?',
|
|
58
70
|
initialValue: signedIn ? 'setup' : 'login',
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.friendlyError = friendlyError;
|
|
7
|
+
exports.renderErrorPanel = renderErrorPanel;
|
|
8
|
+
const picocolors_1 = __importDefault(require("picocolors"));
|
|
9
|
+
// Translates a ByteError (HTTP status + BYTE_* / provider detail code) into plain
|
|
10
|
+
// language a first-time user can act on: what happened, why, and the exact next
|
|
11
|
+
// command. The goal is that no error ever leaves someone stuck.
|
|
12
|
+
function friendlyError(err) {
|
|
13
|
+
const code = (err.code || '').toUpperCase();
|
|
14
|
+
const status = err.status;
|
|
15
|
+
const byCode = {
|
|
16
|
+
BYTE_AUTH_REQUIRED: {
|
|
17
|
+
issue: 'You are not signed in yet.',
|
|
18
|
+
why: 'This command needs your Byte account.',
|
|
19
|
+
next: 'byte login',
|
|
20
|
+
},
|
|
21
|
+
BYTE_KEY_REQUIRED: {
|
|
22
|
+
issue: 'This profile has no Byte API key.',
|
|
23
|
+
why: 'Requests run through a Byte key so every optimization applies automatically.',
|
|
24
|
+
next: 'byte keys create',
|
|
25
|
+
},
|
|
26
|
+
BYTE_API_OFFLINE: {
|
|
27
|
+
issue: 'Could not reach the Byte service.',
|
|
28
|
+
why: 'You may be offline, or the base URL is pointed somewhere unexpected.',
|
|
29
|
+
next: 'byte doctor',
|
|
30
|
+
},
|
|
31
|
+
BYTE_MODEL_FETCH_FAILED: {
|
|
32
|
+
issue: 'Your provider was saved, but its model list could not be loaded.',
|
|
33
|
+
why: 'The provider URL or key looks unreachable right now — your connection is still saved.',
|
|
34
|
+
next: 'byte providers test',
|
|
35
|
+
},
|
|
36
|
+
BYTE_DEVICE_DENIED: {
|
|
37
|
+
issue: 'Sign-in was declined in the browser.',
|
|
38
|
+
why: 'The device approval was rejected or closed.',
|
|
39
|
+
next: 'byte login',
|
|
40
|
+
},
|
|
41
|
+
BYTE_DEVICE_EXPIRED: {
|
|
42
|
+
issue: 'The sign-in code expired.',
|
|
43
|
+
why: 'It was not approved in time.',
|
|
44
|
+
next: 'byte login',
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
if (byCode[code])
|
|
48
|
+
return byCode[code];
|
|
49
|
+
if (status === 0) {
|
|
50
|
+
return { issue: 'Could not reach the Byte service.', why: 'You may be offline.', next: 'byte doctor' };
|
|
51
|
+
}
|
|
52
|
+
if (status === 401 || status === 403) {
|
|
53
|
+
return { issue: 'Your session or key was rejected.', why: 'It may have expired, been revoked, or be missing a scope.', next: 'byte login' };
|
|
54
|
+
}
|
|
55
|
+
if (status === 404) {
|
|
56
|
+
return {
|
|
57
|
+
issue: 'That route or model was not found.',
|
|
58
|
+
why: 'The model may not be wired to your key, or the surface (e.g. /v1/messages) is not enabled for it.',
|
|
59
|
+
next: 'byte doctor',
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (status === 429) {
|
|
63
|
+
return { issue: 'Rate limit reached.', why: 'Too many requests landed in a short window.', next: 'wait a few seconds, then retry' };
|
|
64
|
+
}
|
|
65
|
+
if (status && status >= 500) {
|
|
66
|
+
return {
|
|
67
|
+
issue: 'The service hit an error finishing your request.',
|
|
68
|
+
why: 'This is on the Byte side, not your input — your key and provider are fine.',
|
|
69
|
+
next: 'byte doctor (then retry shortly)',
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return { issue: err.message || 'Something went wrong.', why: `Error code: ${err.code}`, next: 'byte doctor' };
|
|
73
|
+
}
|
|
74
|
+
// A compact, left-accent error panel. picocolors auto-disables under NO_COLOR, so this
|
|
75
|
+
// degrades to clean monochrome text in plain terminals.
|
|
76
|
+
function renderErrorPanel(f) {
|
|
77
|
+
const bar = picocolors_1.default.red('▌');
|
|
78
|
+
const lines = [
|
|
79
|
+
`${bar} ${picocolors_1.default.bold(picocolors_1.default.red('Byte hit a problem'))}`,
|
|
80
|
+
`${bar} ${f.issue}`,
|
|
81
|
+
`${bar}`,
|
|
82
|
+
`${bar} ${picocolors_1.default.dim('Why ')} ${f.why}`,
|
|
83
|
+
`${bar} ${picocolors_1.default.dim('Next')} ${picocolors_1.default.cyan(f.next)}`,
|
|
84
|
+
];
|
|
85
|
+
return `\n${lines.join('\n')}\n`;
|
|
86
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ByteApi } from './api';
|
|
2
|
+
export declare function wordmark(): string;
|
|
3
|
+
export interface HomeData {
|
|
4
|
+
email?: string;
|
|
5
|
+
org?: string;
|
|
6
|
+
providers?: number;
|
|
7
|
+
models?: number;
|
|
8
|
+
byteKey?: string;
|
|
9
|
+
gateway?: 'healthy' | 'down' | 'unknown';
|
|
10
|
+
savingsSeries: number[];
|
|
11
|
+
savedTotal: number;
|
|
12
|
+
tokensTotal: number;
|
|
13
|
+
hitRate?: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function loadHomeData(api: ByteApi, byteKey: string | undefined, fallbackId: string): Promise<HomeData>;
|
|
16
|
+
export declare function renderHome(data: HomeData, signedIn: boolean): string;
|
package/dist/lib/home.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.wordmark = wordmark;
|
|
7
|
+
exports.loadHomeData = loadHomeData;
|
|
8
|
+
exports.renderHome = renderHome;
|
|
9
|
+
const picocolors_1 = __importDefault(require("picocolors"));
|
|
10
|
+
const output_1 = require("./output");
|
|
11
|
+
const ANSI = /\x1b\[[0-9;]*m/g;
|
|
12
|
+
const visLen = (s) => s.replace(ANSI, '').length;
|
|
13
|
+
const plainMode = () => Boolean(process.env.NO_COLOR) || process.env.BYTE_PLAIN === '1';
|
|
14
|
+
// Cyan→blue gradient wordmark. picocolors has no true gradient, so we step the letters
|
|
15
|
+
// across a cyan→blue ramp — a recognizable Byte signature that degrades to plain text.
|
|
16
|
+
function wordmark() {
|
|
17
|
+
const mark = `${picocolors_1.default.dim('›')}${picocolors_1.default.bold(picocolors_1.default.cyanBright('b'))}${picocolors_1.default.dim('_')}`;
|
|
18
|
+
if (plainMode())
|
|
19
|
+
return `${mark} BYTE`;
|
|
20
|
+
const ramp = [picocolors_1.default.cyanBright, picocolors_1.default.cyan, picocolors_1.default.blue, picocolors_1.default.blueBright];
|
|
21
|
+
const name = 'BYTE'
|
|
22
|
+
.split('')
|
|
23
|
+
.map((ch, i) => picocolors_1.default.bold(ramp[i % ramp.length](ch)))
|
|
24
|
+
.join('');
|
|
25
|
+
return `${mark} ${name}`;
|
|
26
|
+
}
|
|
27
|
+
function box(lines, pad = 2) {
|
|
28
|
+
const inner = Math.max(...lines.map(visLen));
|
|
29
|
+
const horiz = '─'.repeat(inner + pad * 2);
|
|
30
|
+
const body = lines.map((l) => `${picocolors_1.default.dim('│')}${' '.repeat(pad)}${l}${' '.repeat(inner - visLen(l) + pad)}${picocolors_1.default.dim('│')}`);
|
|
31
|
+
return [picocolors_1.default.dim(`╭${horiz}╮`), ...body, picocolors_1.default.dim(`╰${horiz}╯`)].join('\n');
|
|
32
|
+
}
|
|
33
|
+
function withTimeout(p, ms) {
|
|
34
|
+
return new Promise((resolve) => {
|
|
35
|
+
const timer = setTimeout(() => resolve(undefined), ms);
|
|
36
|
+
if (typeof timer.unref === 'function')
|
|
37
|
+
timer.unref();
|
|
38
|
+
p.then((v) => {
|
|
39
|
+
clearTimeout(timer);
|
|
40
|
+
resolve(v);
|
|
41
|
+
}, () => {
|
|
42
|
+
clearTimeout(timer);
|
|
43
|
+
resolve(undefined);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
// Fetches everything bare `byte` shows, in parallel, each guarded by a short timeout so a
|
|
48
|
+
// slow or unreachable service never blocks the home screen — missing cells just render dim.
|
|
49
|
+
async function loadHomeData(api, byteKey, fallbackId) {
|
|
50
|
+
const [me, provs, usage, health] = await Promise.all([
|
|
51
|
+
withTimeout(api.me(), 4000),
|
|
52
|
+
withTimeout(api.providersList(), 4000),
|
|
53
|
+
withTimeout(api.usage('day'), 4000),
|
|
54
|
+
withTimeout(api.health(), 3000),
|
|
55
|
+
]);
|
|
56
|
+
const conns = provs?.connections ?? (Array.isArray(provs) ? provs : []);
|
|
57
|
+
const models = conns.reduce((a, c) => a + (Number(c.models ?? c.model_count ?? 0) || 0), 0);
|
|
58
|
+
const series = usage?.series ?? [];
|
|
59
|
+
const num = (v) => {
|
|
60
|
+
const n = typeof v === 'number' ? v : Number(v);
|
|
61
|
+
return Number.isFinite(n) ? n : 0;
|
|
62
|
+
};
|
|
63
|
+
const requests = series.reduce((a, p) => a + num(p.requests), 0);
|
|
64
|
+
const cached = series.reduce((a, p) => a + num(p.cached), 0);
|
|
65
|
+
return {
|
|
66
|
+
email: me?.user?.email,
|
|
67
|
+
org: me?.org?.name ?? me?.user?.org_name,
|
|
68
|
+
providers: conns.length || undefined,
|
|
69
|
+
models: models || undefined,
|
|
70
|
+
byteKey,
|
|
71
|
+
gateway: health ? (health.status === 'ok' || health.ok ? 'healthy' : 'down') : 'unknown',
|
|
72
|
+
savingsSeries: series.map((p) => num(p.savings_usd)),
|
|
73
|
+
savedTotal: series.reduce((a, p) => a + num(p.savings_usd), 0),
|
|
74
|
+
tokensTotal: series.reduce((a, p) => a + num(p.tokens_in) + num(p.tokens_out), 0),
|
|
75
|
+
hitRate: requests > 0 ? cached / requests : undefined,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function renderHome(data, signedIn) {
|
|
79
|
+
const dim = picocolors_1.default.dim;
|
|
80
|
+
const label = (s) => dim(s.padEnd(10));
|
|
81
|
+
const dash = dim('—');
|
|
82
|
+
const gatewayDot = data.gateway === 'healthy' ? `${picocolors_1.default.green('●')} healthy` : data.gateway === 'down' ? `${picocolors_1.default.red('●')} unreachable` : dim('● unknown');
|
|
83
|
+
const lines = [
|
|
84
|
+
`${wordmark()}${' '.repeat(6)}${dim('optimization gateway')}`,
|
|
85
|
+
'',
|
|
86
|
+
`${label('Account')}${signedIn ? `${data.email ?? 'you'}${data.org ? ` ${dim('·')} ${data.org}` : ''}` : picocolors_1.default.yellow('not signed in')}`,
|
|
87
|
+
`${label('Providers')}${data.providers ? `${data.providers} connected${data.models ? ` ${dim('·')} ${data.models} models` : ''}` : dash}`,
|
|
88
|
+
`${label('Byte key')}${data.byteKey ? picocolors_1.default.cyan((0, output_1.maskKey)(data.byteKey)) : dash}`,
|
|
89
|
+
`${label('Gateway')}${gatewayDot}`,
|
|
90
|
+
];
|
|
91
|
+
if (data.savingsSeries.some((v) => v > 0) || data.savedTotal > 0) {
|
|
92
|
+
const spark = (0, output_1.sparkline)(data.savingsSeries, plainMode());
|
|
93
|
+
const summary = `${picocolors_1.default.green((0, output_1.fmtUsd)(data.savedTotal))} saved ${dim('·')} ${(0, output_1.fmtNum)(data.tokensTotal)} tokens${data.hitRate !== undefined ? ` ${dim('·')} ${(0, output_1.pct)(data.hitRate)} cache hits` : ''}`;
|
|
94
|
+
lines.push('', `${label('7-day')}${picocolors_1.default.cyan(spark)} ${summary}`);
|
|
95
|
+
}
|
|
96
|
+
return box(lines);
|
|
97
|
+
}
|
package/dist/lib/output.d.ts
CHANGED
|
@@ -2,3 +2,6 @@ export declare function renderTable(head: string[], rows: Array<Array<string | n
|
|
|
2
2
|
export declare function maskKey(value: string | undefined): string;
|
|
3
3
|
export declare function fmtUsd(value: unknown): string;
|
|
4
4
|
export declare function pct(value: unknown): string;
|
|
5
|
+
export declare function fmtNum(value: unknown): string;
|
|
6
|
+
export declare function fmtBucket(value: unknown): string;
|
|
7
|
+
export declare function sparkline(values: number[], plain?: boolean): string;
|
package/dist/lib/output.js
CHANGED
|
@@ -7,6 +7,9 @@ exports.renderTable = renderTable;
|
|
|
7
7
|
exports.maskKey = maskKey;
|
|
8
8
|
exports.fmtUsd = fmtUsd;
|
|
9
9
|
exports.pct = pct;
|
|
10
|
+
exports.fmtNum = fmtNum;
|
|
11
|
+
exports.fmtBucket = fmtBucket;
|
|
12
|
+
exports.sparkline = sparkline;
|
|
10
13
|
const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
11
14
|
function renderTable(head, rows) {
|
|
12
15
|
const table = new cli_table3_1.default({ head });
|
|
@@ -35,3 +38,43 @@ function pct(value) {
|
|
|
35
38
|
const scaled = n <= 1 ? n * 100 : n;
|
|
36
39
|
return `${scaled.toFixed(1)}%`;
|
|
37
40
|
}
|
|
41
|
+
// 1234 -> "1.2K", 1_500_000 -> "1.5M". Keeps small counts exact.
|
|
42
|
+
function fmtNum(value) {
|
|
43
|
+
const n = typeof value === 'number' ? value : Number(value);
|
|
44
|
+
if (!Number.isFinite(n))
|
|
45
|
+
return '-';
|
|
46
|
+
const abs = Math.abs(n);
|
|
47
|
+
if (abs >= 1_000_000)
|
|
48
|
+
return `${(n / 1_000_000).toFixed(1)}M`;
|
|
49
|
+
if (abs >= 1_000)
|
|
50
|
+
return `${(n / 1_000).toFixed(1)}K`;
|
|
51
|
+
return String(Math.round(n));
|
|
52
|
+
}
|
|
53
|
+
const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
54
|
+
// "2026-05-30" -> "May 30"; "2026-05-30T14:00:00Z" -> "May 30 14:00".
|
|
55
|
+
function fmtBucket(value) {
|
|
56
|
+
if (typeof value !== 'string' || !value)
|
|
57
|
+
return '-';
|
|
58
|
+
const m = value.match(/^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):)?/);
|
|
59
|
+
if (!m)
|
|
60
|
+
return value;
|
|
61
|
+
const mon = MONTHS[Number(m[2]) - 1] ?? m[2];
|
|
62
|
+
const day = String(Number(m[3]));
|
|
63
|
+
return m[4] ? `${mon} ${day} ${m[4]}:00` : `${mon} ${day}`;
|
|
64
|
+
}
|
|
65
|
+
// Unicode block sparkline with an ASCII fallback for legacy terminals (NO_COLOR/conhost).
|
|
66
|
+
function sparkline(values, plain = false) {
|
|
67
|
+
const nums = values.map((v) => (Number.isFinite(v) ? v : 0));
|
|
68
|
+
if (!nums.length)
|
|
69
|
+
return '';
|
|
70
|
+
const ramp = plain ? '.:-=+*#'.split('') : '▁▂▃▄▅▆▇█'.split('');
|
|
71
|
+
const max = Math.max(...nums);
|
|
72
|
+
const min = Math.min(...nums);
|
|
73
|
+
const span = max - min || 1;
|
|
74
|
+
return nums
|
|
75
|
+
.map((v) => {
|
|
76
|
+
const idx = Math.round(((v - min) / span) * (ramp.length - 1));
|
|
77
|
+
return ramp[Math.max(0, Math.min(ramp.length - 1, idx))];
|
|
78
|
+
})
|
|
79
|
+
.join('');
|
|
80
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface PresetCard {
|
|
2
|
+
value: string;
|
|
3
|
+
label: string;
|
|
4
|
+
hint: string;
|
|
5
|
+
blurb: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const PRESET_CARDS: PresetCard[];
|
|
8
|
+
export declare const PRESET_VALUES: string[];
|
|
9
|
+
export declare function presetCard(value: string): PresetCard | undefined;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PRESET_VALUES = exports.PRESET_CARDS = void 0;
|
|
4
|
+
exports.presetCard = presetCard;
|
|
5
|
+
exports.PRESET_CARDS = [
|
|
6
|
+
{
|
|
7
|
+
value: 'maximum',
|
|
8
|
+
label: 'Maximum — everything on (recommended)',
|
|
9
|
+
hint: 'best savings + quality',
|
|
10
|
+
blurb: 'Every production-safe optimization runs on every request: full caching, compression, routing, and quality guards.',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
value: 'max_savings',
|
|
14
|
+
label: 'Maximum savings',
|
|
15
|
+
hint: 'cheapest',
|
|
16
|
+
blurb: 'Leans hardest on caching, compression, and cheaper routing to cut spend the most.',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
value: 'balanced',
|
|
20
|
+
label: 'Balanced',
|
|
21
|
+
hint: 'savings + speed',
|
|
22
|
+
blurb: 'A middle ground: strong savings without the deepest latency-adding passes.',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
value: 'lowest_latency',
|
|
26
|
+
label: 'Lowest latency',
|
|
27
|
+
hint: 'fastest',
|
|
28
|
+
blurb: 'Skips the heavier passes so responses come back as fast as possible.',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
value: 'reliability_first',
|
|
32
|
+
label: 'Reliability first',
|
|
33
|
+
hint: 'most robust',
|
|
34
|
+
blurb: 'Prioritizes verification, repair, and fallbacks over maximum savings.',
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
exports.PRESET_VALUES = exports.PRESET_CARDS.map((c) => c.value);
|
|
38
|
+
function presetCard(value) {
|
|
39
|
+
return exports.PRESET_CARDS.find((c) => c.value === value);
|
|
40
|
+
}
|
package/dist/lib/ui.js
CHANGED
|
@@ -32,8 +32,20 @@ exports.theme = {
|
|
|
32
32
|
muted: (s) => picocolors_1.default.dim(s),
|
|
33
33
|
bold: (s) => picocolors_1.default.bold(s),
|
|
34
34
|
};
|
|
35
|
+
// The Byte signature: a `›b_` prompt glyph and a cyan→blue gradient wordmark. picocolors
|
|
36
|
+
// has no true gradient, so the letters step across a cyan→blue ramp; degrades to plain
|
|
37
|
+
// text under NO_COLOR/BYTE_PLAIN. Kept in sync with the home dashboard wordmark.
|
|
35
38
|
function banner() {
|
|
36
|
-
|
|
39
|
+
const plain = Boolean(process.env.NO_COLOR) || process.env.BYTE_PLAIN === '1';
|
|
40
|
+
const mark = `${picocolors_1.default.dim('›')}${picocolors_1.default.bold(picocolors_1.default.cyanBright('b'))}${picocolors_1.default.dim('_')}`;
|
|
41
|
+
if (plain)
|
|
42
|
+
return `${mark} BYTE`;
|
|
43
|
+
const ramp = [picocolors_1.default.cyanBright, picocolors_1.default.cyan, picocolors_1.default.blue, picocolors_1.default.blueBright];
|
|
44
|
+
const name = 'BYTE'
|
|
45
|
+
.split('')
|
|
46
|
+
.map((ch, i) => picocolors_1.default.bold(ramp[i % ramp.length](ch)))
|
|
47
|
+
.join('');
|
|
48
|
+
return `${mark} ${name}`;
|
|
37
49
|
}
|
|
38
50
|
function guard(value, c) {
|
|
39
51
|
if (c.isCancel(value)) {
|
package/oclif.manifest.json
CHANGED
|
@@ -609,12 +609,13 @@
|
|
|
609
609
|
"type": "option"
|
|
610
610
|
},
|
|
611
611
|
"preset": {
|
|
612
|
-
"description": "Optimization
|
|
612
|
+
"description": "Optimization mode",
|
|
613
613
|
"name": "preset",
|
|
614
|
-
"default": "
|
|
614
|
+
"default": "maximum",
|
|
615
615
|
"hasDynamicHelp": false,
|
|
616
616
|
"multiple": false,
|
|
617
617
|
"options": [
|
|
618
|
+
"maximum",
|
|
618
619
|
"balanced",
|
|
619
620
|
"max_savings",
|
|
620
621
|
"lowest_latency",
|
|
@@ -1198,118 +1199,23 @@
|
|
|
1198
1199
|
"rotate.js"
|
|
1199
1200
|
]
|
|
1200
1201
|
},
|
|
1201
|
-
"sessions": {
|
|
1202
|
-
"aliases": [],
|
|
1203
|
-
"args": {},
|
|
1204
|
-
"description": "List active terminal sessions (CLI tokens) for the org.",
|
|
1205
|
-
"flags": {
|
|
1206
|
-
"json": {
|
|
1207
|
-
"description": "Format output as json.",
|
|
1208
|
-
"helpGroup": "GLOBAL",
|
|
1209
|
-
"name": "json",
|
|
1210
|
-
"allowNo": false,
|
|
1211
|
-
"type": "boolean"
|
|
1212
|
-
},
|
|
1213
|
-
"profile": {
|
|
1214
|
-
"description": "Configuration profile to use",
|
|
1215
|
-
"env": "BYTE_PROFILE",
|
|
1216
|
-
"name": "profile",
|
|
1217
|
-
"hasDynamicHelp": false,
|
|
1218
|
-
"multiple": false,
|
|
1219
|
-
"type": "option"
|
|
1220
|
-
},
|
|
1221
|
-
"base-url": {
|
|
1222
|
-
"description": "Override the API base URL",
|
|
1223
|
-
"env": "BYTE_BASE_URL",
|
|
1224
|
-
"name": "base-url",
|
|
1225
|
-
"hasDynamicHelp": false,
|
|
1226
|
-
"multiple": false,
|
|
1227
|
-
"type": "option"
|
|
1228
|
-
}
|
|
1229
|
-
},
|
|
1230
|
-
"hasDynamicHelp": false,
|
|
1231
|
-
"hiddenAliases": [],
|
|
1232
|
-
"id": "sessions",
|
|
1233
|
-
"pluginAlias": "@bytevion/cli",
|
|
1234
|
-
"pluginName": "@bytevion/cli",
|
|
1235
|
-
"pluginType": "core",
|
|
1236
|
-
"strict": true,
|
|
1237
|
-
"enableJsonFlag": true,
|
|
1238
|
-
"isESM": false,
|
|
1239
|
-
"relativePath": [
|
|
1240
|
-
"dist",
|
|
1241
|
-
"commands",
|
|
1242
|
-
"sessions",
|
|
1243
|
-
"index.js"
|
|
1244
|
-
]
|
|
1245
|
-
},
|
|
1246
|
-
"sessions:revoke": {
|
|
1247
|
-
"aliases": [],
|
|
1248
|
-
"args": {
|
|
1249
|
-
"id": {
|
|
1250
|
-
"description": "Session id (see `byte sessions`)",
|
|
1251
|
-
"name": "id",
|
|
1252
|
-
"required": true
|
|
1253
|
-
}
|
|
1254
|
-
},
|
|
1255
|
-
"description": "Revoke a terminal session (CLI token) by id.",
|
|
1256
|
-
"flags": {
|
|
1257
|
-
"json": {
|
|
1258
|
-
"description": "Format output as json.",
|
|
1259
|
-
"helpGroup": "GLOBAL",
|
|
1260
|
-
"name": "json",
|
|
1261
|
-
"allowNo": false,
|
|
1262
|
-
"type": "boolean"
|
|
1263
|
-
},
|
|
1264
|
-
"profile": {
|
|
1265
|
-
"description": "Configuration profile to use",
|
|
1266
|
-
"env": "BYTE_PROFILE",
|
|
1267
|
-
"name": "profile",
|
|
1268
|
-
"hasDynamicHelp": false,
|
|
1269
|
-
"multiple": false,
|
|
1270
|
-
"type": "option"
|
|
1271
|
-
},
|
|
1272
|
-
"base-url": {
|
|
1273
|
-
"description": "Override the API base URL",
|
|
1274
|
-
"env": "BYTE_BASE_URL",
|
|
1275
|
-
"name": "base-url",
|
|
1276
|
-
"hasDynamicHelp": false,
|
|
1277
|
-
"multiple": false,
|
|
1278
|
-
"type": "option"
|
|
1279
|
-
}
|
|
1280
|
-
},
|
|
1281
|
-
"hasDynamicHelp": false,
|
|
1282
|
-
"hiddenAliases": [],
|
|
1283
|
-
"id": "sessions:revoke",
|
|
1284
|
-
"pluginAlias": "@bytevion/cli",
|
|
1285
|
-
"pluginName": "@bytevion/cli",
|
|
1286
|
-
"pluginType": "core",
|
|
1287
|
-
"strict": true,
|
|
1288
|
-
"enableJsonFlag": true,
|
|
1289
|
-
"isESM": false,
|
|
1290
|
-
"relativePath": [
|
|
1291
|
-
"dist",
|
|
1292
|
-
"commands",
|
|
1293
|
-
"sessions",
|
|
1294
|
-
"revoke.js"
|
|
1295
|
-
]
|
|
1296
|
-
},
|
|
1297
1202
|
"opt:preset": {
|
|
1298
1203
|
"aliases": [],
|
|
1299
1204
|
"args": {
|
|
1300
1205
|
"preset": {
|
|
1301
|
-
"description": "
|
|
1206
|
+
"description": "Mode to apply",
|
|
1302
1207
|
"name": "preset",
|
|
1303
1208
|
"options": [
|
|
1304
|
-
"
|
|
1209
|
+
"maximum",
|
|
1305
1210
|
"max_savings",
|
|
1211
|
+
"balanced",
|
|
1306
1212
|
"lowest_latency",
|
|
1307
1213
|
"reliability_first"
|
|
1308
1214
|
],
|
|
1309
1215
|
"required": true
|
|
1310
1216
|
}
|
|
1311
1217
|
},
|
|
1312
|
-
"description": "Apply an optimization
|
|
1218
|
+
"description": "Apply an optimization mode for the whole org (maximum, balanced, max_savings, lowest_latency, reliability_first).",
|
|
1313
1219
|
"flags": {
|
|
1314
1220
|
"json": {
|
|
1315
1221
|
"description": "Format output as json.",
|
|
@@ -1420,7 +1326,7 @@
|
|
|
1420
1326
|
"opt:show": {
|
|
1421
1327
|
"aliases": [],
|
|
1422
1328
|
"args": {},
|
|
1423
|
-
"description": "Show the current optimization
|
|
1329
|
+
"description": "Show the current optimization mode and every layer running on your requests.",
|
|
1424
1330
|
"flags": {
|
|
1425
1331
|
"json": {
|
|
1426
1332
|
"description": "Format output as json.",
|
|
@@ -1707,7 +1613,103 @@
|
|
|
1707
1613
|
"providers",
|
|
1708
1614
|
"test.js"
|
|
1709
1615
|
]
|
|
1616
|
+
},
|
|
1617
|
+
"sessions": {
|
|
1618
|
+
"aliases": [],
|
|
1619
|
+
"args": {},
|
|
1620
|
+
"description": "List active terminal sessions (CLI tokens) for the org.",
|
|
1621
|
+
"flags": {
|
|
1622
|
+
"json": {
|
|
1623
|
+
"description": "Format output as json.",
|
|
1624
|
+
"helpGroup": "GLOBAL",
|
|
1625
|
+
"name": "json",
|
|
1626
|
+
"allowNo": false,
|
|
1627
|
+
"type": "boolean"
|
|
1628
|
+
},
|
|
1629
|
+
"profile": {
|
|
1630
|
+
"description": "Configuration profile to use",
|
|
1631
|
+
"env": "BYTE_PROFILE",
|
|
1632
|
+
"name": "profile",
|
|
1633
|
+
"hasDynamicHelp": false,
|
|
1634
|
+
"multiple": false,
|
|
1635
|
+
"type": "option"
|
|
1636
|
+
},
|
|
1637
|
+
"base-url": {
|
|
1638
|
+
"description": "Override the API base URL",
|
|
1639
|
+
"env": "BYTE_BASE_URL",
|
|
1640
|
+
"name": "base-url",
|
|
1641
|
+
"hasDynamicHelp": false,
|
|
1642
|
+
"multiple": false,
|
|
1643
|
+
"type": "option"
|
|
1644
|
+
}
|
|
1645
|
+
},
|
|
1646
|
+
"hasDynamicHelp": false,
|
|
1647
|
+
"hiddenAliases": [],
|
|
1648
|
+
"id": "sessions",
|
|
1649
|
+
"pluginAlias": "@bytevion/cli",
|
|
1650
|
+
"pluginName": "@bytevion/cli",
|
|
1651
|
+
"pluginType": "core",
|
|
1652
|
+
"strict": true,
|
|
1653
|
+
"enableJsonFlag": true,
|
|
1654
|
+
"isESM": false,
|
|
1655
|
+
"relativePath": [
|
|
1656
|
+
"dist",
|
|
1657
|
+
"commands",
|
|
1658
|
+
"sessions",
|
|
1659
|
+
"index.js"
|
|
1660
|
+
]
|
|
1661
|
+
},
|
|
1662
|
+
"sessions:revoke": {
|
|
1663
|
+
"aliases": [],
|
|
1664
|
+
"args": {
|
|
1665
|
+
"id": {
|
|
1666
|
+
"description": "Session id (see `byte sessions`)",
|
|
1667
|
+
"name": "id",
|
|
1668
|
+
"required": true
|
|
1669
|
+
}
|
|
1670
|
+
},
|
|
1671
|
+
"description": "Revoke a terminal session (CLI token) by id.",
|
|
1672
|
+
"flags": {
|
|
1673
|
+
"json": {
|
|
1674
|
+
"description": "Format output as json.",
|
|
1675
|
+
"helpGroup": "GLOBAL",
|
|
1676
|
+
"name": "json",
|
|
1677
|
+
"allowNo": false,
|
|
1678
|
+
"type": "boolean"
|
|
1679
|
+
},
|
|
1680
|
+
"profile": {
|
|
1681
|
+
"description": "Configuration profile to use",
|
|
1682
|
+
"env": "BYTE_PROFILE",
|
|
1683
|
+
"name": "profile",
|
|
1684
|
+
"hasDynamicHelp": false,
|
|
1685
|
+
"multiple": false,
|
|
1686
|
+
"type": "option"
|
|
1687
|
+
},
|
|
1688
|
+
"base-url": {
|
|
1689
|
+
"description": "Override the API base URL",
|
|
1690
|
+
"env": "BYTE_BASE_URL",
|
|
1691
|
+
"name": "base-url",
|
|
1692
|
+
"hasDynamicHelp": false,
|
|
1693
|
+
"multiple": false,
|
|
1694
|
+
"type": "option"
|
|
1695
|
+
}
|
|
1696
|
+
},
|
|
1697
|
+
"hasDynamicHelp": false,
|
|
1698
|
+
"hiddenAliases": [],
|
|
1699
|
+
"id": "sessions:revoke",
|
|
1700
|
+
"pluginAlias": "@bytevion/cli",
|
|
1701
|
+
"pluginName": "@bytevion/cli",
|
|
1702
|
+
"pluginType": "core",
|
|
1703
|
+
"strict": true,
|
|
1704
|
+
"enableJsonFlag": true,
|
|
1705
|
+
"isESM": false,
|
|
1706
|
+
"relativePath": [
|
|
1707
|
+
"dist",
|
|
1708
|
+
"commands",
|
|
1709
|
+
"sessions",
|
|
1710
|
+
"revoke.js"
|
|
1711
|
+
]
|
|
1710
1712
|
}
|
|
1711
1713
|
},
|
|
1712
|
-
"version": "0.
|
|
1714
|
+
"version": "0.3.0"
|
|
1713
1715
|
}
|