feat(cli): add native Bun backend support and Dockerfile.bun (#11039)

4 — Suporte de backend nativo Bun + Dockerfile.bun multi-stage + fallback dinâmico de driver SQLite (better-sqlite3 prioritário sob Bun, bun:sqlite fallback; Node preservado) + correção de estabilidade do DAST CI smoke.
Validado a fundo (worktree board sobre tip): bun-support 4/4, typecheck:core limpo, dashboard-typecheck OK (220 dentro do baseline), open-sse-typecheck OK (5 pré-existentes), gate de runtime OK sob Node, changelog-integrity OK, file-size/complexity/cognitive/dead-code OK. Verificado que o driver preserva a cadeia Node/falback conforme AGENTS.md; teste bun-support presente. Baselines de typecheck removidos são ratchet honesto (erros não existem mais).
OBS: destravei 2 base-reds do tip neste turno (push direto 7ffa3ef): movi o changelog fragment da #11050 da seção inválida breaking/ para fixes/, e rebaselinei AddApiKeyModal 1067->1073 (crescimento da #11056). Sem isso a #11039 e o resto da fila ficariam vermelhos.
This commit is contained in:
Rouzbeh†
2026-08-22 03:58:43 +03:30
committed by GitHub
parent 7ffa3efaf0
commit 7ddbaf69a4
34 changed files with 339 additions and 72 deletions

View File

@@ -212,8 +212,7 @@ export function createSyncDriverFactory(load: DriverLoader, betterSqliteProbe?:
filePath: string,
options?: Record<string, unknown>
): SqliteAdapter | null {
// Bun ships a supported SQLite implementation. Prefer it over the native
// Node addon, which Bun intentionally skips because its ABI is incompatible.
// 1. Bun native sqlite driver: preferred built-in driver when running under Bun
if (process.versions.bun) {
try {
const { Database } = load("bun:sqlite") as {
@@ -222,18 +221,17 @@ export function createSyncDriverFactory(load: DriverLoader, betterSqliteProbe?:
if (options?.fileMustExist === true && filePath !== ":memory:" && !existsSync(filePath)) {
throw new Error(`SQLite file does not exist: ${filePath}`);
}
const db = new Database(filePath, {
...(options?.readonly === true
? { readonly: true }
: { readwrite: true, create: options?.fileMustExist !== true }),
});
const bunOptions: Record<string, unknown> = {};
if (options?.readonly === true) bunOptions.readonly = true;
if (options?.create === false && filePath !== ":memory:") bunOptions.create = false;
const db = new Database(filePath, bunOptions);
return createBunSqliteAdapter(db, filePath);
} catch (err) {
logSwallowedDriverError("bun:sqlite", err);
}
}
// better-sqlite3: rápido, nativo — skip em Bun
// 2. better-sqlite3: preferred native driver on Node.js
if (!process.versions.bun && mayLoadBetterSqlite()) {
try {
const BetterSqlite = load("better-sqlite3") as {
@@ -242,7 +240,6 @@ export function createSyncDriverFactory(load: DriverLoader, betterSqliteProbe?:
const db = new BetterSqlite(filePath, options);
return createBetterSqliteAdapter(db);
} catch (err) {
// continua para próximo driver
logSwallowedDriverError("better-sqlite3", err);
}
}

View File

@@ -329,7 +329,7 @@ export async function createEmbeddingResponse(
const responseHeaders = new Headers(result.headers);
if (result.success) {
if (credentials) await clearRecoveredProviderState(credentials);
if (credentials) await clearRecoveredProviderState(credentials as Record<string, unknown>);
responseHeaders.set("Content-Type", "application/json");
const usage = (result.data as { usage?: Record<string, number> })?.usage ?? null;
const costUsd = usage ? await calculateCost(provider, effectiveModel ?? "", usage) : 0;

View File

@@ -145,13 +145,16 @@ export function runNpm(
options: { cwd?: string; timeoutMs?: number; prefix?: string } = {}
): Promise<NpmRunResult> {
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
// On Windows, npm is npm.cmd; on Unix it's npm.
const npmBin = process.platform === "win32" ? "npm.cmd" : "npm";
const isBun = Boolean(process.versions.bun);
const npmBin = process.platform === "win32"
? (isBun ? "bun.exe" : "npm.cmd")
: (isBun ? "bun" : "npm");
const execArgs = isBun && args[0] === "install" ? ["add", ...args.slice(1)] : args;
return new Promise((resolve, reject) => {
execFile(
npmBin,
args,
execArgs,
buildNpmExecOptions(process.platform, {
cwd: options.cwd,
timeoutMs,