vibora 1.1.0 → 1.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/dist/assets/index-ClQ5DUcr.js +46 -0
- package/dist/assets/index-qBnZOT-E.js +45 -0
- package/dist/index.html +1 -1
- package/package.json +1 -1
- package/server/index.js +127 -32
package/dist/index.html
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<link rel="icon" type="image/png" sizes="512x512" href="/vibora-icon.png" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
8
|
<title>Vibora</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-ClQ5DUcr.js"></script>
|
|
10
10
|
<link rel="stylesheet" crossorigin href="/assets/index-DCMXlbkM.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -599,7 +599,7 @@ var require_printer = __commonJS((exports) => {
|
|
|
599
599
|
leave: printDocASTReducer
|
|
600
600
|
});
|
|
601
601
|
}
|
|
602
|
-
var
|
|
602
|
+
var MAX_LINE_LENGTH = 80;
|
|
603
603
|
var printDocASTReducer = {
|
|
604
604
|
Name: function Name(node) {
|
|
605
605
|
return node.value;
|
|
@@ -633,7 +633,7 @@ var require_printer = __commonJS((exports) => {
|
|
|
633
633
|
var { alias, name, arguments: args, directives, selectionSet } = _ref3;
|
|
634
634
|
var prefix = wrap("", alias, ": ") + name;
|
|
635
635
|
var argsLine = prefix + wrap("(", join6(args, ", "), ")");
|
|
636
|
-
if (argsLine.length >
|
|
636
|
+
if (argsLine.length > MAX_LINE_LENGTH) {
|
|
637
637
|
argsLine = prefix + wrap(`(
|
|
638
638
|
`, indent(join6(args, `
|
|
639
639
|
`)), `
|
|
@@ -13540,6 +13540,21 @@ function getDescendantPids(pid) {
|
|
|
13540
13540
|
} catch {}
|
|
13541
13541
|
return descendants;
|
|
13542
13542
|
}
|
|
13543
|
+
function isClaudeProcess(pid) {
|
|
13544
|
+
try {
|
|
13545
|
+
const cmdline = readFileSync2(`/proc/${pid}/cmdline`, "utf-8");
|
|
13546
|
+
return /\bclaude\b/i.test(cmdline);
|
|
13547
|
+
} catch {
|
|
13548
|
+
try {
|
|
13549
|
+
const result = execSync(`ps -p ${pid} -o args= 2>/dev/null || true`, {
|
|
13550
|
+
encoding: "utf-8"
|
|
13551
|
+
});
|
|
13552
|
+
return /\bclaude\b/i.test(result);
|
|
13553
|
+
} catch {
|
|
13554
|
+
return false;
|
|
13555
|
+
}
|
|
13556
|
+
}
|
|
13557
|
+
}
|
|
13543
13558
|
function killProcessTree(pid) {
|
|
13544
13559
|
const descendants = getDescendantPids(pid);
|
|
13545
13560
|
for (const childPid of descendants.reverse()) {
|
|
@@ -13582,6 +13597,21 @@ class DtachService {
|
|
|
13582
13597
|
killProcessTree(pid);
|
|
13583
13598
|
}
|
|
13584
13599
|
}
|
|
13600
|
+
killClaudeInSession(terminalId) {
|
|
13601
|
+
const socketPath = this.getSocketPath(terminalId);
|
|
13602
|
+
const dtachPids = findProcessesByArg(socketPath);
|
|
13603
|
+
let killedAny = false;
|
|
13604
|
+
for (const dtachPid of dtachPids) {
|
|
13605
|
+
const descendants = getDescendantPids(dtachPid);
|
|
13606
|
+
for (const pid of descendants) {
|
|
13607
|
+
if (isClaudeProcess(pid)) {
|
|
13608
|
+
killProcessTree(pid);
|
|
13609
|
+
killedAny = true;
|
|
13610
|
+
}
|
|
13611
|
+
}
|
|
13612
|
+
}
|
|
13613
|
+
return killedAny;
|
|
13614
|
+
}
|
|
13585
13615
|
static isAvailable() {
|
|
13586
13616
|
try {
|
|
13587
13617
|
execSync("which dtach", { encoding: "utf-8" });
|
|
@@ -13602,8 +13632,7 @@ function getDtachService() {
|
|
|
13602
13632
|
// server/terminal/buffer-manager.ts
|
|
13603
13633
|
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, unlinkSync } from "fs";
|
|
13604
13634
|
import * as path4 from "path";
|
|
13605
|
-
var
|
|
13606
|
-
var MAX_LINE_LENGTH = 2000;
|
|
13635
|
+
var MAX_BUFFER_BYTES = 1e6;
|
|
13607
13636
|
function getBuffersDir() {
|
|
13608
13637
|
const dir = path4.join(getViboraDir(), "buffers");
|
|
13609
13638
|
if (!existsSync4(dir)) {
|
|
@@ -13613,47 +13642,43 @@ function getBuffersDir() {
|
|
|
13613
13642
|
}
|
|
13614
13643
|
|
|
13615
13644
|
class BufferManager {
|
|
13616
|
-
|
|
13617
|
-
|
|
13645
|
+
chunks = [];
|
|
13646
|
+
totalBytes = 0;
|
|
13618
13647
|
terminalId = null;
|
|
13619
13648
|
setTerminalId(id) {
|
|
13620
13649
|
this.terminalId = id;
|
|
13621
13650
|
}
|
|
13622
13651
|
append(data) {
|
|
13623
|
-
|
|
13624
|
-
|
|
13625
|
-
|
|
13626
|
-
|
|
13627
|
-
|
|
13628
|
-
const truncated = line.length > MAX_LINE_LENGTH ? line.slice(0, MAX_LINE_LENGTH) + "..." : line;
|
|
13629
|
-
this.lines.push(truncated);
|
|
13630
|
-
}
|
|
13631
|
-
if (this.lines.length > MAX_BUFFER_LINES) {
|
|
13632
|
-
this.lines = this.lines.slice(-MAX_BUFFER_LINES);
|
|
13652
|
+
this.chunks.push({ data, timestamp: Date.now() });
|
|
13653
|
+
this.totalBytes += data.length;
|
|
13654
|
+
while (this.totalBytes > MAX_BUFFER_BYTES && this.chunks.length > 1) {
|
|
13655
|
+
const removed = this.chunks.shift();
|
|
13656
|
+
this.totalBytes -= removed.data.length;
|
|
13633
13657
|
}
|
|
13634
13658
|
}
|
|
13635
13659
|
getContents() {
|
|
13636
|
-
|
|
13637
|
-
`);
|
|
13638
|
-
if (this.partialLine) {
|
|
13639
|
-
return content + `
|
|
13640
|
-
` + this.partialLine;
|
|
13641
|
-
}
|
|
13642
|
-
return content;
|
|
13660
|
+
return this.chunks.map((c) => c.data).join("");
|
|
13643
13661
|
}
|
|
13644
13662
|
clear() {
|
|
13645
|
-
this.
|
|
13646
|
-
this.
|
|
13663
|
+
this.chunks = [];
|
|
13664
|
+
this.totalBytes = 0;
|
|
13647
13665
|
}
|
|
13648
13666
|
getLineCount() {
|
|
13649
|
-
|
|
13667
|
+
const content = this.getContents();
|
|
13668
|
+
return content.split(`
|
|
13669
|
+
`).length;
|
|
13650
13670
|
}
|
|
13651
13671
|
saveToDisk() {
|
|
13652
13672
|
if (!this.terminalId)
|
|
13653
13673
|
return;
|
|
13654
13674
|
const filePath = path4.join(getBuffersDir(), `${this.terminalId}.buf`);
|
|
13655
13675
|
try {
|
|
13656
|
-
|
|
13676
|
+
const content = this.getContents();
|
|
13677
|
+
const fileData = {
|
|
13678
|
+
version: 2,
|
|
13679
|
+
content: Buffer.from(content).toString("base64")
|
|
13680
|
+
};
|
|
13681
|
+
writeFileSync2(filePath, JSON.stringify(fileData), "utf-8");
|
|
13657
13682
|
} catch (err) {
|
|
13658
13683
|
console.error(`[BufferManager] Failed to save buffer for ${this.terminalId}:`, err);
|
|
13659
13684
|
}
|
|
@@ -13664,11 +13689,21 @@ class BufferManager {
|
|
|
13664
13689
|
const filePath = path4.join(getBuffersDir(), `${this.terminalId}.buf`);
|
|
13665
13690
|
try {
|
|
13666
13691
|
if (existsSync4(filePath)) {
|
|
13667
|
-
const
|
|
13668
|
-
|
|
13669
|
-
|
|
13670
|
-
|
|
13671
|
-
|
|
13692
|
+
const raw2 = readFileSync3(filePath, "utf-8");
|
|
13693
|
+
let content;
|
|
13694
|
+
try {
|
|
13695
|
+
const parsed = JSON.parse(raw2);
|
|
13696
|
+
if (parsed.version === 2 && typeof parsed.content === "string") {
|
|
13697
|
+
content = Buffer.from(parsed.content, "base64").toString();
|
|
13698
|
+
} else {
|
|
13699
|
+
content = raw2;
|
|
13700
|
+
}
|
|
13701
|
+
} catch {
|
|
13702
|
+
content = raw2;
|
|
13703
|
+
}
|
|
13704
|
+
this.chunks = [{ data: content, timestamp: Date.now() }];
|
|
13705
|
+
this.totalBytes = content.length;
|
|
13706
|
+
console.log(`[BufferManager] Loaded ${this.totalBytes} bytes for ${this.terminalId}`);
|
|
13672
13707
|
} else {
|
|
13673
13708
|
console.log(`[BufferManager] No buffer file for ${this.terminalId}`);
|
|
13674
13709
|
}
|
|
@@ -13838,6 +13873,10 @@ class TerminalSession {
|
|
|
13838
13873
|
getBuffer() {
|
|
13839
13874
|
return this.buffer.getContents();
|
|
13840
13875
|
}
|
|
13876
|
+
clearBuffer() {
|
|
13877
|
+
this.buffer.clear();
|
|
13878
|
+
this.buffer.saveToDisk();
|
|
13879
|
+
}
|
|
13841
13880
|
getInfo() {
|
|
13842
13881
|
return {
|
|
13843
13882
|
id: this.id,
|
|
@@ -14011,6 +14050,14 @@ class PTYManager {
|
|
|
14011
14050
|
}
|
|
14012
14051
|
return session.getBuffer();
|
|
14013
14052
|
}
|
|
14053
|
+
clearBuffer(terminalId) {
|
|
14054
|
+
const session = this.sessions.get(terminalId);
|
|
14055
|
+
if (!session) {
|
|
14056
|
+
return false;
|
|
14057
|
+
}
|
|
14058
|
+
session.clearBuffer();
|
|
14059
|
+
return true;
|
|
14060
|
+
}
|
|
14014
14061
|
getInfo(terminalId) {
|
|
14015
14062
|
const session = this.sessions.get(terminalId);
|
|
14016
14063
|
if (!session) {
|
|
@@ -14021,6 +14068,14 @@ class PTYManager {
|
|
|
14021
14068
|
listTerminals() {
|
|
14022
14069
|
return Array.from(this.sessions.values()).map((s) => s.getInfo());
|
|
14023
14070
|
}
|
|
14071
|
+
killClaudeInTerminal(terminalId) {
|
|
14072
|
+
const session = this.sessions.get(terminalId);
|
|
14073
|
+
if (!session) {
|
|
14074
|
+
return false;
|
|
14075
|
+
}
|
|
14076
|
+
const dtach = getDtachService();
|
|
14077
|
+
return dtach.killClaudeInSession(terminalId);
|
|
14078
|
+
}
|
|
14024
14079
|
detachAll() {
|
|
14025
14080
|
for (const session of this.sessions.values()) {
|
|
14026
14081
|
session.detach();
|
|
@@ -14059,6 +14114,19 @@ function destroyTerminalAndBroadcast(terminalId) {
|
|
|
14059
14114
|
}
|
|
14060
14115
|
return success;
|
|
14061
14116
|
}
|
|
14117
|
+
function killClaudeInTerminalsForWorktree(worktreePath) {
|
|
14118
|
+
const manager = getPTYManager();
|
|
14119
|
+
const terminals2 = manager.listTerminals();
|
|
14120
|
+
let count = 0;
|
|
14121
|
+
for (const terminal of terminals2) {
|
|
14122
|
+
if (terminal.cwd === worktreePath) {
|
|
14123
|
+
if (manager.killClaudeInTerminal(terminal.id)) {
|
|
14124
|
+
count++;
|
|
14125
|
+
}
|
|
14126
|
+
}
|
|
14127
|
+
}
|
|
14128
|
+
return count;
|
|
14129
|
+
}
|
|
14062
14130
|
|
|
14063
14131
|
// server/terminal/tab-manager.ts
|
|
14064
14132
|
var VIEW_STATE_ID = "singleton";
|
|
@@ -14308,6 +14376,17 @@ var terminalWebSocketHandlers = {
|
|
|
14308
14376
|
}
|
|
14309
14377
|
break;
|
|
14310
14378
|
}
|
|
14379
|
+
case "terminal:clearBuffer": {
|
|
14380
|
+
const { terminalId } = message.payload;
|
|
14381
|
+
const success = ptyManager2.clearBuffer(terminalId);
|
|
14382
|
+
if (success) {
|
|
14383
|
+
broadcastToTerminal(terminalId, {
|
|
14384
|
+
type: "terminal:bufferCleared",
|
|
14385
|
+
payload: { terminalId }
|
|
14386
|
+
});
|
|
14387
|
+
}
|
|
14388
|
+
break;
|
|
14389
|
+
}
|
|
14311
14390
|
case "tab:create": {
|
|
14312
14391
|
const { name, position } = message.payload;
|
|
14313
14392
|
const tab = tabManager2.create({ name, position });
|
|
@@ -138015,6 +138094,22 @@ app2.patch("/:id/status", async (c) => {
|
|
|
138015
138094
|
return c.json({ error: err instanceof Error ? err.message : "Failed to update task status" }, 400);
|
|
138016
138095
|
}
|
|
138017
138096
|
});
|
|
138097
|
+
app2.post("/:id/kill-claude", (c) => {
|
|
138098
|
+
const id = c.req.param("id");
|
|
138099
|
+
const task = db.select().from(tasks).where(eq(tasks.id, id)).get();
|
|
138100
|
+
if (!task) {
|
|
138101
|
+
return c.json({ error: "Task not found" }, 404);
|
|
138102
|
+
}
|
|
138103
|
+
if (!task.worktreePath) {
|
|
138104
|
+
return c.json({ success: true, terminalsAffected: 0 });
|
|
138105
|
+
}
|
|
138106
|
+
try {
|
|
138107
|
+
const count = killClaudeInTerminalsForWorktree(task.worktreePath);
|
|
138108
|
+
return c.json({ success: true, terminalsAffected: count });
|
|
138109
|
+
} catch {
|
|
138110
|
+
return c.json({ success: true, terminalsAffected: 0 });
|
|
138111
|
+
}
|
|
138112
|
+
});
|
|
138018
138113
|
var tasks_default = app2;
|
|
138019
138114
|
|
|
138020
138115
|
// server/routes/git.ts
|