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/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 existing = db.select().from(repositories).where(eq(repositories.path, body.path)).get();
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 || body.path.split("/").pop() || "repo";
147444
+ const displayName = body.displayName || repoPath.split("/").pop() || "repo";
147352
147445
  const newRepo = {
147353
147446
  id: crypto.randomUUID(),
147354
- path: body.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
- const gitReposDir = settings.paths.defaultGitReposDir;
147380
- const repoName = extractRepoNameFromUrl(body.url);
147381
- const targetPath = join12(gitReposDir, repoName);
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 homedir5 } from "os";
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((resolve4) => setTimeout(resolve4, 500));
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(homedir5(), ".claude", ".credentials.json");
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 resolve4 = session.pendingResolve;
152712
+ const resolve5 = session.pendingResolve;
152570
152713
  session.pendingResolve = null;
152571
152714
  session.pendingReject = null;
152572
- resolve4({ stdout, stderr, exitCode });
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((resolve4, reject) => {
152729
+ return new Promise((resolve5, reject) => {
152587
152730
  session.lastUsedAt = new Date;
152588
152731
  session.outputBuffer = "";
152589
152732
  session.stderrBuffer = "";
152590
- session.pendingResolve = resolve4;
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 = resolve4;
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((resolve4) => {
153019
+ return new Promise((resolve5) => {
152877
153020
  const server = createServer();
152878
- server.once("error", () => resolve4(false));
153021
+ server.once("error", () => resolve5(false));
152879
153022
  server.once("listening", () => {
152880
- server.close(() => resolve4(true));
153023
+ server.close(() => resolve5(true));
152881
153024
  });
152882
153025
  server.listen(port, host);
152883
153026
  });