vibora 8.2.0 → 8.3.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/bin/vibora.js +1 -1
- package/dist/assets/index-3rMJiF3e.js +182 -0
- package/dist/assets/index-CfTPmrF1.css +1 -0
- package/dist/index.html +2 -2
- package/drizzle/0020_opencode_model.sql +2 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +1 -1
- package/server/index.js +189 -35
- package/dist/assets/index-BULnnD3l.js +0 -182
- package/dist/assets/index-CvWMPNXW.css +0 -1
package/server/index.js
CHANGED
|
@@ -325,7 +325,8 @@ function getSettings() {
|
|
|
325
325
|
cloudflareAccountId: parsed.integrations?.cloudflareAccountId ?? null
|
|
326
326
|
},
|
|
327
327
|
agent: {
|
|
328
|
-
defaultAgent: parsed.agent?.defaultAgent ?? DEFAULT_SETTINGS.agent.defaultAgent
|
|
328
|
+
defaultAgent: parsed.agent?.defaultAgent ?? DEFAULT_SETTINGS.agent.defaultAgent,
|
|
329
|
+
opencodeModel: parsed.agent?.opencodeModel ?? null
|
|
329
330
|
},
|
|
330
331
|
appearance: {
|
|
331
332
|
language: parsed.appearance?.language ?? null,
|
|
@@ -629,7 +630,8 @@ var init_settings = __esm(() => {
|
|
|
629
630
|
cloudflareAccountId: null
|
|
630
631
|
},
|
|
631
632
|
agent: {
|
|
632
|
-
defaultAgent: "claude"
|
|
633
|
+
defaultAgent: "claude",
|
|
634
|
+
opencodeModel: null
|
|
633
635
|
},
|
|
634
636
|
appearance: {
|
|
635
637
|
language: null,
|
|
@@ -38761,6 +38763,7 @@ var tasks = sqliteTable("tasks", {
|
|
|
38761
38763
|
agent: text("agent").notNull().default("claude"),
|
|
38762
38764
|
aiMode: text("ai_mode"),
|
|
38763
38765
|
agentOptions: text("agent_options"),
|
|
38766
|
+
opencodeModel: text("opencode_model"),
|
|
38764
38767
|
createdAt: text("created_at").notNull(),
|
|
38765
38768
|
updatedAt: text("updated_at").notNull()
|
|
38766
38769
|
});
|
|
@@ -38804,6 +38807,7 @@ var repositories = sqliteTable("repositories", {
|
|
|
38804
38807
|
copyFiles: text("copy_files"),
|
|
38805
38808
|
claudeOptions: text("claude_options"),
|
|
38806
38809
|
opencodeOptions: text("opencode_options"),
|
|
38810
|
+
opencodeModel: text("opencode_model"),
|
|
38807
38811
|
defaultAgent: text("default_agent"),
|
|
38808
38812
|
remoteUrl: text("remote_url"),
|
|
38809
38813
|
isCopierTemplate: integer("is_copier_template", { mode: "boolean" }).default(false),
|
|
@@ -38936,6 +38940,7 @@ function runMigrations(sqlite, drizzleDb) {
|
|
|
38936
38940
|
const hasTunnelsTable = sqlite.query("SELECT name FROM sqlite_master WHERE type='table' AND name='tunnels'").get();
|
|
38937
38941
|
const hasAgentColumn = sqlite.query("SELECT name FROM pragma_table_info('tasks') WHERE name='agent'").get();
|
|
38938
38942
|
const hasDefaultAgentColumn = sqlite.query("SELECT name FROM pragma_table_info('repositories') WHERE name='default_agent'").get();
|
|
38943
|
+
const hasOpencodeModelColumn = sqlite.query("SELECT name FROM pragma_table_info('repositories') WHERE name='opencode_model'").get();
|
|
38939
38944
|
const migrationsToMark = [];
|
|
38940
38945
|
for (const entry of journal.entries) {
|
|
38941
38946
|
if (existingHashes.has(entry.tag))
|
|
@@ -38957,6 +38962,8 @@ function runMigrations(sqlite, drizzleDb) {
|
|
|
38957
38962
|
shouldMark = true;
|
|
38958
38963
|
} else if (entry.tag.startsWith("0019") && hasDefaultAgentColumn) {
|
|
38959
38964
|
shouldMark = true;
|
|
38965
|
+
} else if (entry.tag.startsWith("0020") && hasOpencodeModelColumn) {
|
|
38966
|
+
shouldMark = true;
|
|
38960
38967
|
}
|
|
38961
38968
|
if (shouldMark) {
|
|
38962
38969
|
migrationsToMark.push(entry);
|
|
@@ -169482,6 +169489,7 @@ app2.post("/", async (c) => {
|
|
|
169482
169489
|
agent: body.agent || "claude",
|
|
169483
169490
|
aiMode: body.aiMode || null,
|
|
169484
169491
|
agentOptions: body.agentOptions ? JSON.stringify(body.agentOptions) : null,
|
|
169492
|
+
opencodeModel: body.opencodeModel || null,
|
|
169485
169493
|
createdAt: now,
|
|
169486
169494
|
updatedAt: now
|
|
169487
169495
|
};
|
|
@@ -170817,6 +170825,7 @@ var CONFIG_KEYS = {
|
|
|
170817
170825
|
LINEAR_API_KEY: "integrations.linearApiKey",
|
|
170818
170826
|
GITHUB_PAT: "integrations.githubPat",
|
|
170819
170827
|
DEFAULT_AGENT: "agent.defaultAgent",
|
|
170828
|
+
OPENCODE_MODEL: "agent.opencodeModel",
|
|
170820
170829
|
LANGUAGE: "appearance.language",
|
|
170821
170830
|
THEME: "appearance.theme",
|
|
170822
170831
|
SYNC_CLAUDE_CODE_THEME: "appearance.syncClaudeCodeTheme",
|
|
@@ -171046,6 +171055,13 @@ app5.put("/:key", async (c) => {
|
|
|
171046
171055
|
if (!validAgents.includes(value)) {
|
|
171047
171056
|
return c.json({ error: `Default agent must be one of: ${validAgents.join(", ")}` }, 400);
|
|
171048
171057
|
}
|
|
171058
|
+
} else if (path8 === CONFIG_KEYS.OPENCODE_MODEL) {
|
|
171059
|
+
if (value !== null && typeof value !== "string") {
|
|
171060
|
+
return c.json({ error: "OpenCode model must be a string or null" }, 400);
|
|
171061
|
+
}
|
|
171062
|
+
if (value === "") {
|
|
171063
|
+
value = null;
|
|
171064
|
+
}
|
|
171049
171065
|
} else if (typeof value === "string" && value === "") {
|
|
171050
171066
|
if (path8 === CONFIG_KEYS.LINEAR_API_KEY || path8 === CONFIG_KEYS.GITHUB_PAT || path8 === CONFIG_KEYS.REMOTE_HOST || path8 === CONFIG_KEYS.EDITOR_HOST) {
|
|
171051
171067
|
value = null;
|
|
@@ -181108,6 +181124,143 @@ app18.post("/:name/run", (c) => {
|
|
|
181108
181124
|
});
|
|
181109
181125
|
var jobs_default = app18;
|
|
181110
181126
|
|
|
181127
|
+
// server/routes/opencode.ts
|
|
181128
|
+
init_logger2();
|
|
181129
|
+
import { spawn as spawn7 } from "child_process";
|
|
181130
|
+
var app19 = new Hono2;
|
|
181131
|
+
var PROVIDER_NAME_MAP = {
|
|
181132
|
+
"z.ai coding plan": "zai-coding-plan",
|
|
181133
|
+
anthropic: "anthropic",
|
|
181134
|
+
openai: "openai",
|
|
181135
|
+
google: "google",
|
|
181136
|
+
opencode: "opencode",
|
|
181137
|
+
groq: "groq",
|
|
181138
|
+
xai: "xai",
|
|
181139
|
+
deepseek: "deepseek",
|
|
181140
|
+
mistral: "mistral",
|
|
181141
|
+
azure: "azure",
|
|
181142
|
+
bedrock: "bedrock",
|
|
181143
|
+
vertex: "vertex",
|
|
181144
|
+
ollama: "ollama",
|
|
181145
|
+
openrouter: "openrouter",
|
|
181146
|
+
copilot: "copilot"
|
|
181147
|
+
};
|
|
181148
|
+
async function fetchConfiguredProviders() {
|
|
181149
|
+
return new Promise((resolve6) => {
|
|
181150
|
+
const proc2 = spawn7("opencode", ["auth", "list"], {
|
|
181151
|
+
timeout: 1e4,
|
|
181152
|
+
env: { ...process.env, NO_COLOR: "1" }
|
|
181153
|
+
});
|
|
181154
|
+
let stdout = "";
|
|
181155
|
+
proc2.stdout.on("data", (data) => {
|
|
181156
|
+
stdout += data.toString();
|
|
181157
|
+
});
|
|
181158
|
+
proc2.on("error", () => {
|
|
181159
|
+
resolve6([]);
|
|
181160
|
+
});
|
|
181161
|
+
proc2.on("close", (code) => {
|
|
181162
|
+
if (code !== 0) {
|
|
181163
|
+
resolve6([]);
|
|
181164
|
+
return;
|
|
181165
|
+
}
|
|
181166
|
+
const cleanOutput = stdout.replace(/\x1b\[[0-9;]*m/g, "");
|
|
181167
|
+
const providers = [];
|
|
181168
|
+
const lines = cleanOutput.split(`
|
|
181169
|
+
`);
|
|
181170
|
+
for (const line of lines) {
|
|
181171
|
+
const match3 = line.match(/[\u25CF\u25CB]\s+(.+?)\s+(api|oauth|token|key)\s*$/i);
|
|
181172
|
+
if (match3) {
|
|
181173
|
+
const displayName = match3[1].trim().toLowerCase();
|
|
181174
|
+
const providerId = PROVIDER_NAME_MAP[displayName] || displayName.replace(/\s+/g, "-");
|
|
181175
|
+
providers.push(providerId);
|
|
181176
|
+
log2.server.debug("Parsed configured provider", { displayName, providerId });
|
|
181177
|
+
}
|
|
181178
|
+
}
|
|
181179
|
+
log2.server.debug("Configured providers", { providers });
|
|
181180
|
+
resolve6(providers);
|
|
181181
|
+
});
|
|
181182
|
+
});
|
|
181183
|
+
}
|
|
181184
|
+
async function fetchOpencodeModels() {
|
|
181185
|
+
const configuredProviders = await fetchConfiguredProviders();
|
|
181186
|
+
return new Promise((resolve6) => {
|
|
181187
|
+
const proc2 = spawn7("opencode", ["models"], {
|
|
181188
|
+
timeout: 1e4,
|
|
181189
|
+
env: { ...process.env, NO_COLOR: "1" }
|
|
181190
|
+
});
|
|
181191
|
+
let stdout = "";
|
|
181192
|
+
let stderr = "";
|
|
181193
|
+
proc2.stdout.on("data", (data) => {
|
|
181194
|
+
stdout += data.toString();
|
|
181195
|
+
});
|
|
181196
|
+
proc2.stderr.on("data", (data) => {
|
|
181197
|
+
stderr += data.toString();
|
|
181198
|
+
});
|
|
181199
|
+
proc2.on("error", (err) => {
|
|
181200
|
+
log2.server.warn("opencode command not found", { error: err.message });
|
|
181201
|
+
resolve6({
|
|
181202
|
+
installed: false,
|
|
181203
|
+
providers: {},
|
|
181204
|
+
models: [],
|
|
181205
|
+
configuredProviders: []
|
|
181206
|
+
});
|
|
181207
|
+
});
|
|
181208
|
+
proc2.on("close", (code) => {
|
|
181209
|
+
if (code !== 0) {
|
|
181210
|
+
log2.server.warn("opencode models command failed", { code, stderr });
|
|
181211
|
+
resolve6({
|
|
181212
|
+
installed: false,
|
|
181213
|
+
providers: {},
|
|
181214
|
+
models: [],
|
|
181215
|
+
configuredProviders: []
|
|
181216
|
+
});
|
|
181217
|
+
return;
|
|
181218
|
+
}
|
|
181219
|
+
const lines = stdout.trim().split(`
|
|
181220
|
+
`).filter(Boolean);
|
|
181221
|
+
const allProviders = {};
|
|
181222
|
+
const allModels = [];
|
|
181223
|
+
for (const line of lines) {
|
|
181224
|
+
const trimmed = line.trim();
|
|
181225
|
+
if (!trimmed)
|
|
181226
|
+
continue;
|
|
181227
|
+
allModels.push(trimmed);
|
|
181228
|
+
const slashIndex = trimmed.indexOf("/");
|
|
181229
|
+
if (slashIndex > 0) {
|
|
181230
|
+
const provider = trimmed.substring(0, slashIndex);
|
|
181231
|
+
const model = trimmed.substring(slashIndex + 1);
|
|
181232
|
+
if (!allProviders[provider]) {
|
|
181233
|
+
allProviders[provider] = [];
|
|
181234
|
+
}
|
|
181235
|
+
allProviders[provider].push(model);
|
|
181236
|
+
}
|
|
181237
|
+
}
|
|
181238
|
+
const hasConfigured = configuredProviders.length > 0;
|
|
181239
|
+
const providers = {};
|
|
181240
|
+
const models = [];
|
|
181241
|
+
for (const [provider, providerModels] of Object.entries(allProviders)) {
|
|
181242
|
+
if (!hasConfigured || configuredProviders.includes(provider)) {
|
|
181243
|
+
providers[provider] = providerModels;
|
|
181244
|
+
for (const model of providerModels) {
|
|
181245
|
+
models.push(`${provider}/${model}`);
|
|
181246
|
+
}
|
|
181247
|
+
}
|
|
181248
|
+
}
|
|
181249
|
+
resolve6({
|
|
181250
|
+
installed: true,
|
|
181251
|
+
providers,
|
|
181252
|
+
models,
|
|
181253
|
+
configuredProviders
|
|
181254
|
+
});
|
|
181255
|
+
});
|
|
181256
|
+
});
|
|
181257
|
+
}
|
|
181258
|
+
app19.get("/models", async (c) => {
|
|
181259
|
+
const result = await fetchOpencodeModels();
|
|
181260
|
+
return c.json(result);
|
|
181261
|
+
});
|
|
181262
|
+
var opencode_default = app19;
|
|
181263
|
+
|
|
181111
181264
|
// server/app.ts
|
|
181112
181265
|
init_logger2();
|
|
181113
181266
|
function getDistPath() {
|
|
@@ -181117,40 +181270,41 @@ function getDistPath() {
|
|
|
181117
181270
|
return join25(process.cwd(), "dist");
|
|
181118
181271
|
}
|
|
181119
181272
|
function createApp() {
|
|
181120
|
-
const
|
|
181121
|
-
|
|
181122
|
-
|
|
181273
|
+
const app20 = new Hono2;
|
|
181274
|
+
app20.use("*", logger());
|
|
181275
|
+
app20.use("*", cors({
|
|
181123
181276
|
origin: "*",
|
|
181124
181277
|
allowMethods: ["GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS"],
|
|
181125
181278
|
allowHeaders: ["Content-Type"]
|
|
181126
181279
|
}));
|
|
181127
|
-
|
|
181128
|
-
|
|
181129
|
-
|
|
181130
|
-
|
|
181131
|
-
|
|
181132
|
-
|
|
181133
|
-
|
|
181134
|
-
|
|
181135
|
-
|
|
181136
|
-
|
|
181137
|
-
|
|
181138
|
-
|
|
181139
|
-
|
|
181140
|
-
|
|
181141
|
-
|
|
181142
|
-
|
|
181143
|
-
|
|
181144
|
-
|
|
181145
|
-
|
|
181146
|
-
|
|
181280
|
+
app20.route("/health", health_default);
|
|
181281
|
+
app20.route("/api/tasks", tasks_default);
|
|
181282
|
+
app20.route("/api/git", git_default);
|
|
181283
|
+
app20.route("/api/fs", filesystem_default);
|
|
181284
|
+
app20.route("/api/config", config_default);
|
|
181285
|
+
app20.route("/api/uploads", uploads_default);
|
|
181286
|
+
app20.route("/api/worktrees", worktrees_default);
|
|
181287
|
+
app20.route("/api/terminal-view-state", terminal_view_state_default);
|
|
181288
|
+
app20.route("/api/repositories", repositories_default);
|
|
181289
|
+
app20.route("/api/copier", copier_default);
|
|
181290
|
+
app20.route("/api/linear", linear_default);
|
|
181291
|
+
app20.route("/api/github", github_default);
|
|
181292
|
+
app20.route("/api/monitoring", monitoringRoutes);
|
|
181293
|
+
app20.route("/api/system", system_default);
|
|
181294
|
+
app20.route("/api/exec", exec_default);
|
|
181295
|
+
app20.route("/api/apps", apps_default);
|
|
181296
|
+
app20.route("/api/compose", compose_default);
|
|
181297
|
+
app20.route("/api/deployment", deployment_default);
|
|
181298
|
+
app20.route("/api/jobs", jobs_default);
|
|
181299
|
+
app20.route("/api/opencode", opencode_default);
|
|
181300
|
+
app20.post("/api/logs", async (c) => {
|
|
181147
181301
|
const { entries } = await c.req.json();
|
|
181148
181302
|
for (const entry of entries) {
|
|
181149
181303
|
writeEntry(entry);
|
|
181150
181304
|
}
|
|
181151
181305
|
return c.json({ ok: true });
|
|
181152
181306
|
});
|
|
181153
|
-
|
|
181307
|
+
app20.post("/api/debug", async (c) => {
|
|
181154
181308
|
const body = await c.req.json();
|
|
181155
181309
|
const entry = {
|
|
181156
181310
|
ts: new Date().toISOString(),
|
|
@@ -181187,14 +181341,14 @@ function createApp() {
|
|
|
181187
181341
|
headers: { "Content-Type": mimeTypes2[ext2 || ""] || "application/octet-stream" }
|
|
181188
181342
|
});
|
|
181189
181343
|
};
|
|
181190
|
-
|
|
181344
|
+
app20.get("/assets/*", async (c) => {
|
|
181191
181345
|
const assetPath = join25(distPath, c.req.path);
|
|
181192
181346
|
if (existsSync18(assetPath)) {
|
|
181193
181347
|
return serveFile(assetPath);
|
|
181194
181348
|
}
|
|
181195
181349
|
return c.notFound();
|
|
181196
181350
|
});
|
|
181197
|
-
|
|
181351
|
+
app20.get("/sounds/*", async (c) => {
|
|
181198
181352
|
const soundPath = join25(distPath, c.req.path);
|
|
181199
181353
|
if (existsSync18(soundPath)) {
|
|
181200
181354
|
return serveFile(soundPath);
|
|
@@ -181203,7 +181357,7 @@ function createApp() {
|
|
|
181203
181357
|
});
|
|
181204
181358
|
const staticFiles = ["vibora-icon.png", "vibora-logo.jpeg", "vite.svg", "logo.png", "goat.jpeg"];
|
|
181205
181359
|
for (const file of staticFiles) {
|
|
181206
|
-
|
|
181360
|
+
app20.get(`/${file}`, async () => {
|
|
181207
181361
|
const filePath = join25(distPath, file);
|
|
181208
181362
|
if (existsSync18(filePath)) {
|
|
181209
181363
|
return serveFile(filePath);
|
|
@@ -181211,7 +181365,7 @@ function createApp() {
|
|
|
181211
181365
|
return new Response("Not Found", { status: 404 });
|
|
181212
181366
|
});
|
|
181213
181367
|
}
|
|
181214
|
-
|
|
181368
|
+
app20.get("*", async (c, next) => {
|
|
181215
181369
|
const path10 = c.req.path;
|
|
181216
181370
|
if (path10.startsWith("/api/") || path10.startsWith("/ws/") || path10 === "/health") {
|
|
181217
181371
|
return next();
|
|
@@ -181220,7 +181374,7 @@ function createApp() {
|
|
|
181220
181374
|
return c.html(html);
|
|
181221
181375
|
});
|
|
181222
181376
|
}
|
|
181223
|
-
return
|
|
181377
|
+
return app20;
|
|
181224
181378
|
}
|
|
181225
181379
|
|
|
181226
181380
|
// server/index.ts
|
|
@@ -181330,11 +181484,11 @@ setBroadcastDestroyed((terminalId) => {
|
|
|
181330
181484
|
payload: { terminalId }
|
|
181331
181485
|
});
|
|
181332
181486
|
});
|
|
181333
|
-
var
|
|
181334
|
-
var { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app:
|
|
181335
|
-
|
|
181487
|
+
var app20 = createApp();
|
|
181488
|
+
var { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app: app20 });
|
|
181489
|
+
app20.get("/ws/terminal", upgradeWebSocket(() => terminalWebSocketHandlers));
|
|
181336
181490
|
var server = serve({
|
|
181337
|
-
fetch:
|
|
181491
|
+
fetch: app20.fetch,
|
|
181338
181492
|
port: PORT,
|
|
181339
181493
|
hostname: HOST
|
|
181340
181494
|
}, (info) => {
|