gearbox-code 0.1.7 → 0.1.9
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 +49 -73
- package/dist/cli.mjs +274 -5
- package/install.ps1 +6 -0
- package/install.sh +10 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,117 +1,93 @@
|
|
|
1
|
-
#
|
|
1
|
+
# gearbox
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Install
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
macOS, Linux, WSL:
|
|
6
6
|
|
|
7
|
+
```bash
|
|
8
|
+
curl -fsSL https://unpkg.com/gearbox-code@latest/install.sh | bash
|
|
7
9
|
```
|
|
8
|
-
⚙ gearbox
|
|
9
|
-
coding harness · sonnet-4.6
|
|
10
|
-
──────────────────────────────────────────────────────────
|
|
11
|
-
|
|
12
|
-
› add a --json flag to the CLI and cover it with a test
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
✓ read_file src/cli.tsx
|
|
17
|
-
renders the Ink app · 18 lines
|
|
18
|
-
✓ edit_file src/cli.tsx
|
|
19
|
-
✓ run_shell bun test
|
|
20
|
-
9 pass · 0 fail
|
|
21
|
-
|
|
22
|
-
⏺ Done — flag added with a passing test.
|
|
11
|
+
Windows PowerShell:
|
|
23
12
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
╰──────────────────────────────────────────────────────────╯
|
|
27
|
-
gearbox · sonnet-4.6 · 18,432 tok · ⏎ send ctrl+c quit
|
|
13
|
+
```powershell
|
|
14
|
+
irm https://unpkg.com/gearbox-code@latest/install.ps1 | iex
|
|
28
15
|
```
|
|
29
16
|
|
|
30
|
-
|
|
17
|
+
These installers do not use `sudo`, admin privileges, or `npm install -g`.
|
|
18
|
+
They install Gearbox into a user-owned directory, create the `gearbox` command,
|
|
19
|
+
then start onboarding before the coding app opens.
|
|
31
20
|
|
|
32
|
-
|
|
33
|
-
bun install
|
|
34
|
-
gearbox auth add <api-key> # paste-detects common providers when possible
|
|
35
|
-
# or: gearbox auth add <provider> <api-key>
|
|
36
|
-
# or: gearbox auth import # import keys from env/cloud credentials
|
|
37
|
-
bun start # or: bun run src/cli.tsx
|
|
38
|
-
bun start -- --model gemini-flash # pick a model
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
No provider configured? Gearbox opens a setup screen and will not run a fake model. Preview the look without running anything:
|
|
21
|
+
Run without installing:
|
|
42
22
|
|
|
43
23
|
```bash
|
|
44
|
-
|
|
24
|
+
npx gearbox-code@latest
|
|
45
25
|
```
|
|
46
26
|
|
|
47
|
-
##
|
|
27
|
+
## First Run
|
|
48
28
|
|
|
49
|
-
|
|
29
|
+
Gearbox needs one provider account before it opens the coding app. The installer
|
|
30
|
+
runs setup automatically. You can also run it yourself:
|
|
50
31
|
|
|
51
32
|
```bash
|
|
52
|
-
|
|
33
|
+
gearbox onboard
|
|
53
34
|
```
|
|
54
35
|
|
|
55
|
-
|
|
36
|
+
Common setup commands:
|
|
56
37
|
|
|
57
|
-
```
|
|
58
|
-
|
|
38
|
+
```bash
|
|
39
|
+
gearbox auth add <api-key> # auto-detects known key prefixes
|
|
40
|
+
gearbox auth add <provider> <api-key> # anthropic, openai, google, deepseek, openrouter, groq, xai, mistral...
|
|
41
|
+
gearbox auth import # import credentials from env/cloud config
|
|
42
|
+
gearbox auth providers # list supported providers
|
|
59
43
|
```
|
|
60
44
|
|
|
61
|
-
|
|
62
|
-
`gearbox-code` tarball, and create a user-owned `gearbox` command. They avoid
|
|
63
|
-
`sudo`, admin privileges, and `npm install -g`.
|
|
64
|
-
|
|
65
|
-
Then:
|
|
45
|
+
After setup:
|
|
66
46
|
|
|
67
47
|
```bash
|
|
68
|
-
|
|
69
|
-
|
|
48
|
+
cd ~/your-project
|
|
49
|
+
gearbox
|
|
70
50
|
```
|
|
71
51
|
|
|
72
|
-
|
|
73
|
-
add to your shell config.
|
|
52
|
+
No account configured means no fake/demo model: Gearbox runs onboarding first.
|
|
74
53
|
|
|
75
|
-
|
|
54
|
+
## Uninstall
|
|
55
|
+
|
|
56
|
+
macOS, Linux, WSL:
|
|
76
57
|
|
|
77
58
|
```bash
|
|
78
|
-
|
|
59
|
+
rm -f ~/.local/bin/gearbox
|
|
60
|
+
rm -rf ~/.local/share/gearbox
|
|
79
61
|
```
|
|
80
62
|
|
|
81
|
-
|
|
82
|
-
for git checkouts.
|
|
63
|
+
Windows PowerShell:
|
|
83
64
|
|
|
84
|
-
|
|
65
|
+
```powershell
|
|
66
|
+
Remove-Item "$env:LOCALAPPDATA\Gearbox" -Recurse -Force
|
|
67
|
+
```
|
|
85
68
|
|
|
86
|
-
|
|
69
|
+
If you previously installed with npm global:
|
|
87
70
|
|
|
88
71
|
```bash
|
|
89
|
-
|
|
90
|
-
bun run src/cli.tsx
|
|
72
|
+
npm uninstall -g gearbox-code
|
|
91
73
|
```
|
|
92
74
|
|
|
93
|
-
|
|
75
|
+
## What It Is
|
|
94
76
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
```
|
|
77
|
+
Gearbox is a terminal coding agent that can use the model accounts you already
|
|
78
|
+
pay for. It supports provider accounts, local credential storage, model routing,
|
|
79
|
+
session history, file edits, shell commands, and permission gates.
|
|
99
80
|
|
|
100
|
-
|
|
81
|
+
Supported setup paths include API keys, detected env/cloud credentials, Azure,
|
|
82
|
+
and provider CLIs where available.
|
|
101
83
|
|
|
102
84
|
## Develop
|
|
103
85
|
|
|
86
|
+
Requires [Bun](https://bun.sh).
|
|
87
|
+
|
|
104
88
|
```bash
|
|
105
|
-
bun
|
|
89
|
+
bun install
|
|
90
|
+
bun run src/cli.tsx
|
|
91
|
+
bun test
|
|
106
92
|
bun run typecheck
|
|
107
93
|
```
|
|
108
|
-
|
|
109
|
-
## Principles
|
|
110
|
-
|
|
111
|
-
- **Open + free.** MIT. No paid dependencies, no hosted backend, no telemetry. The only cost is your own model calls on your own keys.
|
|
112
|
-
- **Beautiful + calm.** One accent color, generous spacing, consistent glyphs. The whole look lives in `src/ui/theme.ts`.
|
|
113
|
-
- **Routing-ready.** Model choice happens in exactly one place (`src/model/selector.ts`); the router drops in there later with no changes upstream. See [`CLAUDE.md`](./CLAUDE.md).
|
|
114
|
-
|
|
115
|
-
## Status
|
|
116
|
-
|
|
117
|
-
v0.1 — streaming agent loop, real file + shell tools, a polished Ink TUI, multi-provider support, accounts + spend ledger, BM25 context retrieval, and basic per-task routing (classify → quality bar → cheapest winner). The richer routing engine (shadow-eval, credit/limit/plan scoring, per-repo calibration) is next (`DESIGN.md`).
|
package/dist/cli.mjs
CHANGED
|
@@ -63894,8 +63894,18 @@ var require_src6 = __commonJS((exports) => {
|
|
|
63894
63894
|
});
|
|
63895
63895
|
|
|
63896
63896
|
// src/proc.ts
|
|
63897
|
+
var exports_proc = {};
|
|
63898
|
+
__export(exports_proc, {
|
|
63899
|
+
write: () => writeFile,
|
|
63900
|
+
which: () => which,
|
|
63901
|
+
spawnSyncProc: () => spawnSyncProc,
|
|
63902
|
+
spawnProc: () => spawnProc,
|
|
63903
|
+
readStream: () => readStream,
|
|
63904
|
+
Glob: () => Glob
|
|
63905
|
+
});
|
|
63897
63906
|
import { spawn, spawnSync as nodeSpawnSync, execFileSync } from "node:child_process";
|
|
63898
63907
|
import { readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
|
|
63908
|
+
import { writeFile } from "node:fs/promises";
|
|
63899
63909
|
import { resolve as resolve6 } from "node:path";
|
|
63900
63910
|
function which(bin) {
|
|
63901
63911
|
try {
|
|
@@ -64270,6 +64280,13 @@ var init_onboarding = __esm(() => {
|
|
|
64270
64280
|
});
|
|
64271
64281
|
|
|
64272
64282
|
// src/agent/cli-backend.ts
|
|
64283
|
+
var exports_cli_backend = {};
|
|
64284
|
+
__export(exports_cli_backend, {
|
|
64285
|
+
subscriptionEnv: () => subscriptionEnv,
|
|
64286
|
+
runCliTask: () => runCliTask,
|
|
64287
|
+
parseCliLines: () => parseCliLines,
|
|
64288
|
+
buildCliArgs: () => buildCliArgs
|
|
64289
|
+
});
|
|
64273
64290
|
function newState() {
|
|
64274
64291
|
return { text: "", usage: { inputTokens: 0, outputTokens: 0 }, rates: new Map, toolNames: new Map };
|
|
64275
64292
|
}
|
|
@@ -64407,6 +64424,22 @@ function buildCliArgs(binary, prompt, opts = {}) {
|
|
|
64407
64424
|
args.push("--resume", opts.sessionId);
|
|
64408
64425
|
return args;
|
|
64409
64426
|
}
|
|
64427
|
+
function parseCliLines(binary, lines, onEvent) {
|
|
64428
|
+
const state = newState();
|
|
64429
|
+
for (const line of lines) {
|
|
64430
|
+
const t2 = line.trim();
|
|
64431
|
+
if (!t2)
|
|
64432
|
+
continue;
|
|
64433
|
+
let obj;
|
|
64434
|
+
try {
|
|
64435
|
+
obj = JSON.parse(t2);
|
|
64436
|
+
} catch {
|
|
64437
|
+
continue;
|
|
64438
|
+
}
|
|
64439
|
+
mapCliEvent(binary, obj, state, onEvent);
|
|
64440
|
+
}
|
|
64441
|
+
return finalize(state);
|
|
64442
|
+
}
|
|
64410
64443
|
function finalize(state) {
|
|
64411
64444
|
return { messages: [], usage: state.usage, sessionId: state.sessionId, costUSD: state.costUSD, rates: [...state.rates.values()] };
|
|
64412
64445
|
}
|
|
@@ -70267,7 +70300,8 @@ var import_react20 = __toESM(require_react(), 1);
|
|
|
70267
70300
|
// node_modules/ink/build/hooks/use-focus-manager.js
|
|
70268
70301
|
var import_react21 = __toESM(require_react(), 1);
|
|
70269
70302
|
// src/cli.tsx
|
|
70270
|
-
import {
|
|
70303
|
+
import { createInterface } from "node:readline/promises";
|
|
70304
|
+
import { execFileSync as execFileSync4, spawnSync } from "node:child_process";
|
|
70271
70305
|
import { resolve as resolve11 } from "node:path";
|
|
70272
70306
|
import { existsSync as existsSync8 } from "node:fs";
|
|
70273
70307
|
|
|
@@ -121288,6 +121322,9 @@ function pickDefaultModel(preferredId) {
|
|
|
121288
121322
|
return wanted;
|
|
121289
121323
|
return MODELS.find((m2) => providerAvailable(m2.provider));
|
|
121290
121324
|
}
|
|
121325
|
+
function anyProviderAvailable() {
|
|
121326
|
+
return MODELS.some((m2) => providerAvailable(m2.provider));
|
|
121327
|
+
}
|
|
121291
121328
|
|
|
121292
121329
|
// src/model/selector.ts
|
|
121293
121330
|
class FixedSelector {
|
|
@@ -131808,7 +131845,7 @@ var uiMessagesSchema = lazyValidator3(() => zodSchema7(exports_external.array(ex
|
|
|
131808
131845
|
})).nonempty("Messages array must not be empty")));
|
|
131809
131846
|
|
|
131810
131847
|
// src/tools.ts
|
|
131811
|
-
import { readFile, writeFile, readdir, stat as stat2 } from "node:fs/promises";
|
|
131848
|
+
import { readFile, writeFile as writeFile2, readdir, stat as stat2 } from "node:fs/promises";
|
|
131812
131849
|
import { existsSync as existsSync3 } from "node:fs";
|
|
131813
131850
|
import { resolve as resolve7, relative, isAbsolute } from "node:path";
|
|
131814
131851
|
// node_modules/diff/libesm/diff/base.js
|
|
@@ -132171,7 +132208,7 @@ function createTools(onEvent) {
|
|
|
132171
132208
|
if (!await requestPermission({ kind: "write", title: exists ? "Overwrite a file" : "Create a file", detail: path }))
|
|
132172
132209
|
throw new Error(DENIED);
|
|
132173
132210
|
const before2 = exists ? await readFile(abs, "utf8") : "";
|
|
132174
|
-
await
|
|
132211
|
+
await writeFile2(abs, content, "utf8");
|
|
132175
132212
|
const diff2 = computeDiff(before2, content);
|
|
132176
132213
|
return { summary: `wrote ${path} (${diffStat(diff2)})`, diff: diff2 };
|
|
132177
132214
|
}
|
|
@@ -132187,7 +132224,7 @@ function createTools(onEvent) {
|
|
|
132187
132224
|
if (!await requestPermission({ kind: "edit", title: "Edit a file", detail: path }))
|
|
132188
132225
|
throw new Error(DENIED);
|
|
132189
132226
|
const after2 = before2.replace(find2, replace2);
|
|
132190
|
-
await
|
|
132227
|
+
await writeFile2(abs, after2, "utf8");
|
|
132191
132228
|
const diff2 = computeDiff(before2, after2);
|
|
132192
132229
|
return { summary: `edited ${path} (${diffStat(diff2)})`, diff: diff2 };
|
|
132193
132230
|
}
|
|
@@ -136244,8 +136281,222 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
|
|
|
136244
136281
|
var jsx_dev_runtime13 = __toESM(require_jsx_dev_runtime(), 1);
|
|
136245
136282
|
process.env.LANG = process.env.LANG || "en_US.UTF-8";
|
|
136246
136283
|
process.env.LC_ALL = process.env.LC_ALL || "en_US.UTF-8";
|
|
136247
|
-
var VERSION16 = "0.1.
|
|
136284
|
+
var VERSION16 = "0.1.9";
|
|
136248
136285
|
var args = process.argv.slice(2);
|
|
136286
|
+
var supportsAnsi = process.env.NO_COLOR !== "1" && process.env.TERM !== "dumb" && (process.stdout.isTTY || process.env.FORCE_COLOR === "1");
|
|
136287
|
+
var ansi = (code) => supportsAnsi ? `\x1B[${code}m` : "";
|
|
136288
|
+
var paint = (code, text2) => `${ansi(code)}${text2}${ansi("0")}`;
|
|
136289
|
+
var bold = (text2) => paint("1", text2);
|
|
136290
|
+
var accent = (text2) => paint("36", text2);
|
|
136291
|
+
var dim = (text2) => paint("2", text2);
|
|
136292
|
+
var ok = (text2) => paint("32", text2);
|
|
136293
|
+
var warn = (text2) => paint("33", text2);
|
|
136294
|
+
var errColor = (text2) => paint("31", text2);
|
|
136295
|
+
var stripAnsi2 = (text2) => text2.replace(/\x1b\[[0-9;]*m/g, "");
|
|
136296
|
+
var visibleLength = (text2) => stripAnsi2(text2).length;
|
|
136297
|
+
var padVisible = (text2, width) => text2 + " ".repeat(Math.max(width - visibleLength(text2), 0));
|
|
136298
|
+
function onboardingBoo() {
|
|
136299
|
+
return [
|
|
136300
|
+
" .-''''-.",
|
|
136301
|
+
" .' .--. '.",
|
|
136302
|
+
" / ( ) \\",
|
|
136303
|
+
" | .-. .-. |",
|
|
136304
|
+
" | |_| |_| |",
|
|
136305
|
+
" | __ |",
|
|
136306
|
+
" \\ .' '. /",
|
|
136307
|
+
" '._\\____/_.'",
|
|
136308
|
+
" /| |\\"
|
|
136309
|
+
].join(`
|
|
136310
|
+
`);
|
|
136311
|
+
}
|
|
136312
|
+
function box(title, lines) {
|
|
136313
|
+
const width = Math.min(78, Math.max(title.length + 4, ...lines.map((l) => visibleLength(l) + 4)));
|
|
136314
|
+
const rule = "─".repeat(width - 2);
|
|
136315
|
+
console.log(accent(`╭${rule}╮`));
|
|
136316
|
+
console.log(accent("│ ") + padVisible(bold(title), width - 3) + accent("│"));
|
|
136317
|
+
console.log(accent(`├${rule}┤`));
|
|
136318
|
+
for (const line of lines) {
|
|
136319
|
+
console.log(accent("│ ") + padVisible(line, width - 3) + accent("│"));
|
|
136320
|
+
}
|
|
136321
|
+
console.log(accent(`╰${rule}╯`));
|
|
136322
|
+
}
|
|
136323
|
+
function optionLine(key, label, detail) {
|
|
136324
|
+
return `${accent(key.padStart(2))} ${bold(label)} ${dim(detail)}`;
|
|
136325
|
+
}
|
|
136326
|
+
async function runCliOnboarding() {
|
|
136327
|
+
const { listAccounts: listAccounts2 } = await Promise.resolve().then(() => (init_store(), exports_store));
|
|
136328
|
+
const { importableEnvCreds: importableEnvCreds2, importEnvCred: importEnvCred2, importableCloudCreds: importableCloudCreds2, importCloudCred: importCloudCred2 } = await Promise.resolve().then(() => (init_detect(), exports_detect));
|
|
136329
|
+
const { addApiKeyAccount: addApiKeyAccount2, addAzureAccount: addAzureAccount2, addAzureFoundryAccount: addAzureFoundryAccount2, addByPastedKey: addByPastedKey2, testAccount: testAccount2, addableProviders: addableProviders2, addCliAccount: addCliAccount2, cliAuthStatus: cliAuthStatus2, cliLoginArgs: cliLoginArgs2 } = await Promise.resolve().then(() => (init_onboard(), exports_onboard));
|
|
136330
|
+
const { subscriptionEnv: subscriptionEnv2 } = await Promise.resolve().then(() => (init_cli_backend(), exports_cli_backend));
|
|
136331
|
+
const { detectProviderByKey: detectProviderByKey2 } = await Promise.resolve().then(() => (init_catalog(), exports_catalog));
|
|
136332
|
+
const { which: which2 } = await Promise.resolve().then(() => (init_proc(), exports_proc));
|
|
136333
|
+
const pipedAnswers = process.stdin.isTTY ? null : (await readStdin()).split(/\r?\n/);
|
|
136334
|
+
const rl = pipedAnswers ? null : createInterface({ input: process.stdin, output: process.stdout });
|
|
136335
|
+
const ask = async (q) => {
|
|
136336
|
+
if (pipedAnswers) {
|
|
136337
|
+
const answer = (pipedAnswers.shift() ?? "").trim();
|
|
136338
|
+
console.log(accent(q) + answer);
|
|
136339
|
+
return answer;
|
|
136340
|
+
}
|
|
136341
|
+
return (await rl.question(accent(q))).trim();
|
|
136342
|
+
};
|
|
136343
|
+
const providerRows = () => addableProviders2().map((p) => ` ${accent(p.id.padEnd(16))} ${p.label}`).join(`
|
|
136344
|
+
`);
|
|
136345
|
+
const testAndReport = async (account) => {
|
|
136346
|
+
console.log(dim("Testing credential with the provider..."));
|
|
136347
|
+
const t2 = await testAccount2(account);
|
|
136348
|
+
console.log(t2.ok ? ok(`Credential works: ${t2.message}`) : warn(`Stored, but the live test failed: ${t2.message}`));
|
|
136349
|
+
};
|
|
136350
|
+
const addSubscription = async (provider) => {
|
|
136351
|
+
const res = addCliAccount2(provider);
|
|
136352
|
+
console.log(res.ok ? ok(res.message) : errColor(res.message));
|
|
136353
|
+
if (!res.ok || !res.account || res.account.auth.kind !== "cli")
|
|
136354
|
+
return false;
|
|
136355
|
+
const bin = res.account.auth.binary;
|
|
136356
|
+
const profile = res.account.auth.loginProfile;
|
|
136357
|
+
let status = await cliAuthStatus2(bin, profile);
|
|
136358
|
+
if (!status.loggedIn) {
|
|
136359
|
+
console.log(dim(`Starting ${bin} sign-in in this terminal...`));
|
|
136360
|
+
const r2 = spawnSync(bin, cliLoginArgs2(bin), { stdio: "inherit", env: subscriptionEnv2(bin, profile) });
|
|
136361
|
+
if ((r2.status ?? 1) !== 0)
|
|
136362
|
+
return false;
|
|
136363
|
+
status = await cliAuthStatus2(bin, profile);
|
|
136364
|
+
}
|
|
136365
|
+
if (!status.loggedIn) {
|
|
136366
|
+
console.log(warn(`${bin} did not report a completed sign-in.`));
|
|
136367
|
+
return false;
|
|
136368
|
+
}
|
|
136369
|
+
console.log(ok(`${bin} subscription ready${status.detail ? ` (${status.detail})` : ""}`));
|
|
136370
|
+
return true;
|
|
136371
|
+
};
|
|
136372
|
+
try {
|
|
136373
|
+
console.log("");
|
|
136374
|
+
if (supportsAnsi)
|
|
136375
|
+
console.log(paint("38;5;117", onboardingBoo()));
|
|
136376
|
+
else
|
|
136377
|
+
console.log(onboardingBoo());
|
|
136378
|
+
console.log("");
|
|
136379
|
+
console.log(bold("Gearbox setup"));
|
|
136380
|
+
console.log("Boo needs one model account before the coding app opens.");
|
|
136381
|
+
console.log(dim("Your credentials stay local. API keys are stored in Gearbox's credential store; subscription sign-ins stay inside the vendor CLI."));
|
|
136382
|
+
console.log("");
|
|
136383
|
+
while (!anyProviderAvailable()) {
|
|
136384
|
+
const env3 = importableEnvCreds2();
|
|
136385
|
+
const cloud = importableCloudCreds2();
|
|
136386
|
+
const existing = listAccounts2();
|
|
136387
|
+
if (existing.length)
|
|
136388
|
+
break;
|
|
136389
|
+
const options2 = [];
|
|
136390
|
+
if (env3.length || cloud.length) {
|
|
136391
|
+
const names = [...env3.map((c) => c.envVar), ...cloud.map((c) => `${c.label} (${c.source})`)];
|
|
136392
|
+
options2.push(optionLine("1", "Import detected credentials", names.join(", ")));
|
|
136393
|
+
}
|
|
136394
|
+
options2.push(optionLine("2", "Paste API key", "auto-detects common key prefixes"));
|
|
136395
|
+
options2.push(optionLine("3", "Choose provider + key", "Anthropic, OpenAI, Gemini, OpenRouter, Groq, ..."));
|
|
136396
|
+
options2.push(optionLine("4", "Azure endpoint + key", "Azure OpenAI or Azure AI Foundry"));
|
|
136397
|
+
if (which2("claude"))
|
|
136398
|
+
options2.push(optionLine("5", "Claude subscription", "uses the official claude CLI; no token extraction"));
|
|
136399
|
+
if (which2("codex"))
|
|
136400
|
+
options2.push(optionLine("6", "ChatGPT subscription", "uses the official codex CLI; no token extraction"));
|
|
136401
|
+
options2.push(optionLine("p", "Show provider catalog", "all API-key providers Gearbox knows how to add"));
|
|
136402
|
+
options2.push(optionLine("q", "Quit setup", "Gearbox will not open the coding app yet"));
|
|
136403
|
+
box("Choose how Gearbox should connect", options2);
|
|
136404
|
+
console.log("");
|
|
136405
|
+
const choice = (await ask("Selection: ")).toLowerCase();
|
|
136406
|
+
if (choice === "q" || choice === "quit" || choice === "skip") {
|
|
136407
|
+
console.log("");
|
|
136408
|
+
console.log(warn("Setup skipped. Run `gearbox onboard` when you are ready."));
|
|
136409
|
+
return false;
|
|
136410
|
+
}
|
|
136411
|
+
if (choice === "p" || choice === "providers") {
|
|
136412
|
+
console.log("");
|
|
136413
|
+
console.log(bold("Provider catalog"));
|
|
136414
|
+
console.log(dim("Use these ids with: gearbox auth add <provider> <api-key>"));
|
|
136415
|
+
console.log(providerRows());
|
|
136416
|
+
console.log("");
|
|
136417
|
+
continue;
|
|
136418
|
+
}
|
|
136419
|
+
if (choice === "1" && (env3.length || cloud.length)) {
|
|
136420
|
+
for (const c of env3)
|
|
136421
|
+
await importEnvCred2(c);
|
|
136422
|
+
for (const c of cloud)
|
|
136423
|
+
await importCloudCred2(c);
|
|
136424
|
+
console.log(ok(`Imported ${env3.length + cloud.length} credential${env3.length + cloud.length === 1 ? "" : "s"}.`));
|
|
136425
|
+
break;
|
|
136426
|
+
}
|
|
136427
|
+
if (choice === "2") {
|
|
136428
|
+
console.log(dim("Paste is visible in most terminals. Use option 3 if you want to be explicit about the provider."));
|
|
136429
|
+
const key = await ask("Paste API key: ");
|
|
136430
|
+
if (!key)
|
|
136431
|
+
continue;
|
|
136432
|
+
const detected = detectProviderByKey2(key);
|
|
136433
|
+
if (!detected) {
|
|
136434
|
+
console.log(warn("Could not detect the provider from that key. Use option 3."));
|
|
136435
|
+
continue;
|
|
136436
|
+
}
|
|
136437
|
+
const res = await addByPastedKey2(key);
|
|
136438
|
+
console.log(res.ok ? ok(res.message) : errColor(res.message));
|
|
136439
|
+
if (res.ok && res.account) {
|
|
136440
|
+
await testAndReport(res.account);
|
|
136441
|
+
break;
|
|
136442
|
+
}
|
|
136443
|
+
continue;
|
|
136444
|
+
}
|
|
136445
|
+
if (choice === "3") {
|
|
136446
|
+
console.log("");
|
|
136447
|
+
console.log(bold("Provider catalog"));
|
|
136448
|
+
console.log(providerRows());
|
|
136449
|
+
console.log("");
|
|
136450
|
+
const provider = await ask("Provider id: ");
|
|
136451
|
+
console.log(dim("The key is stored locally and tested before setup finishes."));
|
|
136452
|
+
const key = await ask("API key: ");
|
|
136453
|
+
const res = await addApiKeyAccount2(provider, key);
|
|
136454
|
+
console.log(res.ok ? ok(res.message) : errColor(res.message));
|
|
136455
|
+
if (res.ok && res.account) {
|
|
136456
|
+
await testAndReport(res.account);
|
|
136457
|
+
break;
|
|
136458
|
+
}
|
|
136459
|
+
continue;
|
|
136460
|
+
}
|
|
136461
|
+
if (choice === "4") {
|
|
136462
|
+
console.log(dim("Use a resource name like my-openai-resource, or a full Foundry endpoint URL."));
|
|
136463
|
+
const endpoint = await ask("Azure resource name or endpoint: ");
|
|
136464
|
+
const key = await ask("API key: ");
|
|
136465
|
+
const apiVersion = await ask("API version (optional): ");
|
|
136466
|
+
const res = /^https?:\/\//i.test(endpoint) ? await addAzureFoundryAccount2(endpoint, key) : await addAzureAccount2(endpoint, key, { apiVersion: apiVersion || undefined });
|
|
136467
|
+
console.log(res.ok ? ok(res.message) : errColor(res.message));
|
|
136468
|
+
if (res.ok && res.account) {
|
|
136469
|
+
await testAndReport(res.account);
|
|
136470
|
+
break;
|
|
136471
|
+
}
|
|
136472
|
+
continue;
|
|
136473
|
+
}
|
|
136474
|
+
if (choice === "5" && which2("claude")) {
|
|
136475
|
+
if (await addSubscription("claude-cli"))
|
|
136476
|
+
break;
|
|
136477
|
+
continue;
|
|
136478
|
+
}
|
|
136479
|
+
if (choice === "6" && which2("codex")) {
|
|
136480
|
+
if (await addSubscription("codex-cli"))
|
|
136481
|
+
break;
|
|
136482
|
+
continue;
|
|
136483
|
+
}
|
|
136484
|
+
console.log(warn("Choose one of the listed options."));
|
|
136485
|
+
}
|
|
136486
|
+
console.log("");
|
|
136487
|
+
console.log(ok("Gearbox is ready."));
|
|
136488
|
+
console.log(`Next: ${accent("cd ~/your-project")} and run ${accent("gearbox")}.`);
|
|
136489
|
+
return true;
|
|
136490
|
+
} finally {
|
|
136491
|
+
rl?.close();
|
|
136492
|
+
}
|
|
136493
|
+
}
|
|
136494
|
+
async function readStdin() {
|
|
136495
|
+
let input = "";
|
|
136496
|
+
for await (const chunk2 of process.stdin)
|
|
136497
|
+
input += chunk2;
|
|
136498
|
+
return input;
|
|
136499
|
+
}
|
|
136249
136500
|
if (args[0] === "upgrade" || args[0] === "update") {
|
|
136250
136501
|
const root2 = resolve11(import.meta.dir, "..");
|
|
136251
136502
|
if (!existsSync8(resolve11(root2, ".git"))) {
|
|
@@ -136270,6 +136521,7 @@ if (args.includes("--help") || args.includes("-h")) {
|
|
|
136270
136521
|
|
|
136271
136522
|
Usage:
|
|
136272
136523
|
gearbox start in the current directory (it becomes the workspace)
|
|
136524
|
+
gearbox onboard set up a provider before opening the app
|
|
136273
136525
|
gearbox --model <name> start with a specific model
|
|
136274
136526
|
gearbox --continue resume the most recent session in this directory
|
|
136275
136527
|
gearbox upgrade pull the latest version + reinstall deps
|
|
@@ -136284,6 +136536,7 @@ Options:
|
|
|
136284
136536
|
-h, --help this help
|
|
136285
136537
|
|
|
136286
136538
|
Set up at least one provider first:
|
|
136539
|
+
gearbox onboard
|
|
136287
136540
|
gearbox auth add <api-key>
|
|
136288
136541
|
gearbox auth add <provider> <api-key>
|
|
136289
136542
|
gearbox auth import
|
|
@@ -136296,6 +136549,10 @@ if (args.includes("--version") || args.includes("-v")) {
|
|
|
136296
136549
|
console.log(VERSION16);
|
|
136297
136550
|
process.exit(0);
|
|
136298
136551
|
}
|
|
136552
|
+
if (args[0] === "onboard" || args[0] === "setup") {
|
|
136553
|
+
await runCliOnboarding();
|
|
136554
|
+
process.exit(0);
|
|
136555
|
+
}
|
|
136299
136556
|
if (args[0] === "auth") {
|
|
136300
136557
|
const { listAccounts: listAccounts2, loadAccounts: loadAccounts3, removeAccount: removeAccount2 } = await Promise.resolve().then(() => (init_store(), exports_store));
|
|
136301
136558
|
const { importableEnvCreds: importableEnvCreds2, importEnvCred: importEnvCred2, importableCloudCreds: importableCloudCreds2, importCloudCred: importCloudCred2 } = await Promise.resolve().then(() => (init_detect(), exports_detect));
|
|
@@ -136343,6 +136600,18 @@ Importable from your env (gearbox auth import): ${imp.map((c) => c.envVar).join(
|
|
|
136343
136600
|
}
|
|
136344
136601
|
process.exit(0);
|
|
136345
136602
|
}
|
|
136603
|
+
if (!anyProviderAvailable()) {
|
|
136604
|
+
if (process.stdin.isTTY && process.stdout.isTTY && process.env.GEARBOX_SKIP_ONBOARD !== "1") {
|
|
136605
|
+
const ready = await runCliOnboarding();
|
|
136606
|
+
if (!ready || !anyProviderAvailable())
|
|
136607
|
+
process.exit(0);
|
|
136608
|
+
} else {
|
|
136609
|
+
console.log("Gearbox needs one provider before the coding app can open.");
|
|
136610
|
+
console.log("Run: gearbox onboard");
|
|
136611
|
+
console.log("Or: gearbox auth add <api-key>");
|
|
136612
|
+
process.exit(1);
|
|
136613
|
+
}
|
|
136614
|
+
}
|
|
136346
136615
|
var mi = args.indexOf("--model");
|
|
136347
136616
|
var preferred = mi >= 0 ? args[mi + 1] : undefined;
|
|
136348
136617
|
var pinned = preferred ?? loadPrefs().pinnedModel;
|
package/install.ps1
CHANGED
|
@@ -81,6 +81,12 @@ node "$TargetCli" %*
|
|
|
81
81
|
if (-not $AlreadyOnPath) {
|
|
82
82
|
Write-Host "Open a new terminal if this shell does not pick up the PATH change."
|
|
83
83
|
}
|
|
84
|
+
|
|
85
|
+
if ($env:GEARBOX_SKIP_ONBOARD -ne "1") {
|
|
86
|
+
Write-Host ""
|
|
87
|
+
Write-Host "Starting setup..."
|
|
88
|
+
& $CmdPath onboard
|
|
89
|
+
}
|
|
84
90
|
}
|
|
85
91
|
finally {
|
|
86
92
|
Remove-Item -Recurse -Force $Temp -ErrorAction SilentlyContinue
|
package/install.sh
CHANGED
|
@@ -93,3 +93,13 @@ case ":${PATH}:" in
|
|
|
93
93
|
echo "Then run: gearbox"
|
|
94
94
|
;;
|
|
95
95
|
esac
|
|
96
|
+
|
|
97
|
+
if [[ "${GEARBOX_SKIP_ONBOARD:-}" != "1" ]]; then
|
|
98
|
+
echo ""
|
|
99
|
+
echo "Starting setup..."
|
|
100
|
+
if [[ -r /dev/tty && -w /dev/tty ]]; then
|
|
101
|
+
"${BIN_DIR}/gearbox" onboard < /dev/tty > /dev/tty
|
|
102
|
+
else
|
|
103
|
+
echo "No interactive terminal detected. Run: ${BIN_DIR}/gearbox onboard"
|
|
104
|
+
fi
|
|
105
|
+
fi
|
package/package.json
CHANGED