vibora 4.6.0 → 4.7.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 +35 -14
- package/dist/assets/{index-D9hu2Hyr.js → index-BHItdbV8.js} +108 -105
- package/dist/assets/index-uq7fsilP.css +1 -0
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/server/index.js +165 -22
- package/dist/assets/index-CVgbBi7w.css +0 -1
package/server/index.js
CHANGED
|
@@ -14380,6 +14380,11 @@ function migrateSettings(parsed) {
|
|
|
14380
14380
|
return result;
|
|
14381
14381
|
}
|
|
14382
14382
|
function expandPath(p) {
|
|
14383
|
+
if (!p)
|
|
14384
|
+
return p;
|
|
14385
|
+
if (p === "~") {
|
|
14386
|
+
return os.homedir();
|
|
14387
|
+
}
|
|
14383
14388
|
if (p.startsWith("~/")) {
|
|
14384
14389
|
return path.join(os.homedir(), p.slice(2));
|
|
14385
14390
|
}
|
|
@@ -146030,6 +146035,92 @@ app3.post("/sync-parent", async (c) => {
|
|
|
146030
146035
|
return c.json({ error: err instanceof Error ? err.message : "Failed to sync parent" }, 500);
|
|
146031
146036
|
}
|
|
146032
146037
|
});
|
|
146038
|
+
app3.post("/create-pr", async (c) => {
|
|
146039
|
+
try {
|
|
146040
|
+
const body = await c.req.json();
|
|
146041
|
+
const { worktreePath, title, baseBranch } = body;
|
|
146042
|
+
if (!worktreePath || !title) {
|
|
146043
|
+
return c.json({ error: "Missing required fields: worktreePath, title" }, 400);
|
|
146044
|
+
}
|
|
146045
|
+
if (!fs4.existsSync(worktreePath)) {
|
|
146046
|
+
return c.json({ error: "Worktree path does not exist" }, 404);
|
|
146047
|
+
}
|
|
146048
|
+
try {
|
|
146049
|
+
const status = gitExec(worktreePath, "status --porcelain");
|
|
146050
|
+
if (status.trim()) {
|
|
146051
|
+
return c.json({
|
|
146052
|
+
error: "Worktree has uncommitted changes. Please commit changes before creating a PR.",
|
|
146053
|
+
hasUncommittedChanges: true
|
|
146054
|
+
}, 409);
|
|
146055
|
+
}
|
|
146056
|
+
} catch {}
|
|
146057
|
+
let branch;
|
|
146058
|
+
try {
|
|
146059
|
+
branch = gitExec(worktreePath, "rev-parse --abbrev-ref HEAD");
|
|
146060
|
+
} catch {
|
|
146061
|
+
return c.json({ error: "Failed to determine current branch" }, 500);
|
|
146062
|
+
}
|
|
146063
|
+
const args = ["gh", "pr", "create", "--title", JSON.stringify(title), "--fill"];
|
|
146064
|
+
if (baseBranch) {
|
|
146065
|
+
args.push("--base", baseBranch);
|
|
146066
|
+
}
|
|
146067
|
+
try {
|
|
146068
|
+
const output = execSync3(args.join(" "), {
|
|
146069
|
+
cwd: worktreePath,
|
|
146070
|
+
encoding: "utf-8",
|
|
146071
|
+
timeout: 30000,
|
|
146072
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
146073
|
+
});
|
|
146074
|
+
const prUrl = output.trim();
|
|
146075
|
+
return c.json({
|
|
146076
|
+
success: true,
|
|
146077
|
+
prUrl,
|
|
146078
|
+
branch
|
|
146079
|
+
});
|
|
146080
|
+
} catch (err) {
|
|
146081
|
+
const errorMsg = err instanceof Error ? err.message : "Unknown error";
|
|
146082
|
+
const stderr = err && typeof err === "object" && "stderr" in err ? String(err.stderr).trim() : "";
|
|
146083
|
+
if (stderr.includes("already exists") || errorMsg.includes("already exists")) {
|
|
146084
|
+
try {
|
|
146085
|
+
const prViewOutput = execSync3("gh pr view --json url -q .url", {
|
|
146086
|
+
cwd: worktreePath,
|
|
146087
|
+
encoding: "utf-8",
|
|
146088
|
+
timeout: 1e4,
|
|
146089
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
146090
|
+
});
|
|
146091
|
+
const existingPrUrl = prViewOutput.trim();
|
|
146092
|
+
return c.json({
|
|
146093
|
+
error: "A pull request already exists for this branch",
|
|
146094
|
+
prAlreadyExists: true,
|
|
146095
|
+
existingPrUrl
|
|
146096
|
+
}, 409);
|
|
146097
|
+
} catch {
|
|
146098
|
+
return c.json({
|
|
146099
|
+
error: "A pull request already exists for this branch",
|
|
146100
|
+
prAlreadyExists: true
|
|
146101
|
+
}, 409);
|
|
146102
|
+
}
|
|
146103
|
+
}
|
|
146104
|
+
if (stderr.includes("not pushed") || errorMsg.includes("not pushed") || stderr.includes("no upstream") || errorMsg.includes("no upstream")) {
|
|
146105
|
+
return c.json({
|
|
146106
|
+
error: "Branch has not been pushed to remote. Please push first.",
|
|
146107
|
+
branchNotPushed: true
|
|
146108
|
+
}, 409);
|
|
146109
|
+
}
|
|
146110
|
+
if (stderr.includes("gh auth login") || errorMsg.includes("gh auth login")) {
|
|
146111
|
+
return c.json({
|
|
146112
|
+
error: "GitHub CLI not authenticated. Run `gh auth login` first.",
|
|
146113
|
+
notAuthenticated: true
|
|
146114
|
+
}, 401);
|
|
146115
|
+
}
|
|
146116
|
+
return c.json({
|
|
146117
|
+
error: stderr || errorMsg || "Failed to create PR"
|
|
146118
|
+
}, 500);
|
|
146119
|
+
}
|
|
146120
|
+
} catch (err) {
|
|
146121
|
+
return c.json({ error: err instanceof Error ? err.message : "Failed to create PR" }, 500);
|
|
146122
|
+
}
|
|
146123
|
+
});
|
|
146033
146124
|
app3.get("/remote", (c) => {
|
|
146034
146125
|
let repoPath = c.req.query("path");
|
|
146035
146126
|
if (!repoPath) {
|
|
@@ -147297,9 +147388,10 @@ app8.patch("/", async (c) => {
|
|
|
147297
147388
|
var terminal_view_state_default = app8;
|
|
147298
147389
|
|
|
147299
147390
|
// server/routes/repositories.ts
|
|
147300
|
-
import { existsSync as existsSync10, rmSync as rmSync4, readdirSync as readdirSync7 } from "fs";
|
|
147301
|
-
import { join as join12 } from "path";
|
|
147391
|
+
import { existsSync as existsSync10, rmSync as rmSync4, readdirSync as readdirSync7, mkdirSync as mkdirSync6 } from "fs";
|
|
147392
|
+
import { join as join12, resolve as resolve4 } from "path";
|
|
147302
147393
|
import { execSync as execSync4 } from "child_process";
|
|
147394
|
+
import { homedir as homedir5 } from "os";
|
|
147303
147395
|
|
|
147304
147396
|
// server/lib/git-utils.ts
|
|
147305
147397
|
function isGitUrl(source) {
|
|
@@ -147343,15 +147435,16 @@ app9.post("/", async (c) => {
|
|
|
147343
147435
|
if (!body.path) {
|
|
147344
147436
|
return c.json({ error: "path is required" }, 400);
|
|
147345
147437
|
}
|
|
147346
|
-
const
|
|
147438
|
+
const repoPath = expandPath(body.path);
|
|
147439
|
+
const existing = db.select().from(repositories).where(eq(repositories.path, repoPath)).get();
|
|
147347
147440
|
if (existing) {
|
|
147348
147441
|
return c.json({ error: "Repository with this path already exists" }, 400);
|
|
147349
147442
|
}
|
|
147350
147443
|
const now = new Date().toISOString();
|
|
147351
|
-
const displayName = body.displayName ||
|
|
147444
|
+
const displayName = body.displayName || repoPath.split("/").pop() || "repo";
|
|
147352
147445
|
const newRepo = {
|
|
147353
147446
|
id: crypto.randomUUID(),
|
|
147354
|
-
path:
|
|
147447
|
+
path: repoPath,
|
|
147355
147448
|
displayName,
|
|
147356
147449
|
startupScript: body.startupScript || null,
|
|
147357
147450
|
copyFiles: body.copyFiles || null,
|
|
@@ -147376,12 +147469,37 @@ app9.post("/clone", async (c) => {
|
|
|
147376
147469
|
return c.json({ error: "Invalid git URL format" }, 400);
|
|
147377
147470
|
}
|
|
147378
147471
|
const settings = getSettings();
|
|
147379
|
-
|
|
147380
|
-
|
|
147381
|
-
const
|
|
147472
|
+
let parentDir = body.targetDir?.trim() || settings.paths.defaultGitReposDir;
|
|
147473
|
+
parentDir = expandPath(parentDir);
|
|
147474
|
+
const home = homedir5();
|
|
147475
|
+
if (resolve4(parentDir) === home) {
|
|
147476
|
+
return c.json({ error: "Cannot clone directly into home directory. Please specify a subdirectory." }, 400);
|
|
147477
|
+
}
|
|
147478
|
+
const repoName = body.folderName?.trim() || extractRepoNameFromUrl(body.url);
|
|
147479
|
+
if (!repoName) {
|
|
147480
|
+
return c.json({ error: "Could not determine folder name from URL" }, 400);
|
|
147481
|
+
}
|
|
147482
|
+
if (repoName === "." || repoName === "..") {
|
|
147483
|
+
return c.json({ error: "Invalid folder name" }, 400);
|
|
147484
|
+
}
|
|
147485
|
+
if (repoName.includes("/") || repoName.includes("\\")) {
|
|
147486
|
+
return c.json({ error: "Folder name cannot contain path separators" }, 400);
|
|
147487
|
+
}
|
|
147488
|
+
const targetPath = join12(parentDir, repoName);
|
|
147489
|
+
const resolvedParent = resolve4(parentDir);
|
|
147490
|
+
const resolvedTarget = resolve4(targetPath);
|
|
147491
|
+
if (!resolvedTarget.startsWith(resolvedParent + "/") && resolvedTarget !== resolvedParent) {
|
|
147492
|
+
return c.json({ error: "Invalid target path" }, 400);
|
|
147493
|
+
}
|
|
147382
147494
|
if (existsSync10(targetPath)) {
|
|
147383
147495
|
return c.json({ error: `Directory already exists: ${targetPath}` }, 400);
|
|
147384
147496
|
}
|
|
147497
|
+
if (!existsSync10(parentDir)) {
|
|
147498
|
+
if (resolve4(parentDir) === home) {
|
|
147499
|
+
return c.json({ error: "Cannot create home directory" }, 400);
|
|
147500
|
+
}
|
|
147501
|
+
mkdirSync6(parentDir, { recursive: true });
|
|
147502
|
+
}
|
|
147385
147503
|
try {
|
|
147386
147504
|
execSync4(`git clone "${body.url}" "${targetPath}"`, {
|
|
147387
147505
|
encoding: "utf-8",
|
|
@@ -147389,7 +147507,7 @@ app9.post("/clone", async (c) => {
|
|
|
147389
147507
|
timeout: 120000
|
|
147390
147508
|
});
|
|
147391
147509
|
} catch (cloneErr) {
|
|
147392
|
-
if (existsSync10(targetPath)) {
|
|
147510
|
+
if (existsSync10(targetPath) && resolvedTarget.startsWith(resolvedParent + "/")) {
|
|
147393
147511
|
rmSync4(targetPath, { recursive: true, force: true });
|
|
147394
147512
|
}
|
|
147395
147513
|
const errorMessage = cloneErr instanceof Error ? cloneErr.message : "Clone failed";
|
|
@@ -147449,18 +147567,43 @@ app9.patch("/:id", async (c) => {
|
|
|
147449
147567
|
});
|
|
147450
147568
|
app9.delete("/:id", (c) => {
|
|
147451
147569
|
const id = c.req.param("id");
|
|
147570
|
+
const deleteDirectory = c.req.query("deleteDirectory") === "true";
|
|
147452
147571
|
const existing = db.select().from(repositories).where(eq(repositories.id, id)).get();
|
|
147453
147572
|
if (!existing) {
|
|
147454
147573
|
return c.json({ error: "Repository not found" }, 404);
|
|
147455
147574
|
}
|
|
147575
|
+
if (deleteDirectory && existing.path) {
|
|
147576
|
+
const repoPath = existing.path;
|
|
147577
|
+
const home = homedir5();
|
|
147578
|
+
if (resolve4(repoPath) === home) {
|
|
147579
|
+
return c.json({ error: "Cannot delete home directory" }, 400);
|
|
147580
|
+
}
|
|
147581
|
+
const dangerousPaths = ["/", "/home", "/usr", "/etc", "/var", "/tmp", "/root"];
|
|
147582
|
+
if (dangerousPaths.includes(resolve4(repoPath))) {
|
|
147583
|
+
return c.json({ error: "Cannot delete system directory" }, 400);
|
|
147584
|
+
}
|
|
147585
|
+
if (existsSync10(repoPath)) {
|
|
147586
|
+
const gitPath = join12(repoPath, ".git");
|
|
147587
|
+
if (!existsSync10(gitPath)) {
|
|
147588
|
+
return c.json({ error: "Directory does not appear to be a git repository" }, 400);
|
|
147589
|
+
}
|
|
147590
|
+
try {
|
|
147591
|
+
rmSync4(repoPath, { recursive: true, force: true });
|
|
147592
|
+
} catch (err) {
|
|
147593
|
+
return c.json({
|
|
147594
|
+
error: `Failed to delete directory: ${err instanceof Error ? err.message : "Unknown error"}`
|
|
147595
|
+
}, 500);
|
|
147596
|
+
}
|
|
147597
|
+
}
|
|
147598
|
+
}
|
|
147456
147599
|
db.delete(repositories).where(eq(repositories.id, id)).run();
|
|
147457
|
-
return c.json({ success: true });
|
|
147600
|
+
return c.json({ success: true, directoryDeleted: deleteDirectory });
|
|
147458
147601
|
});
|
|
147459
147602
|
app9.post("/scan", async (c) => {
|
|
147460
147603
|
try {
|
|
147461
147604
|
const body = await c.req.json().catch(() => ({}));
|
|
147462
147605
|
const settings = getSettings();
|
|
147463
|
-
const directory = body.directory || settings.paths.defaultGitReposDir;
|
|
147606
|
+
const directory = expandPath(body.directory || settings.paths.defaultGitReposDir);
|
|
147464
147607
|
if (!existsSync10(directory)) {
|
|
147465
147608
|
return c.json({ error: `Directory does not exist: ${directory}` }, 400);
|
|
147466
147609
|
}
|
|
@@ -151521,7 +151664,7 @@ var github_default = app12;
|
|
|
151521
151664
|
// server/routes/monitoring.ts
|
|
151522
151665
|
import { readdirSync as readdirSync8, readFileSync as readFileSync7, readlinkSync as readlinkSync2, existsSync as existsSync12 } from "fs";
|
|
151523
151666
|
import { execSync as execSync7 } from "child_process";
|
|
151524
|
-
import { homedir as
|
|
151667
|
+
import { homedir as homedir6 } from "os";
|
|
151525
151668
|
import { join as join14 } from "path";
|
|
151526
151669
|
|
|
151527
151670
|
// server/services/metrics-collector.ts
|
|
@@ -152309,7 +152452,7 @@ monitoringRoutes.post("/vibora-instances/:pid/kill", async (c) => {
|
|
|
152309
152452
|
killedPids.push(pid);
|
|
152310
152453
|
} catch {}
|
|
152311
152454
|
}
|
|
152312
|
-
await new Promise((
|
|
152455
|
+
await new Promise((resolve5) => setTimeout(resolve5, 500));
|
|
152313
152456
|
for (const pid of pidsToKill) {
|
|
152314
152457
|
try {
|
|
152315
152458
|
process.kill(pid, 0);
|
|
@@ -152327,7 +152470,7 @@ var cachedUsage = null;
|
|
|
152327
152470
|
var usageCacheTimestamp = 0;
|
|
152328
152471
|
var USAGE_CACHE_MS = 15 * 1000;
|
|
152329
152472
|
async function getClaudeOAuthToken() {
|
|
152330
|
-
const primaryPath = join14(
|
|
152473
|
+
const primaryPath = join14(homedir6(), ".claude", ".credentials.json");
|
|
152331
152474
|
try {
|
|
152332
152475
|
if (existsSync12(primaryPath)) {
|
|
152333
152476
|
const content = readFileSync7(primaryPath, "utf-8");
|
|
@@ -152566,10 +152709,10 @@ function checkForCompletion(session) {
|
|
|
152566
152709
|
const stderr = session.stderrBuffer;
|
|
152567
152710
|
session.outputBuffer = "";
|
|
152568
152711
|
session.stderrBuffer = "";
|
|
152569
|
-
const
|
|
152712
|
+
const resolve5 = session.pendingResolve;
|
|
152570
152713
|
session.pendingResolve = null;
|
|
152571
152714
|
session.pendingReject = null;
|
|
152572
|
-
|
|
152715
|
+
resolve5({ stdout, stderr, exitCode });
|
|
152573
152716
|
}
|
|
152574
152717
|
}
|
|
152575
152718
|
function destroySession(id) {
|
|
@@ -152583,11 +152726,11 @@ function destroySession(id) {
|
|
|
152583
152726
|
return true;
|
|
152584
152727
|
}
|
|
152585
152728
|
async function executeCommand(session, command, timeout) {
|
|
152586
|
-
return new Promise((
|
|
152729
|
+
return new Promise((resolve5, reject) => {
|
|
152587
152730
|
session.lastUsedAt = new Date;
|
|
152588
152731
|
session.outputBuffer = "";
|
|
152589
152732
|
session.stderrBuffer = "";
|
|
152590
|
-
session.pendingResolve =
|
|
152733
|
+
session.pendingResolve = resolve5;
|
|
152591
152734
|
session.pendingReject = reject;
|
|
152592
152735
|
const wrappedCommand = `echo "${START_MARKER}"; ${command}; echo "${END_MARKER_PREFIX}$?>>"
|
|
152593
152736
|
`;
|
|
@@ -152596,7 +152739,7 @@ async function executeCommand(session, command, timeout) {
|
|
|
152596
152739
|
session.pendingReject = null;
|
|
152597
152740
|
reject(new Error("Command timed out"));
|
|
152598
152741
|
}, timeout);
|
|
152599
|
-
const originalResolve =
|
|
152742
|
+
const originalResolve = resolve5;
|
|
152600
152743
|
session.pendingResolve = (result) => {
|
|
152601
152744
|
clearTimeout(timeoutId);
|
|
152602
152745
|
originalResolve(result);
|
|
@@ -152873,11 +153016,11 @@ function stopPRMonitor() {
|
|
|
152873
153016
|
var PORT = getSettingByKey("port");
|
|
152874
153017
|
var HOST = process.env.HOST || "localhost";
|
|
152875
153018
|
async function checkPortAvailable(port, host) {
|
|
152876
|
-
return new Promise((
|
|
153019
|
+
return new Promise((resolve5) => {
|
|
152877
153020
|
const server = createServer();
|
|
152878
|
-
server.once("error", () =>
|
|
153021
|
+
server.once("error", () => resolve5(false));
|
|
152879
153022
|
server.once("listening", () => {
|
|
152880
|
-
server.close(() =>
|
|
153023
|
+
server.close(() => resolve5(true));
|
|
152881
153024
|
});
|
|
152882
153025
|
server.listen(port, host);
|
|
152883
153026
|
});
|