@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 +66 -0
- package/dist/commands/compare.d.ts +12 -0
- package/dist/commands/compare.js +104 -0
- package/dist/commands/integrate.d.ts +1 -0
- package/dist/commands/integrate.js +76 -28
- package/dist/commands/login.js +40 -59
- package/dist/commands/providers/add.d.ts +3 -1
- package/dist/commands/providers/add.js +102 -20
- package/dist/commands/providers/rotate.js +39 -4
- package/dist/commands/setup.d.ts +19 -0
- package/dist/commands/setup.js +201 -0
- package/dist/hooks/init/home.d.ts +3 -0
- package/dist/hooks/init/home.js +95 -0
- package/dist/lib/api.d.ts +1 -0
- package/dist/lib/api.js +60 -0
- package/dist/lib/auth.d.ts +9 -0
- package/dist/lib/auth.js +100 -0
- package/dist/lib/integrations.d.ts +1 -0
- package/dist/lib/integrations.js +198 -61
- package/dist/lib/providers.d.ts +13 -0
- package/dist/lib/providers.js +39 -0
- package/dist/lib/tty.d.ts +1 -0
- package/dist/lib/tty.js +11 -0
- package/dist/lib/ui.d.ts +60 -0
- package/dist/lib/ui.js +110 -0
- package/dist/lib/util.d.ts +1 -2
- package/dist/lib/util.js +11 -79
- package/oclif.manifest.json +305 -108
- package/package.json +76 -59
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,
|
|
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
|
|
16
|
-
'<%= config.bin %> integrate claude-code --write --
|
|
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
|
|
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)(
|
|
38
|
-
if (!integ)
|
|
39
|
-
return this.error(`Unknown target "${
|
|
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)
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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 ? '
|
|
128
|
-
this.log(`${r.target.padEnd(
|
|
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
|
}
|
package/dist/commands/login.js
CHANGED
|
@@ -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
|
|
7
|
-
const
|
|
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
|
|
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
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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 = '
|
|
8
|
-
static examples = [
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
21
|
-
if (!
|
|
22
|
-
return this.error('
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
}
|