@bytevion/cli 0.1.0 → 0.2.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/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # @bytevion/cli — Byte from your terminal
2
+
3
+ Control the Byte LLM-optimization gateway from the command line: connect a provider, mint a
4
+ Byte key, wire your coding tool, and watch the savings — without leaving the terminal.
5
+
6
+ ```bash
7
+ npm i -g @bytevion/cli # Node >= 20.12
8
+ byte # interactive home menu
9
+ byte setup # guided setup (sign in → provider → key → tool)
10
+ ```
11
+
12
+ ## Quickstart
13
+
14
+ ```bash
15
+ byte login # approve the device in your browser
16
+ byte providers add --provider deepseek --api-key sk-... # or run `byte setup`
17
+ byte keys create my-key --preset lowest_latency
18
+ byte integrate opencode --write
19
+ byte run "hello from byte"
20
+ byte usage
21
+ ```
22
+
23
+ ## Commands
24
+
25
+ | Command | What it does |
26
+ |---|---|
27
+ | `byte` | Interactive home menu (run with no arguments in a terminal) |
28
+ | `byte setup` (alias `init`) | Guided wizard: sign in → connect provider → create key → wire a tool |
29
+ | `byte login` / `logout` / `whoami` | Device-code sign in / out / identity |
30
+ | `byte providers add\|list\|rotate\|test` | Manage upstream provider keys (BYOK) |
31
+ | `byte keys create\|list\|rotate\|revoke` | Manage Byte API keys (optional per-key `--preset`) |
32
+ | `byte opt show\|set\|preset` | Inspect / tune optimizations |
33
+ | `byte integrate <tool>` | Wire a coding harness or SDK (`--list` to see all, `--write` to apply) |
34
+ | `byte run "<prompt>"` | One-off prompt through the gateway |
35
+ | `byte compare "<prompt>"` | Run direct vs Byte and compare cost, latency, quality |
36
+ | `byte usage` / `byte stats` | Usage and savings |
37
+ | `byte env` | Print shell exports for an SDK/tool |
38
+ | `byte doctor` | Connectivity / auth / provider / gateway health |
39
+ | `byte batch submit\|list\|poll` | Offline, cost-optimized batch jobs |
40
+ | `byte sessions` | List / revoke terminal sessions |
41
+
42
+ ## Providers (presets)
43
+
44
+ `byte providers add --provider <id> --api-key <key>` — base URL and gateway mode are inferred:
45
+
46
+ `openai`, `anthropic`, `deepseek`, `gemini`, `xai`, `groq`, `mistral`, `openrouter`, `together`,
47
+ `fireworks`, `perplexity`, `cerebras`, `sambanova`, `deepinfra`, `moonshot`, `zhipu`, `qwen`,
48
+ `nebius`, `hyperbolic`, `novita`, plus `custom` (`--provider-base-url`). Azure / Bedrock / Vertex /
49
+ Cohere are advanced (need a deployment URL / SigV4 / OAuth2) — use `--provider-base-url` / the dashboard.
50
+
51
+ ## Tools you can wire (`byte integrate`)
52
+
53
+ Claude Code, Codex CLI, opencode, Cursor, Cline, Aider, Continue, Roo Code, Windsurf, Zed, Goose,
54
+ Kilo Code, Open Interpreter, Tabby, Cody, GitHub Copilot (BYOK) + Copilot CLI, Qwen Code, Kimi CLI,
55
+ Gemini CLI, and the OpenAI / Anthropic SDKs.
56
+
57
+ ## Environment
58
+
59
+ `BYTE_TOKEN` (control-plane token, for CI) · `BYTE_API_KEY` (gateway key) · `BYTE_PROFILE` ·
60
+ `BYTE_BASE_URL` · `BYTE_HOME` · `BYTE_NO_MENU=1` (disable the home menu) · `NO_COLOR` · `CI`.
61
+
62
+ Every command accepts `--json` for machine output and `--profile <name>` for multiple workspaces.
63
+
64
+ ## Exit codes
65
+
66
+ `0` ok · `2` usage · `3` auth · `4` not configured · `5` offline · `6` upstream.
@@ -0,0 +1,12 @@
1
+ import { BaseCommand } from '../base';
2
+ export default class Compare extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ prompt: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ model: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
10
+ };
11
+ run(): Promise<unknown>;
12
+ }
@@ -0,0 +1,104 @@
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 tty_1 = require("../lib/tty");
39
+ const ui = __importStar(require("../lib/ui"));
40
+ const num = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : Number(v) || 0);
41
+ const usd = (v) => `$${num(v).toFixed(6)}`;
42
+ const tokensOf = (m) => num(m?.tokens_in) + num(m?.tokens_out);
43
+ const pad = (s, n) => s.padEnd(n);
44
+ class Compare extends base_1.BaseCommand {
45
+ static description = 'Prove the win: run a prompt direct vs through Byte and compare cost, latency, and quality.';
46
+ static examples = [
47
+ '<%= config.bin %> compare "Summarize the latest release notes"',
48
+ '<%= config.bin %> compare "Refactor this function" --model byte-2-deepseek-chat',
49
+ ];
50
+ static args = {
51
+ prompt: core_1.Args.string({ description: 'Prompt to run on both lanes', required: true }),
52
+ };
53
+ static flags = {
54
+ model: core_1.Flags.string({ description: 'Model id (byte alias) or "auto"', default: 'auto' }),
55
+ };
56
+ async run() {
57
+ const { args, flags } = await this.parse(Compare);
58
+ this.requireToken(flags);
59
+ const api = this.api(flags);
60
+ const body = { messages: [{ role: 'user', content: args.prompt }], model: flags.model };
61
+ const panels = {};
62
+ const spin = !this.jsonEnabled() && (0, tty_1.interactive)() ? await ui.spinner() : null;
63
+ spin?.start('Running the prompt direct and through Byte…');
64
+ try {
65
+ for await (const event of api.compareStream(body)) {
66
+ const panel = String(event?.panel || '');
67
+ if (event?.metrics && (panel === 'direct' || panel === 'byte'))
68
+ panels[panel] = event.metrics;
69
+ }
70
+ }
71
+ finally {
72
+ spin?.stop('Comparison complete');
73
+ }
74
+ const direct = panels.direct || {};
75
+ const byte = panels.byte || {};
76
+ const summary = byte.compare_summary || {};
77
+ if (this.jsonEnabled())
78
+ return { direct, byte, summary };
79
+ if (!panels.direct && !panels.byte) {
80
+ this.log('No comparison result was produced. Make sure a provider and model alias are configured (`byte setup`).');
81
+ return { direct, byte, summary };
82
+ }
83
+ const savings = num(summary.savings_usd ?? byte.savings_usd);
84
+ const savingsPct = num(byte.savings_pct);
85
+ const latencySaved = num(summary.latency_saved_ms ?? byte.latency_saved_ms);
86
+ const dominance = String(byte.dominance_status || summary.dominance_status || 'n/a');
87
+ const quality = String(byte.quality_guard_state || 'not_measured');
88
+ this.log('');
89
+ this.log(` ${pad('', 16)}${pad('Direct', 18)}Byte`);
90
+ this.log(` ${pad('Cost', 16)}${pad(usd(direct.cost_usd), 18)}${usd(byte.cost_usd)}`);
91
+ this.log(` ${pad('Latency (ms)', 16)}${pad(String(num(direct.latency_ms)), 18)}${num(byte.latency_ms)}`);
92
+ this.log(` ${pad('Tokens', 16)}${pad(String(tokensOf(direct)), 18)}${tokensOf(byte)}`);
93
+ this.log('');
94
+ const sign = latencySaved >= 0 ? '−' : '+';
95
+ this.log(` Savings ${usd(savings)} (${savingsPct.toFixed(1)}%)`);
96
+ this.log(` Latency ${sign}${Math.abs(latencySaved)} ms`);
97
+ this.log(` Quality ${quality}`);
98
+ const verdict = dominance === 'dominant' ? ui.theme.ok('Byte wins (dominant)') : ui.theme.warn(dominance);
99
+ this.log(` Verdict ${verdict}`);
100
+ this.log('');
101
+ return { direct, byte, summary };
102
+ }
103
+ }
104
+ exports.default = Compare;
@@ -11,6 +11,7 @@ export default class Integrate extends BaseCommand {
11
11
  write: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
12
12
  yes: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
13
13
  key: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
14
+ model: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
14
15
  preset: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
15
16
  };
16
17
  run(): Promise<unknown>;
@@ -1,4 +1,37 @@
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 node_fs_1 = require("node:fs");
@@ -8,12 +41,15 @@ const api_1 = require("../lib/api");
8
41
  const errors_1 = require("../lib/errors");
9
42
  const integrations_1 = require("../lib/integrations");
10
43
  const shell_1 = require("../lib/shell");
44
+ const tty_1 = require("../lib/tty");
45
+ const ui = __importStar(require("../lib/ui"));
46
+ const util_1 = require("../lib/util");
11
47
  class Integrate extends base_1.BaseCommand {
12
- static description = 'Wire a coding harness or SDK to Byte (Claude Code, Codex, Cursor, Cline, Aider, OpenAI/Anthropic SDK).';
48
+ static description = 'Wire a coding harness or SDK to Byte (Claude Code, Codex, opencode, Cursor, Cline, Aider, ).';
13
49
  static examples = [
14
50
  '<%= config.bin %> integrate --list',
15
- '<%= config.bin %> integrate codex',
16
- '<%= config.bin %> integrate claude-code --write --preset lowest_latency',
51
+ '<%= config.bin %> integrate opencode --write',
52
+ '<%= config.bin %> integrate claude-code --write --model byte-2-deepseek-chat',
17
53
  ];
18
54
  static args = {
19
55
  target: core_1.Args.string({ description: 'Target to wire (run --list to see all)', required: false }),
@@ -24,6 +60,7 @@ class Integrate extends base_1.BaseCommand {
24
60
  write: core_1.Flags.boolean({ description: 'Write tool config files (a timestamped .bak is kept)' }),
25
61
  yes: core_1.Flags.boolean({ char: 'y', description: 'Skip confirmation prompts' }),
26
62
  key: core_1.Flags.string({ description: 'Byte API key to use (defaults to the saved profile key)' }),
63
+ model: core_1.Flags.string({ description: 'Model id (byte alias) to wire (default byte-default)' }),
27
64
  preset: core_1.Flags.string({
28
65
  description: 'Apply this optimization preset to the org before wiring',
29
66
  options: ['balanced', 'max_savings', 'lowest_latency', 'reliability_first'],
@@ -31,16 +68,27 @@ class Integrate extends base_1.BaseCommand {
31
68
  };
32
69
  async run() {
33
70
  const { args, flags } = await this.parse(Integrate);
34
- if (flags.list || !args.target) {
71
+ if (flags.list)
35
72
  return this.showList(flags);
73
+ let target = args.target;
74
+ if (!target) {
75
+ if ((0, tty_1.interactive)() && !this.jsonEnabled()) {
76
+ target = await ui.select({
77
+ message: 'Connect which tool?',
78
+ options: (0, integrations_1.listIntegrations)().map((i) => ({ value: i.key, label: i.title, hint: i.compat })),
79
+ });
80
+ }
81
+ else {
82
+ return this.showList(flags);
83
+ }
36
84
  }
37
- const integ = (0, integrations_1.findIntegration)(args.target);
38
- if (!integ) {
39
- return this.error(`Unknown target "${args.target}". Run \`byte integrate --list\`.`, { exit: 2 });
40
- }
85
+ const integ = (0, integrations_1.findIntegration)(target);
86
+ if (!integ)
87
+ return this.error(`Unknown target "${target}". Run \`byte integrate --list\`.`, { exit: 2 });
41
88
  const byteKey = flags.key || this.requireByteKey(flags);
42
89
  const base = this.baseUrlFrom(flags);
43
90
  const shell = (0, shell_1.detectShell)(flags.shell);
91
+ const modelAlias = flags.model || 'byte-default';
44
92
  if (flags.preset) {
45
93
  this.requireToken(flags);
46
94
  await this.api(flags).optPatch({ default_mode: flags.preset });
@@ -48,11 +96,10 @@ class Integrate extends base_1.BaseCommand {
48
96
  if (integ.compat === 'anthropic') {
49
97
  const exists = await this.messagesEndpointExists(base, byteKey);
50
98
  if (!exists) {
51
- this.warn(`${integ.title} needs Byte's Anthropic endpoint (/v1/messages), which did not respond. ` +
52
- 'The values below are correct once it is enabled on your gateway.');
99
+ this.warn(`${integ.title} needs Byte's Anthropic endpoint (/v1/messages). The values below are correct once it is enabled.`);
53
100
  }
54
101
  }
55
- const result = integ.build({ baseUrl: base, byteKey });
102
+ const result = integ.build({ baseUrl: base, byteKey, modelAlias });
56
103
  if (!this.jsonEnabled()) {
57
104
  this.log(`Integrating ${integ.title} (${integ.compat}-compatible):`);
58
105
  this.log('');
@@ -65,9 +112,8 @@ class Integrate extends base_1.BaseCommand {
65
112
  this.log(` ${line}`);
66
113
  }
67
114
  if (result.settingsFile) {
68
- if (flags.write) {
115
+ if (flags.write)
69
116
  this.writeSettings(result.settingsFile);
70
- }
71
117
  else {
72
118
  this.log('');
73
119
  this.log(` Tip: re-run with --write to update ${result.settingsFile.path} automatically.`);
@@ -77,7 +123,7 @@ class Integrate extends base_1.BaseCommand {
77
123
  else if (flags.write && result.settingsFile) {
78
124
  this.writeSettings(result.settingsFile);
79
125
  }
80
- return { target: integ.key, compat: integ.compat, env: Object.fromEntries(result.env) };
126
+ return { target: integ.key, compat: integ.compat, model: modelAlias, env: Object.fromEntries(result.env) };
81
127
  }
82
128
  writeSettings(file) {
83
129
  let existing = {};
@@ -97,24 +143,26 @@ class Integrate extends base_1.BaseCommand {
97
143
  this.log(` Updated ${file.path}`);
98
144
  }
99
145
  async messagesEndpointExists(base, byteKey) {
100
- try {
101
- await new api_1.ByteApi(base).probeMessages(byteKey);
102
- return true;
103
- }
104
- catch (err) {
105
- // 404 means the endpoint is not deployed; any other status means it exists.
106
- if (err instanceof errors_1.ByteError)
107
- return err.status !== 404;
108
- return false;
109
- }
146
+ const probe = (async () => {
147
+ try {
148
+ await new api_1.ByteApi(base).probeMessages(byteKey);
149
+ return true;
150
+ }
151
+ catch (err) {
152
+ if (err instanceof errors_1.ByteError)
153
+ return err.status !== 404;
154
+ return false;
155
+ }
156
+ })();
157
+ // Never block the UX on a slow gateway; assume reachable on timeout.
158
+ return (0, util_1.withTimeout)(probe, 4000, true);
110
159
  }
111
160
  async showList(flags) {
112
161
  const targets = (0, integrations_1.listIntegrations)();
113
162
  const key = flags.key || this.optionalByteKey(flags);
114
163
  let messagesReady;
115
- if (key) {
164
+ if (key)
116
165
  messagesReady = await this.messagesEndpointExists(this.baseUrlFrom(flags), key);
117
- }
118
166
  const rows = targets.map((t) => ({
119
167
  target: t.key,
120
168
  title: t.title,
@@ -124,8 +172,8 @@ class Integrate extends base_1.BaseCommand {
124
172
  if (this.jsonEnabled())
125
173
  return rows;
126
174
  for (const r of rows) {
127
- const status = r.ready === undefined ? 'unknown (sign in / create a key to probe)' : r.ready ? 'ready' : 'needs /v1/messages';
128
- this.log(`${r.target.padEnd(14)} ${r.title.padEnd(16)} ${r.compat.padEnd(10)} ${status}`);
175
+ const status = r.ready === undefined ? 'sign in to probe' : r.ready ? 'ready' : 'needs /v1/messages';
176
+ this.log(`${r.target.padEnd(16)} ${r.title.padEnd(22)} ${r.compat.padEnd(10)} ${status}`);
129
177
  }
130
178
  return rows;
131
179
  }
@@ -1,76 +1,57 @@
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
- const node_os_1 = require("node:os");
5
37
  const base_1 = require("../base");
6
- const api_1 = require("../lib/api");
7
- const config_1 = require("../lib/config");
8
- const credentials_1 = require("../lib/credentials");
9
- const errors_1 = require("../lib/errors");
10
- const util_1 = require("../lib/util");
38
+ const auth_1 = require("../lib/auth");
39
+ const ui = __importStar(require("../lib/ui"));
11
40
  class Login extends base_1.BaseCommand {
12
41
  static description = 'Sign in from this terminal by approving a device code in the dashboard.';
13
42
  static examples = ['<%= config.bin %> login', '<%= config.bin %> login --no-browser'];
14
43
  static flags = {
15
- 'no-browser': core_1.Flags.boolean({ description: 'Do not try to open the browser automatically' }),
44
+ 'no-browser': core_1.Flags.boolean({ description: 'Do not open the browser automatically' }),
16
45
  };
17
46
  async run() {
18
47
  const { flags } = await this.parse(Login);
19
48
  const profile = this.profileFrom(flags);
20
49
  const base = this.baseUrlFrom(flags);
21
- const api = new api_1.ByteApi(base);
22
- const start = await api.deviceStart(`byte-cli@${(0, node_os_1.hostname)()}`);
23
- const verifyUrl = start.verification_uri_complete || start.verification_uri;
24
- this.log('');
25
- this.log(' Approve this terminal in your browser:');
26
- this.log(` ${verifyUrl}`);
27
- this.log('');
28
- this.log(` Verification code: ${start.user_code}`);
29
- this.log('');
30
- if (!flags['no-browser']) {
31
- try {
32
- await (0, util_1.openBrowser)(verifyUrl);
33
- }
34
- catch {
35
- // user can open manually
36
- }
37
- }
38
- let interval = (Number(start.interval) || 5) * 1000;
39
- const deadline = Date.now() + (Number(start.expires_in) || 600) * 1000;
40
- while (Date.now() < deadline) {
41
- await (0, util_1.sleep)(interval);
42
- try {
43
- const tok = await api.deviceToken(start.device_code);
44
- (0, credentials_1.setToken)(profile, tok.access_token);
45
- (0, config_1.setProfile)(profile, {
46
- base_url: base,
47
- dashboard_url: (0, config_1.getProfile)(profile).dashboard_url || config_1.DEFAULT_DASHBOARD_URL,
48
- org_id: tok.org?.id,
49
- org_name: tok.org?.name,
50
- email: tok.user?.email,
51
- });
52
- if (!this.jsonEnabled()) {
53
- this.log(`Signed in as ${tok.user?.email} (org: ${tok.org?.name}).`);
54
- this.log('Next: run `byte init` to add a provider key and create your Byte API key.');
55
- }
56
- return { status: 'signed_in', email: tok.user?.email, org: tok.org?.name, profile };
57
- }
58
- catch (err) {
59
- const code = err instanceof errors_1.ByteError ? err.code : '';
60
- if (code === 'authorization_pending')
61
- continue;
62
- if (code === 'slow_down') {
63
- interval += 5000;
64
- continue;
65
- }
66
- if (code === 'access_denied')
67
- return this.error('Authorization was denied in the dashboard.', { exit: 3 });
68
- if (code === 'expired_token')
69
- return this.error('The code expired. Run `byte login` again.', { exit: 3 });
70
- throw err;
71
- }
50
+ const res = await (0, auth_1.deviceLogin)({ baseUrl: base, profile, noBrowser: flags['no-browser'] });
51
+ if (!this.jsonEnabled()) {
52
+ await ui.log.success(`Signed in as ${res.email} (org: ${res.org}). Next: byte setup`);
72
53
  }
73
- return this.error('Login timed out. Run `byte login` again.', { exit: 3 });
54
+ return { status: 'signed_in', email: res.email, org: res.org, profile };
74
55
  }
75
56
  }
76
57
  exports.default = Login;
@@ -3,8 +3,10 @@ export default class ProvidersAdd extends BaseCommand {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static flags: {
6
+ provider: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
6
7
  'api-key': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
7
- mode: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
8
+ 'provider-base-url': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
9
+ mode: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
8
10
  label: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
9
11
  'no-fetch': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
12
  };
@@ -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
  }