@bytevion/cli 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/connect.d.ts +12 -0
- package/dist/commands/connect.js +64 -0
- package/dist/commands/dash.d.ts +9 -0
- package/dist/commands/dash.js +120 -0
- package/dist/commands/integrate.d.ts +3 -0
- package/dist/commands/integrate.js +58 -6
- package/dist/commands/keys/rotate.d.ts +3 -0
- package/dist/commands/keys/rotate.js +9 -1
- package/dist/commands/mcp/add.d.ts +13 -0
- package/dist/commands/mcp/add.js +31 -0
- package/dist/commands/mcp/list.d.ts +5 -0
- package/dist/commands/mcp/list.js +22 -0
- package/dist/commands/mcp/remove.d.ts +11 -0
- package/dist/commands/mcp/remove.js +19 -0
- package/dist/commands/mcp/test.d.ts +11 -0
- package/dist/commands/mcp/test.js +20 -0
- package/dist/commands/providers/compare.d.ts +13 -0
- package/dist/commands/providers/compare.js +52 -0
- package/dist/commands/sync.d.ts +10 -0
- package/dist/commands/sync.js +39 -0
- package/dist/lib/api.d.ts +7 -0
- package/dist/lib/api.js +24 -0
- package/dist/lib/config-formats.d.ts +3 -0
- package/dist/lib/config-formats.js +88 -0
- package/dist/lib/detect.d.ts +9 -0
- package/dist/lib/detect.js +83 -0
- package/dist/lib/friendly.js +10 -0
- package/dist/lib/integrations.d.ts +1 -0
- package/dist/lib/integrations.js +33 -4
- package/dist/lib/paths.d.ts +2 -0
- package/dist/lib/paths.js +13 -0
- package/dist/lib/tui.d.ts +42 -0
- package/dist/lib/tui.js +10 -0
- package/dist/lib/wiring.d.ts +10 -0
- package/dist/lib/wiring.js +53 -0
- package/dist/tui/components/Dash.d.ts +16 -0
- package/dist/tui/components/Dash.js +53 -0
- package/dist/tui/contract.d.ts +42 -0
- package/dist/tui/index.d.ts +2 -1
- package/dist/tui/index.js +34 -0
- package/oclif.manifest.json +585 -96
- package/package.json +4 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseCommand } from '../base';
|
|
2
|
+
export default class Connect extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
model: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
7
|
+
preset: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
only: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
yes: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<unknown>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const base_1 = require("../base");
|
|
5
|
+
const detect_1 = require("../lib/detect");
|
|
6
|
+
class Connect extends base_1.BaseCommand {
|
|
7
|
+
static description = 'Detect installed coding agents and wire every one Byte can auto-configure — in one shot.';
|
|
8
|
+
static examples = [
|
|
9
|
+
'<%= config.bin %> connect',
|
|
10
|
+
'<%= config.bin %> connect --model byte-2-deepseek-v4-flash',
|
|
11
|
+
'<%= config.bin %> connect --only opencode,codex',
|
|
12
|
+
];
|
|
13
|
+
static flags = {
|
|
14
|
+
model: core_1.Flags.string({ description: 'Model id (byte alias) to wire (default byte-default)' }),
|
|
15
|
+
preset: core_1.Flags.string({
|
|
16
|
+
description: 'Optimization preset to apply before wiring',
|
|
17
|
+
options: ['maximum', 'balanced', 'max_savings', 'lowest_latency', 'reliability_first'],
|
|
18
|
+
}),
|
|
19
|
+
only: core_1.Flags.string({ description: 'Comma-separated subset of tool keys to wire' }),
|
|
20
|
+
yes: core_1.Flags.boolean({ char: 'y', description: 'Skip confirmation prompts' }),
|
|
21
|
+
};
|
|
22
|
+
async run() {
|
|
23
|
+
const { flags } = await this.parse(Connect);
|
|
24
|
+
// A Byte key is what gets written into each tool's config, so require one up front.
|
|
25
|
+
this.requireByteKey(flags);
|
|
26
|
+
const only = flags.only ? new Set(flags.only.split(',').map((s) => s.trim()).filter(Boolean)) : null;
|
|
27
|
+
const detected = (0, detect_1.detectTools)().filter((d) => d.installed && (!only || only.has(d.key)));
|
|
28
|
+
const targets = detected.filter((d) => d.canAutoWrite);
|
|
29
|
+
const manual = detected.filter((d) => !d.canAutoWrite).map((d) => d.key);
|
|
30
|
+
if (!targets.length) {
|
|
31
|
+
if (!this.jsonEnabled()) {
|
|
32
|
+
this.log('No auto-configurable coding agents detected.');
|
|
33
|
+
if (manual.length)
|
|
34
|
+
this.log(`Installed but manual: ${manual.join(', ')} — run \`byte integrate <tool>\`.`);
|
|
35
|
+
else
|
|
36
|
+
this.log('Install one (e.g. opencode) or wire manually with `byte integrate --list`.');
|
|
37
|
+
}
|
|
38
|
+
return { wired: [], manual };
|
|
39
|
+
}
|
|
40
|
+
const wired = [];
|
|
41
|
+
for (const target of targets) {
|
|
42
|
+
const argv = [
|
|
43
|
+
target.key,
|
|
44
|
+
'--write',
|
|
45
|
+
'--yes',
|
|
46
|
+
...(flags.model ? ['--model', flags.model] : []),
|
|
47
|
+
...(flags.preset ? ['--preset', flags.preset] : []),
|
|
48
|
+
];
|
|
49
|
+
// sequential keeps config writes + their backups ordered and surfaces the first failure clearly
|
|
50
|
+
// eslint-disable-next-line no-await-in-loop
|
|
51
|
+
await this.config.runCommand('integrate', argv);
|
|
52
|
+
wired.push(target.key);
|
|
53
|
+
}
|
|
54
|
+
if (!this.jsonEnabled()) {
|
|
55
|
+
this.log('');
|
|
56
|
+
this.log(`Connected ${wired.length} tool(s): ${wired.join(', ')}.`);
|
|
57
|
+
if (manual.length)
|
|
58
|
+
this.log(`Manual setup needed for: ${manual.join(', ')} — run \`byte integrate <tool>\`.`);
|
|
59
|
+
this.log('Export the Byte key in your shell (see `byte env`) and start coding.');
|
|
60
|
+
}
|
|
61
|
+
return { wired, manual };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
exports.default = Connect;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const base_1 = require("../base");
|
|
4
|
+
const config_1 = require("../lib/config");
|
|
5
|
+
const tui_1 = require("../lib/tui");
|
|
6
|
+
class Dash extends base_1.BaseCommand {
|
|
7
|
+
static description = 'Live terminal dashboard: savings, recent requests, optimizations, and gateway health.';
|
|
8
|
+
static aliases = ['monitor', 'top'];
|
|
9
|
+
async run() {
|
|
10
|
+
const { flags } = await this.parse(Dash);
|
|
11
|
+
const load = () => this.loadDash(flags);
|
|
12
|
+
let data;
|
|
13
|
+
try {
|
|
14
|
+
data = await load();
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
data = this.emptyDash();
|
|
18
|
+
}
|
|
19
|
+
if ((0, tui_1.useInk)({ json: this.jsonEnabled() })) {
|
|
20
|
+
const res = await (0, tui_1.renderDashIsland)({
|
|
21
|
+
data,
|
|
22
|
+
ports: {
|
|
23
|
+
refresh: async () => {
|
|
24
|
+
try {
|
|
25
|
+
return await load();
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return data;
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
ascii: (0, tui_1.computeAscii)(),
|
|
33
|
+
plainColor: Boolean(process.env.NO_COLOR),
|
|
34
|
+
version: this.config.version,
|
|
35
|
+
});
|
|
36
|
+
if (res.status !== 'fallback')
|
|
37
|
+
return data;
|
|
38
|
+
}
|
|
39
|
+
if (this.jsonEnabled())
|
|
40
|
+
return data;
|
|
41
|
+
this.log(this.renderText(data));
|
|
42
|
+
return data;
|
|
43
|
+
}
|
|
44
|
+
emptyDash() {
|
|
45
|
+
return { gateway: 'unknown', requests: 0, cacheHitRate: 0, savedTotal: 0, tokensTotal: 0, costTotal: 0, savingsSeries: [], providers: 0, models: 0, recent: [], layers: [] };
|
|
46
|
+
}
|
|
47
|
+
async loadDash(flags) {
|
|
48
|
+
const api = this.api(flags);
|
|
49
|
+
const out = this.emptyDash();
|
|
50
|
+
const settle = async (p) => {
|
|
51
|
+
try {
|
|
52
|
+
return await p;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const [summary, usage, opt, reqs, health] = await Promise.all([
|
|
59
|
+
settle(api.dashboard()),
|
|
60
|
+
settle(api.usage('day')),
|
|
61
|
+
settle(api.optShow()),
|
|
62
|
+
settle(api.requests()),
|
|
63
|
+
settle(api.health()),
|
|
64
|
+
]);
|
|
65
|
+
const s = summary ?? {};
|
|
66
|
+
out.requests = Number(s.total_requests ?? s.requests ?? 0);
|
|
67
|
+
out.savedTotal = Number(s.total_savings_usd ?? s.savings_usd ?? 0);
|
|
68
|
+
out.costTotal = Number(s.byte_cost_usd ?? s.cost_usd ?? 0);
|
|
69
|
+
out.tokensTotal = Number(s.total_tokens ?? s.tokens ?? 0);
|
|
70
|
+
out.providers = Number(s.providers ?? s.connections ?? 0);
|
|
71
|
+
out.models = Number(s.models ?? s.enabled_models ?? 0);
|
|
72
|
+
try {
|
|
73
|
+
out.email = (0, config_1.getProfile)(this.profileFrom(flags)).email;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
out.email = undefined;
|
|
77
|
+
}
|
|
78
|
+
const h = health ?? {};
|
|
79
|
+
out.gateway = h.status === 'ready' || h.status === 'ok' ? 'healthy' : h.status ? 'down' : 'unknown';
|
|
80
|
+
const buckets = Array.isArray(usage?.buckets) ? usage.buckets : Array.isArray(usage) ? usage : [];
|
|
81
|
+
out.savingsSeries = buckets.map((b) => Number(b.savings_usd ?? b.savings ?? 0));
|
|
82
|
+
const totalReq = buckets.reduce((n, b) => n + Number(b.requests ?? 0), 0);
|
|
83
|
+
const cached = buckets.reduce((n, b) => n + Number(b.cached ?? b.cache_hits ?? 0), 0);
|
|
84
|
+
let hitRate = totalReq ? cached / totalReq : Number(s.cache_hit_rate ?? s.cache_hit_rate_pct ?? 0);
|
|
85
|
+
if (hitRate > 1)
|
|
86
|
+
hitRate = hitRate / 100; // some summaries report a percentage, not a fraction
|
|
87
|
+
out.cacheHitRate = Math.max(0, Math.min(1, hitRate));
|
|
88
|
+
if (!out.requests && totalReq)
|
|
89
|
+
out.requests = totalReq;
|
|
90
|
+
const o = opt ?? {};
|
|
91
|
+
out.mode = String(o.summary?.default_mode ?? o.settings?.default_mode ?? o.default_mode ?? 'maximum');
|
|
92
|
+
const layers = Array.isArray(o.layers) ? o.layers : [];
|
|
93
|
+
out.layers = layers.map((l) => ({ label: String(l.plain_label ?? l.label ?? l.key ?? 'layer'), active: Boolean(l.enabled ?? l.active ?? l.status === 'active') }));
|
|
94
|
+
const list = Array.isArray(reqs?.requests) ? reqs.requests : Array.isArray(reqs) ? reqs : [];
|
|
95
|
+
out.recent = list.slice(0, 8).map((r) => ({
|
|
96
|
+
model: String(r.model ?? r.selected_alias ?? r.routed_model ?? r.alias ?? '—'),
|
|
97
|
+
status: String(r.status ?? 'ok'),
|
|
98
|
+
latencyMs: Number(r.total_latency_ms ?? r.latency_ms ?? 0),
|
|
99
|
+
tokens: Number(r.tokens_in ?? 0) + Number(r.tokens_out ?? 0) || Number(r.tokens ?? 0),
|
|
100
|
+
cache: r.cache_hit ? String(r.cache_hit_type ?? 'hit') : '',
|
|
101
|
+
}));
|
|
102
|
+
if (!out.tokensTotal)
|
|
103
|
+
out.tokensTotal = out.recent.reduce((n, r) => n + r.tokens, 0);
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
renderText(d) {
|
|
107
|
+
const lines = [
|
|
108
|
+
`byte dash — gateway ${d.gateway}, mode ${d.mode ?? 'maximum'}`,
|
|
109
|
+
`saved $${d.savedTotal.toFixed(4)} · requests ${d.requests} · cache ${Math.round(d.cacheHitRate * 100)}% · tokens ${d.tokensTotal}`,
|
|
110
|
+
`optimizations ${d.layers.filter((l) => l.active).length}/${d.layers.length} active · providers ${d.providers} · models ${d.models}`,
|
|
111
|
+
];
|
|
112
|
+
if (d.recent.length) {
|
|
113
|
+
lines.push('recent:');
|
|
114
|
+
for (const r of d.recent.slice(0, 8))
|
|
115
|
+
lines.push(` ${r.model} ${r.latencyMs}ms ${r.tokens}tok ${r.cache ?? ''}`);
|
|
116
|
+
}
|
|
117
|
+
return lines.join('\n');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
exports.default = Dash;
|
|
@@ -7,6 +7,7 @@ export default class Integrate extends BaseCommand {
|
|
|
7
7
|
};
|
|
8
8
|
static flags: {
|
|
9
9
|
list: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
status: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
shell: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
12
|
write: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
12
13
|
yes: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
@@ -15,7 +16,9 @@ export default class Integrate extends BaseCommand {
|
|
|
15
16
|
preset: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
16
17
|
};
|
|
17
18
|
run(): Promise<unknown>;
|
|
19
|
+
private recordWiring;
|
|
18
20
|
private writeSettings;
|
|
19
21
|
private messagesEndpointExists;
|
|
22
|
+
private showStatus;
|
|
20
23
|
private showList;
|
|
21
24
|
}
|
|
@@ -39,11 +39,14 @@ const node_path_1 = require("node:path");
|
|
|
39
39
|
const base_1 = require("../base");
|
|
40
40
|
const api_1 = require("../lib/api");
|
|
41
41
|
const errors_1 = require("../lib/errors");
|
|
42
|
+
const config_formats_1 = require("../lib/config-formats");
|
|
43
|
+
const detect_1 = require("../lib/detect");
|
|
42
44
|
const integrations_1 = require("../lib/integrations");
|
|
43
45
|
const shell_1 = require("../lib/shell");
|
|
44
46
|
const tty_1 = require("../lib/tty");
|
|
45
47
|
const ui = __importStar(require("../lib/ui"));
|
|
46
48
|
const util_1 = require("../lib/util");
|
|
49
|
+
const wiring_1 = require("../lib/wiring");
|
|
47
50
|
class Integrate extends base_1.BaseCommand {
|
|
48
51
|
static description = 'Wire a coding harness or SDK to Byte (Claude Code, Codex, opencode, Cursor, Cline, Aider, …).';
|
|
49
52
|
static examples = [
|
|
@@ -56,6 +59,7 @@ class Integrate extends base_1.BaseCommand {
|
|
|
56
59
|
};
|
|
57
60
|
static flags = {
|
|
58
61
|
list: core_1.Flags.boolean({ description: 'List supported targets and live readiness' }),
|
|
62
|
+
status: core_1.Flags.boolean({ description: 'Show installed tools and whether each is wired / unwired / manual' }),
|
|
59
63
|
shell: core_1.Flags.string({ description: 'Shell syntax for env output', options: ['bash', 'zsh', 'fish', 'powershell', 'cmd'] }),
|
|
60
64
|
write: core_1.Flags.boolean({ description: 'Write tool config files (a timestamped .bak is kept)' }),
|
|
61
65
|
yes: core_1.Flags.boolean({ char: 'y', description: 'Skip confirmation prompts' }),
|
|
@@ -70,6 +74,8 @@ class Integrate extends base_1.BaseCommand {
|
|
|
70
74
|
const { args, flags } = await this.parse(Integrate);
|
|
71
75
|
if (flags.list)
|
|
72
76
|
return this.showList(flags);
|
|
77
|
+
if (flags.status)
|
|
78
|
+
return this.showStatus(flags);
|
|
73
79
|
let target = args.target;
|
|
74
80
|
if (!target) {
|
|
75
81
|
if ((0, tty_1.interactive)() && !this.jsonEnabled()) {
|
|
@@ -112,8 +118,10 @@ class Integrate extends base_1.BaseCommand {
|
|
|
112
118
|
this.log(` ${line}`);
|
|
113
119
|
}
|
|
114
120
|
if (result.settingsFile) {
|
|
115
|
-
if (flags.write)
|
|
116
|
-
this.writeSettings(result.settingsFile)
|
|
121
|
+
if (flags.write) {
|
|
122
|
+
if (this.writeSettings(result.settingsFile))
|
|
123
|
+
this.recordWiring(flags, integ.key, modelAlias);
|
|
124
|
+
}
|
|
117
125
|
else {
|
|
118
126
|
this.log('');
|
|
119
127
|
this.log(` Tip: re-run with --write to update ${result.settingsFile.path} automatically.`);
|
|
@@ -121,26 +129,44 @@ class Integrate extends base_1.BaseCommand {
|
|
|
121
129
|
}
|
|
122
130
|
}
|
|
123
131
|
else if (flags.write && result.settingsFile) {
|
|
124
|
-
this.writeSettings(result.settingsFile)
|
|
132
|
+
if (this.writeSettings(result.settingsFile))
|
|
133
|
+
this.recordWiring(flags, integ.key, modelAlias);
|
|
125
134
|
}
|
|
126
135
|
return { target: integ.key, compat: integ.compat, model: modelAlias, env: Object.fromEntries(result.env) };
|
|
127
136
|
}
|
|
137
|
+
// Remember what we wired (and the model) so `byte sync` can re-apply on key/model changes.
|
|
138
|
+
recordWiring(flags, key, modelAlias) {
|
|
139
|
+
const profile = this.profileFrom(flags);
|
|
140
|
+
(0, wiring_1.addWiredTool)(profile, key);
|
|
141
|
+
(0, wiring_1.setSelectedModel)(profile, modelAlias);
|
|
142
|
+
}
|
|
128
143
|
writeSettings(file) {
|
|
144
|
+
const format = file.format ?? 'json';
|
|
129
145
|
let existing = {};
|
|
130
146
|
if ((0, node_fs_1.existsSync)(file.path)) {
|
|
131
147
|
try {
|
|
132
|
-
existing =
|
|
148
|
+
existing = (0, config_formats_1.parseConfig)(format, (0, node_fs_1.readFileSync)(file.path, 'utf8'));
|
|
133
149
|
}
|
|
134
150
|
catch {
|
|
135
|
-
|
|
151
|
+
this.warn(` Could not parse ${file.path} as ${format} — leaving it unchanged. Use the manual steps above.`);
|
|
152
|
+
return false;
|
|
136
153
|
}
|
|
137
154
|
const backup = `${file.path}.byte-bak-${Date.now()}`;
|
|
138
155
|
(0, node_fs_1.copyFileSync)(file.path, backup);
|
|
139
156
|
this.log(` Backed up existing config to ${backup}`);
|
|
140
157
|
}
|
|
158
|
+
let serialized;
|
|
159
|
+
try {
|
|
160
|
+
serialized = (0, config_formats_1.stringifyConfig)(format, file.apply(existing));
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
this.warn(` Could not render ${file.path} as ${format} — use the manual steps above.`);
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
141
166
|
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(file.path), { recursive: true });
|
|
142
|
-
(0, node_fs_1.writeFileSync)(file.path,
|
|
167
|
+
(0, node_fs_1.writeFileSync)(file.path, serialized);
|
|
143
168
|
this.log(` Updated ${file.path}`);
|
|
169
|
+
return true;
|
|
144
170
|
}
|
|
145
171
|
async messagesEndpointExists(base, byteKey) {
|
|
146
172
|
const probe = (async () => {
|
|
@@ -157,6 +183,32 @@ class Integrate extends base_1.BaseCommand {
|
|
|
157
183
|
// Never block the UX on a slow gateway; assume reachable on timeout.
|
|
158
184
|
return (0, util_1.withTimeout)(probe, 4000, true);
|
|
159
185
|
}
|
|
186
|
+
// Auto-detect installed agents and report each as wired / unwired / manual / not-installed,
|
|
187
|
+
// so a user can see at a glance what `byte connect` will (or did) configure.
|
|
188
|
+
async showStatus(flags) {
|
|
189
|
+
const base = this.baseUrlFrom(flags);
|
|
190
|
+
const byteKey = this.optionalByteKey(flags) || '';
|
|
191
|
+
const rows = (0, detect_1.detectTools)().map((d) => {
|
|
192
|
+
let wired = false;
|
|
193
|
+
const integ = (0, integrations_1.findIntegration)(d.key);
|
|
194
|
+
const file = integ?.build({ baseUrl: base, byteKey, modelAlias: 'byte-default' }).settingsFile;
|
|
195
|
+
if (file && (0, node_fs_1.existsSync)(file.path)) {
|
|
196
|
+
try {
|
|
197
|
+
wired = (0, node_fs_1.readFileSync)(file.path, 'utf8').toLowerCase().includes('byte');
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
wired = false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const state = !d.installed ? 'not-installed' : !d.canAutoWrite ? 'manual' : wired ? 'wired' : 'unwired';
|
|
204
|
+
return { target: d.key, title: d.title, installed: d.installed, state };
|
|
205
|
+
});
|
|
206
|
+
if (this.jsonEnabled())
|
|
207
|
+
return rows;
|
|
208
|
+
for (const r of rows)
|
|
209
|
+
this.log(`${r.target.padEnd(16)} ${r.title.padEnd(22)} ${r.state}`);
|
|
210
|
+
return rows;
|
|
211
|
+
}
|
|
160
212
|
async showList(flags) {
|
|
161
213
|
const targets = (0, integrations_1.listIntegrations)();
|
|
162
214
|
const key = flags.key || this.optionalByteKey(flags);
|
|
@@ -2,21 +2,29 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const core_1 = require("@oclif/core");
|
|
4
4
|
const base_1 = require("../../base");
|
|
5
|
+
const credentials_1 = require("../../lib/credentials");
|
|
5
6
|
class KeysRotate extends base_1.BaseCommand {
|
|
6
7
|
static description = 'Rotate a Byte API key: issue a new secret and invalidate the old one.';
|
|
7
8
|
static args = {
|
|
8
9
|
id: core_1.Args.integer({ description: 'Key id (see `byte keys list`)', required: true }),
|
|
9
10
|
};
|
|
11
|
+
static flags = {
|
|
12
|
+
sync: core_1.Flags.boolean({ description: 'Save the new secret to this profile and re-apply it to all wired tools (`byte sync`)' }),
|
|
13
|
+
};
|
|
10
14
|
async run() {
|
|
11
15
|
const { args, flags } = await this.parse(KeysRotate);
|
|
12
16
|
this.requireToken(flags);
|
|
13
17
|
const res = await this.api(flags).keysRotate(args.id);
|
|
18
|
+
if (flags.sync && res?.key) {
|
|
19
|
+
(0, credentials_1.setByteKey)(this.profileFrom(flags), String(res.key));
|
|
20
|
+
await this.config.runCommand('sync', ['--yes']);
|
|
21
|
+
}
|
|
14
22
|
if (!this.jsonEnabled()) {
|
|
15
23
|
this.log(`Rotated key ${args.id}. New secret (shown once):`);
|
|
16
24
|
this.log('');
|
|
17
25
|
this.log(` ${res.key}`);
|
|
18
26
|
this.log('');
|
|
19
|
-
this.log('Update any integration that used the old secret
|
|
27
|
+
this.log(flags.sync ? 'Saved to this profile and re-synced all wired tools.' : 'Update any integration that used the old secret — or run `byte keys` then `byte sync`.');
|
|
20
28
|
}
|
|
21
29
|
return res;
|
|
22
30
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base';
|
|
2
|
+
export default class McpAdd extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
name: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
url: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
header: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<unknown>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const base_1 = require("../../base");
|
|
5
|
+
class McpAdd extends base_1.BaseCommand {
|
|
6
|
+
static description = 'Register an MCP server for Byte to host — its tools become available to your models (namespaced mcp__<name>__*).';
|
|
7
|
+
static examples = ['<%= config.bin %> mcp add filesystem --url https://mcp.example.com/rpc'];
|
|
8
|
+
static args = {
|
|
9
|
+
name: core_1.Args.string({ description: 'Short name (namespaces the tools)', required: true }),
|
|
10
|
+
};
|
|
11
|
+
static flags = {
|
|
12
|
+
url: core_1.Flags.string({ description: 'MCP server URL (streamable HTTP)', required: true }),
|
|
13
|
+
header: core_1.Flags.string({ description: 'Auth header as KEY:VALUE (repeatable)', multiple: true }),
|
|
14
|
+
};
|
|
15
|
+
async run() {
|
|
16
|
+
const { args, flags } = await this.parse(McpAdd);
|
|
17
|
+
this.requireToken(flags);
|
|
18
|
+
const headers = {};
|
|
19
|
+
for (const entry of flags.header ?? []) {
|
|
20
|
+
const idx = entry.indexOf(':');
|
|
21
|
+
if (idx > 0)
|
|
22
|
+
headers[entry.slice(0, idx).trim()] = entry.slice(idx + 1).trim();
|
|
23
|
+
}
|
|
24
|
+
const res = await this.api(flags).mcpAdd({ name: args.name, url: flags.url, headers });
|
|
25
|
+
if (this.jsonEnabled())
|
|
26
|
+
return res;
|
|
27
|
+
this.log(`Registered MCP server "${res.name}" (id ${res.id}). Verify it with \`byte mcp test ${res.id}\`.`);
|
|
28
|
+
return res;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.default = McpAdd;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const base_1 = require("../../base");
|
|
4
|
+
const output_1 = require("../../lib/output");
|
|
5
|
+
class McpList extends base_1.BaseCommand {
|
|
6
|
+
static description = 'List the MCP servers registered for this org.';
|
|
7
|
+
async run() {
|
|
8
|
+
const { flags } = await this.parse(McpList);
|
|
9
|
+
this.requireToken(flags);
|
|
10
|
+
const res = await this.api(flags).mcpServers();
|
|
11
|
+
const servers = res.servers ?? [];
|
|
12
|
+
if (this.jsonEnabled())
|
|
13
|
+
return servers;
|
|
14
|
+
if (!servers.length) {
|
|
15
|
+
this.log('No MCP servers registered. Add one with `byte mcp add <name> --url ...`.');
|
|
16
|
+
return servers;
|
|
17
|
+
}
|
|
18
|
+
this.log((0, output_1.renderTable)(['ID', 'Name', 'URL', 'Transport', 'Enabled'], servers.map((s) => [String(s.id), s.name, s.url, s.transport, s.enabled ? 'yes' : 'no'])));
|
|
19
|
+
return servers;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.default = McpList;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base';
|
|
2
|
+
export default class McpRemove extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static args: {
|
|
5
|
+
id: import("@oclif/core/lib/interfaces").Arg<number, {
|
|
6
|
+
max?: number;
|
|
7
|
+
min?: number;
|
|
8
|
+
}>;
|
|
9
|
+
};
|
|
10
|
+
run(): Promise<unknown>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const base_1 = require("../../base");
|
|
5
|
+
class McpRemove extends base_1.BaseCommand {
|
|
6
|
+
static description = 'Remove a registered MCP server.';
|
|
7
|
+
static args = {
|
|
8
|
+
id: core_1.Args.integer({ description: 'Server id (see `byte mcp list`)', required: true }),
|
|
9
|
+
};
|
|
10
|
+
async run() {
|
|
11
|
+
const { args, flags } = await this.parse(McpRemove);
|
|
12
|
+
this.requireToken(flags);
|
|
13
|
+
await this.api(flags).mcpRemove(args.id);
|
|
14
|
+
if (!this.jsonEnabled())
|
|
15
|
+
this.log(`Removed MCP server ${args.id}.`);
|
|
16
|
+
return { status: 'removed', id: args.id };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.default = McpRemove;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base';
|
|
2
|
+
export default class McpTest extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static args: {
|
|
5
|
+
id: import("@oclif/core/lib/interfaces").Arg<number, {
|
|
6
|
+
max?: number;
|
|
7
|
+
min?: number;
|
|
8
|
+
}>;
|
|
9
|
+
};
|
|
10
|
+
run(): Promise<unknown>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const base_1 = require("../../base");
|
|
5
|
+
class McpTest extends base_1.BaseCommand {
|
|
6
|
+
static description = 'Verify a registered MCP server by listing the tools it exposes.';
|
|
7
|
+
static args = {
|
|
8
|
+
id: core_1.Args.integer({ description: 'Server id (see `byte mcp list`)', required: true }),
|
|
9
|
+
};
|
|
10
|
+
async run() {
|
|
11
|
+
const { args, flags } = await this.parse(McpTest);
|
|
12
|
+
this.requireToken(flags);
|
|
13
|
+
const res = await this.api(flags).mcpTest(args.id);
|
|
14
|
+
if (this.jsonEnabled())
|
|
15
|
+
return res;
|
|
16
|
+
this.log(`MCP server ${args.id} reachable — ${res.tools} tool(s): ${(res.tool_names ?? []).join(', ')}`);
|
|
17
|
+
return res;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.default = McpTest;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base';
|
|
2
|
+
export default class ProvidersCompare extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
alias: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
all: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
off: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<unknown>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const base_1 = require("../../base");
|
|
5
|
+
class ProvidersCompare extends base_1.BaseCommand {
|
|
6
|
+
static description = 'Enable (or disable) direct-vs-Byte comparison on imported models. Run with --all so `byte compare` works out of the box.';
|
|
7
|
+
static examples = [
|
|
8
|
+
'<%= config.bin %> providers compare --all',
|
|
9
|
+
'<%= config.bin %> providers compare byte-2-deepseek-v4-flash',
|
|
10
|
+
'<%= config.bin %> providers compare byte-2-deepseek-v4-flash --off',
|
|
11
|
+
];
|
|
12
|
+
static args = {
|
|
13
|
+
alias: core_1.Args.string({ description: 'Model byte alias (omit when using --all)', required: false }),
|
|
14
|
+
};
|
|
15
|
+
static flags = {
|
|
16
|
+
all: core_1.Flags.boolean({ description: 'Apply to every priced model in the org' }),
|
|
17
|
+
off: core_1.Flags.boolean({ description: 'Disable comparison instead of enabling it' }),
|
|
18
|
+
};
|
|
19
|
+
async run() {
|
|
20
|
+
const { args, flags } = await this.parse(ProvidersCompare);
|
|
21
|
+
this.requireToken(flags);
|
|
22
|
+
const api = this.api(flags);
|
|
23
|
+
const on = !flags.off;
|
|
24
|
+
const res = await api.modelsList();
|
|
25
|
+
const list = Array.isArray(res) ? res : res?.models ?? [];
|
|
26
|
+
let targets;
|
|
27
|
+
if (flags.all)
|
|
28
|
+
targets = list.filter((m) => m.pricing_status === 'verified' || m.input_price_per_million);
|
|
29
|
+
else if (args.alias)
|
|
30
|
+
targets = list.filter((m) => m.byte_alias === args.alias);
|
|
31
|
+
else
|
|
32
|
+
return this.error('Pass a model alias or --all. See `byte providers list`.', { exit: 2 });
|
|
33
|
+
if (!targets.length) {
|
|
34
|
+
return this.error(args.alias ? `No model with alias "${args.alias}".` : 'No priced models found to enable.', { exit: 2 });
|
|
35
|
+
}
|
|
36
|
+
for (const model of targets) {
|
|
37
|
+
// sequential keeps ordering stable and surfaces the first failure clearly
|
|
38
|
+
// eslint-disable-next-line no-await-in-loop
|
|
39
|
+
await api.modelCompare(model.id, on);
|
|
40
|
+
}
|
|
41
|
+
const verb = on ? 'enabled' : 'disabled';
|
|
42
|
+
const aliases = targets.map((m) => m.byte_alias);
|
|
43
|
+
if (this.jsonEnabled())
|
|
44
|
+
return { compare: verb, models: aliases };
|
|
45
|
+
this.log(`Comparison ${verb} on: ${aliases.join(', ')}`);
|
|
46
|
+
if (on && targets.length < 2 && !flags.all) {
|
|
47
|
+
this.log('Tip: `byte compare` (auto) needs 2+ models with comparison enabled — re-run with --all.');
|
|
48
|
+
}
|
|
49
|
+
return { compare: verb, models: aliases };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.default = ProvidersCompare;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BaseCommand } from '../base';
|
|
2
|
+
export default class Sync extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
model: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
7
|
+
yes: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
};
|
|
9
|
+
run(): Promise<unknown>;
|
|
10
|
+
}
|