@bytevion/cli 0.1.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.
Files changed (41) hide show
  1. package/README.md +66 -0
  2. package/dist/base.js +8 -1
  3. package/dist/commands/compare.d.ts +12 -0
  4. package/dist/commands/compare.js +104 -0
  5. package/dist/commands/integrate.d.ts +1 -0
  6. package/dist/commands/integrate.js +76 -28
  7. package/dist/commands/login.js +40 -59
  8. package/dist/commands/opt/preset.js +8 -5
  9. package/dist/commands/opt/show.js +20 -6
  10. package/dist/commands/providers/add.d.ts +3 -1
  11. package/dist/commands/providers/add.js +102 -20
  12. package/dist/commands/providers/rotate.js +39 -4
  13. package/dist/commands/setup.d.ts +19 -0
  14. package/dist/commands/setup.js +277 -0
  15. package/dist/commands/usage.js +32 -8
  16. package/dist/hooks/init/home.d.ts +3 -0
  17. package/dist/hooks/init/home.js +107 -0
  18. package/dist/lib/api.d.ts +1 -0
  19. package/dist/lib/api.js +60 -0
  20. package/dist/lib/auth.d.ts +9 -0
  21. package/dist/lib/auth.js +100 -0
  22. package/dist/lib/friendly.d.ts +8 -0
  23. package/dist/lib/friendly.js +86 -0
  24. package/dist/lib/home.d.ts +16 -0
  25. package/dist/lib/home.js +97 -0
  26. package/dist/lib/integrations.d.ts +1 -0
  27. package/dist/lib/integrations.js +198 -61
  28. package/dist/lib/output.d.ts +3 -0
  29. package/dist/lib/output.js +43 -0
  30. package/dist/lib/presets.d.ts +9 -0
  31. package/dist/lib/presets.js +40 -0
  32. package/dist/lib/providers.d.ts +13 -0
  33. package/dist/lib/providers.js +39 -0
  34. package/dist/lib/tty.d.ts +1 -0
  35. package/dist/lib/tty.js +11 -0
  36. package/dist/lib/ui.d.ts +60 -0
  37. package/dist/lib/ui.js +122 -0
  38. package/dist/lib/util.d.ts +1 -2
  39. package/dist/lib/util.js +11 -79
  40. package/oclif.manifest.json +215 -16
  41. package/package.json +76 -59
@@ -1,39 +1,121 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  const core_1 = require("@oclif/core");
4
37
  const base_1 = require("../../base");
5
- const util_1 = require("../../lib/util");
38
+ const providers_1 = require("../../lib/providers");
39
+ const tty_1 = require("../../lib/tty");
40
+ const ui = __importStar(require("../../lib/ui"));
6
41
  class ProvidersAdd extends base_1.BaseCommand {
7
- static description = 'Add an upstream provider key (BYOK). Byte stores it encrypted and can auto-import models.';
8
- static examples = ['<%= config.bin %> providers add', '<%= config.bin %> providers add --api-key sk-... --label "OpenAI prod"'];
42
+ static description = 'Connect an upstream provider key (BYOK). Byte stores it encrypted and imports models.';
43
+ static examples = [
44
+ '<%= config.bin %> providers add --provider deepseek --api-key sk-...',
45
+ '<%= config.bin %> providers add --provider custom --provider-base-url https://api.example.com/v1 --api-key ...',
46
+ ];
9
47
  static flags = {
48
+ provider: core_1.Flags.string({ description: 'Provider preset id (openai, deepseek, anthropic, …)' }),
10
49
  'api-key': core_1.Flags.string({ description: 'Upstream provider API key', env: 'BYTE_PROVIDER_KEY' }),
11
- mode: core_1.Flags.string({ description: 'Gateway mode for this provider', default: 'byte_auto' }),
12
- label: core_1.Flags.string({ description: 'A label for this connection' }),
13
- 'no-fetch': core_1.Flags.boolean({ description: 'Skip auto-importing the provider model list' }),
50
+ 'provider-base-url': core_1.Flags.string({ description: 'Custom base URL (for --provider custom or to override)' }),
51
+ mode: core_1.Flags.string({ description: 'Gateway mode (byte_compatible/byte_messages/byte_generative)' }),
52
+ label: core_1.Flags.string({ description: 'Label for this connection' }),
53
+ 'no-fetch': core_1.Flags.boolean({ description: 'Skip auto-importing the model list' }),
14
54
  };
15
55
  async run() {
16
56
  const { flags } = await this.parse(ProvidersAdd);
17
57
  this.requireToken(flags);
58
+ const api = this.api(flags);
59
+ let preset = flags.provider ? (0, providers_1.findPreset)(flags.provider) : undefined;
60
+ if (flags.provider && !preset) {
61
+ return this.error(`Unknown provider "${flags.provider}". Run \`byte providers add --help\` for the list.`, { exit: 2 });
62
+ }
63
+ let baseUrl = flags['provider-base-url'] || preset?.base_url;
64
+ let mode = flags.mode || preset?.gateway_mode || 'byte_compatible';
18
65
  let apiKey = flags['api-key'];
66
+ if ((0, tty_1.interactive)() && !this.jsonEnabled() && (!preset || !baseUrl || !apiKey)) {
67
+ if (!preset) {
68
+ const id = await ui.select({
69
+ message: 'Which provider?',
70
+ options: providers_1.PROVIDER_PRESETS.map((p) => ({ value: p.id, label: p.label, hint: p.advanced ? 'advanced' : undefined })),
71
+ });
72
+ preset = (0, providers_1.findPreset)(id);
73
+ baseUrl = preset.base_url;
74
+ mode = preset.gateway_mode;
75
+ }
76
+ if (preset.custom || !baseUrl) {
77
+ baseUrl = await ui.text({ message: 'Provider base URL', placeholder: 'https://api.example.com/v1', validate: (v) => (v.startsWith('http') ? undefined : 'Must be a URL') });
78
+ }
79
+ if (preset.advanced && preset.note)
80
+ await ui.log.warn(preset.note);
81
+ if (!apiKey)
82
+ apiKey = await ui.password({ message: `${preset.label} API key`, validate: (v) => (v.trim() ? undefined : 'Required') });
83
+ }
19
84
  if (!apiKey)
20
- apiKey = await (0, util_1.promptHidden)('Upstream provider API key: ');
21
- if (!apiKey)
22
- return this.error('A provider API key is required.', { exit: 2 });
23
- const body = {
24
- api_key: apiKey,
25
- gateway_mode: flags.mode,
26
- auto_fetch: !flags['no-fetch'],
27
- };
85
+ return this.error('Missing --api-key (or run interactively).', { exit: 2 });
86
+ if (!baseUrl)
87
+ return this.error('Missing provider base URL pass --provider <preset> or --provider-base-url.', { exit: 2 });
88
+ const body = { api_key: apiKey, gateway_mode: mode, base_url: baseUrl, auto_fetch: !flags['no-fetch'] };
28
89
  if (flags.label)
29
90
  body.label = flags.label;
30
- const res = await this.api(flags).providersAdd(body);
31
- if (!this.jsonEnabled()) {
32
- const id = res.id ?? res.connection?.id;
33
- const imported = res.fetch_result?.imported ?? res.imported ?? 0;
34
- this.log(`Provider connection added (id ${id}). Models imported: ${imported}.`);
35
- this.log('Next: `byte keys create <name>` then `byte integrate <harness>`.');
91
+ if ((0, tty_1.interactive)() && !this.jsonEnabled()) {
92
+ const spin = await ui.spinner();
93
+ spin.start('Connecting provider…');
94
+ let res;
95
+ try {
96
+ res = await api.providersAdd(body);
97
+ }
98
+ catch (err) {
99
+ spin.stop('Connection failed.');
100
+ throw err;
101
+ }
102
+ const models = res.fetch_result?.models ?? res.models ?? [];
103
+ spin.stop(`Connected — ${models.length} model(s) imported.`);
104
+ if (models.length) {
105
+ const alias = await ui.select({
106
+ message: 'Model id to use in your tools',
107
+ options: models.slice(0, 50).map((m) => ({ value: m.byte_alias, label: m.byte_alias, hint: m.model_id })),
108
+ });
109
+ await ui.note(alias, 'Model id (use this everywhere)');
110
+ }
111
+ return res;
36
112
  }
113
+ const res = await api.providersAdd(body);
114
+ if (this.jsonEnabled())
115
+ return res;
116
+ const id = res.id ?? res.connection?.id;
117
+ const imported = res.fetch_result?.imported ?? res.imported ?? 0;
118
+ this.log(`Provider connection added (id ${id}). Models imported: ${imported}.`);
37
119
  return res;
38
120
  }
39
121
  }
@@ -1,8 +1,42 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  const core_1 = require("@oclif/core");
4
37
  const base_1 = require("../../base");
5
- const util_1 = require("../../lib/util");
38
+ const tty_1 = require("../../lib/tty");
39
+ const ui = __importStar(require("../../lib/ui"));
6
40
  class ProvidersRotate extends base_1.BaseCommand {
7
41
  static description = 'Replace the upstream API key on a provider connection (keeps imported models).';
8
42
  static args = {
@@ -15,10 +49,11 @@ class ProvidersRotate extends base_1.BaseCommand {
15
49
  const { args, flags } = await this.parse(ProvidersRotate);
16
50
  this.requireToken(flags);
17
51
  let apiKey = flags['api-key'];
52
+ if (!apiKey && (0, tty_1.interactive)() && !this.jsonEnabled()) {
53
+ apiKey = await ui.password({ message: 'New upstream provider API key', validate: (v) => (v.trim() ? undefined : 'Required') });
54
+ }
18
55
  if (!apiKey)
19
- apiKey = await (0, util_1.promptHidden)('New upstream provider API key: ');
20
- if (!apiKey)
21
- return this.error('A provider API key is required.', { exit: 2 });
56
+ return this.error('Missing --api-key.', { exit: 2 });
22
57
  const res = await this.api(flags).providersRotate(args.id, apiKey);
23
58
  if (!this.jsonEnabled())
24
59
  this.log(`Rotated provider key on connection ${args.id} (now ****${res.secret_last4 ?? '????'}).`);
@@ -0,0 +1,19 @@
1
+ import { BaseCommand } from '../base';
2
+ export default class Setup extends BaseCommand {
3
+ static description: string;
4
+ static aliases: string[];
5
+ static examples: string[];
6
+ static flags: {
7
+ provider: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
8
+ 'provider-key': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
9
+ 'provider-base-url': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
10
+ mode: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
+ preset: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
12
+ 'key-name': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
13
+ 'skip-provider': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
14
+ connect: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
15
+ };
16
+ run(): Promise<unknown>;
17
+ private connectProvider;
18
+ private runNonInteractive;
19
+ }
@@ -0,0 +1,277 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const core_1 = require("@oclif/core");
37
+ const base_1 = require("../base");
38
+ const auth_1 = require("../lib/auth");
39
+ const credentials_1 = require("../lib/credentials");
40
+ const presets_1 = require("../lib/presets");
41
+ const providers_1 = require("../lib/providers");
42
+ const tty_1 = require("../lib/tty");
43
+ const ui = __importStar(require("../lib/ui"));
44
+ class Setup extends base_1.BaseCommand {
45
+ static description = 'Guided setup: sign in, connect a provider, create a Byte key, and wire a tool.';
46
+ static aliases = ['init', 'onboard'];
47
+ static examples = ['<%= config.bin %> setup', '<%= config.bin %> setup --provider deepseek --provider-key sk-... --connect opencode --json'];
48
+ static flags = {
49
+ provider: core_1.Flags.string({ description: 'Provider preset id (openai, deepseek, anthropic, …)' }),
50
+ 'provider-key': core_1.Flags.string({ description: 'Upstream provider API key', env: 'BYTE_PROVIDER_KEY' }),
51
+ 'provider-base-url': core_1.Flags.string({ description: 'Custom provider base URL (for --provider custom)' }),
52
+ mode: core_1.Flags.string({ description: 'Gateway mode override (byte_compatible/byte_messages/byte_generative)' }),
53
+ preset: core_1.Flags.string({
54
+ description: 'Optimization mode',
55
+ options: ['maximum', 'balanced', 'max_savings', 'lowest_latency', 'reliability_first'],
56
+ default: 'maximum',
57
+ }),
58
+ 'key-name': core_1.Flags.string({ description: 'Name for the Byte key', default: 'cli' }),
59
+ 'skip-provider': core_1.Flags.boolean({ description: 'Skip connecting a provider' }),
60
+ connect: core_1.Flags.string({ description: 'Tool to wire after setup (e.g. opencode, codex)' }),
61
+ };
62
+ async run() {
63
+ const { flags } = await this.parse(Setup);
64
+ const profile = this.profileFrom(flags);
65
+ const base = this.baseUrlFrom(flags);
66
+ if (this.jsonEnabled() || !(0, tty_1.interactive)()) {
67
+ return this.runNonInteractive(flags, profile, base);
68
+ }
69
+ await ui.intro(ui.banner());
70
+ // 1) Sign in
71
+ let token = (0, credentials_1.getToken)(profile);
72
+ const api = this.api(flags);
73
+ if (token) {
74
+ try {
75
+ const me = await api.me();
76
+ await ui.log.info(`Signed in as ${me.user?.email ?? 'you'}`);
77
+ }
78
+ catch {
79
+ token = undefined;
80
+ }
81
+ }
82
+ if (!token) {
83
+ await (0, auth_1.deviceLogin)({ baseUrl: base, profile });
84
+ }
85
+ // 2) Connect a provider
86
+ let provider = { status: 'skipped' };
87
+ if (!flags['skip-provider']) {
88
+ const existing = await api.providersList().catch(() => ({ connections: [] }));
89
+ const conns = existing.connections ?? (Array.isArray(existing) ? existing : []);
90
+ let addNew = true;
91
+ if (conns.length) {
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' };
102
+ }
103
+ if (addNew) {
104
+ provider = await this.connectProvider(api, flags);
105
+ }
106
+ }
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
114
+ let byteKey = (0, credentials_1.getByteKey)(profile);
115
+ if (!byteKey) {
116
+ const name = await ui.text({ message: 'Name this Byte key', defaultValue: flags['key-name'] || 'cli' });
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);
121
+ }
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
126
+ const connect = await ui.select({
127
+ message: 'Connect a coding tool now?',
128
+ options: [
129
+ { value: 'skip', label: 'Skip for now' },
130
+ { value: 'opencode', label: 'opencode' },
131
+ { value: 'claude-code', label: 'Claude Code' },
132
+ { value: 'codex', label: 'Codex CLI' },
133
+ { value: 'cursor', label: 'Cursor' },
134
+ { value: 'cline', label: 'Cline' },
135
+ { value: 'aider', label: 'Aider' },
136
+ ],
137
+ });
138
+ if (connect !== 'skip') {
139
+ const argv = [connect, '--write', '--yes', ...(provider.alias ? ['--model', provider.alias] : [])];
140
+ await this.config.runCommand('integrate', argv);
141
+ }
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 };
152
+ }
153
+ async connectProvider(api, flags) {
154
+ const providerId = await ui.select({
155
+ message: 'Which provider?',
156
+ options: providers_1.PROVIDER_PRESETS.map((p) => ({ value: p.id, label: p.label, hint: p.advanced ? 'advanced' : undefined })),
157
+ });
158
+ const preset = (0, providers_1.findPreset)(providerId);
159
+ let baseUrl = preset.base_url;
160
+ if (preset.custom || !baseUrl) {
161
+ baseUrl = await ui.text({
162
+ message: 'Provider base URL',
163
+ placeholder: 'https://api.example.com/v1',
164
+ validate: (v) => (v.startsWith('http') ? undefined : 'Must be a URL'),
165
+ });
166
+ }
167
+ if (preset.advanced && preset.note)
168
+ await ui.log.warn(preset.note);
169
+ const key = await ui.password({ message: `${preset.label} API key`, validate: (v) => (v.trim() ? undefined : 'Required') });
170
+ const spin = await ui.spinner();
171
+ spin.start(`Connecting ${preset.label}…`);
172
+ let res;
173
+ try {
174
+ res = await api.providersAdd({ api_key: key, gateway_mode: flags.mode || preset.gateway_mode, base_url: baseUrl, auto_fetch: true });
175
+ }
176
+ catch (err) {
177
+ spin.stop('Could not save the provider connection.');
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' };
224
+ }
225
+ return { status: 'saved' };
226
+ }
227
+ async runNonInteractive(flags, profile, base) {
228
+ this.requireToken(flags);
229
+ const api = this.api(flags);
230
+ let imported = 0;
231
+ let providerStatus = flags['skip-provider'] ? 'skipped' : 'skipped';
232
+ let providerError;
233
+ let chosenAlias;
234
+ const key = flags['provider-key'];
235
+ if (key && !flags['skip-provider']) {
236
+ const preset = flags.provider ? (0, providers_1.findPreset)(flags.provider) : undefined;
237
+ const baseUrl = flags['provider-base-url'] || preset?.base_url;
238
+ const mode = flags.mode || preset?.gateway_mode || 'byte_compatible';
239
+ if (!baseUrl) {
240
+ return this.error('Provide --provider <preset> or --provider-base-url for the upstream provider.', { exit: 2 });
241
+ }
242
+ const res = await api.providersAdd({ api_key: key, gateway_mode: mode, base_url: baseUrl, auto_fetch: true });
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
+ }
253
+ }
254
+ await api.optPatch({ default_mode: flags.preset }).catch(() => undefined);
255
+ const keyRes = await api.keysCreate({ name: flags['key-name'] || 'cli', preset: flags.preset });
256
+ (0, credentials_1.setByteKey)(profile, keyRes.key);
257
+ if (flags.connect) {
258
+ const argv = [flags.connect, '--write', '--yes', ...(chosenAlias ? ['--model', chosenAlias] : [])];
259
+ await this.config.runCommand('integrate', argv);
260
+ }
261
+ if (!this.jsonEnabled()) {
262
+ this.log(`Byte key: ${keyRes.key}`);
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\`.`);
266
+ }
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
+ };
275
+ }
276
+ }
277
+ exports.default = Setup;
@@ -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
- this.log((0, output_1.renderTable)(['Bucket', 'Requests', 'Tokens', 'Cost', 'Savings'], points.map((p) => [
23
- p.bucket ?? p.date ?? p.t ?? '-',
24
- p.requests ?? p.total_requests ?? '-',
25
- p.tokens ?? p.total_tokens ?? '-',
26
- (0, output_1.fmtUsd)(p.cost_usd ?? p.cost_byte_usd),
27
- (0, output_1.fmtUsd)(p.savings_usd),
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
  }
@@ -0,0 +1,3 @@
1
+ import { Hook } from '@oclif/core';
2
+ declare const hook: Hook<'init'>;
3
+ export default hook;