vibora 1.10.0 → 1.12.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 +95 -208
- package/dist/assets/index-BiapsD8F.css +1 -0
- package/dist/assets/{index-DptSra7n.js → index-C4ahV9ZS.js} +62 -62
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/server/index.js +233 -88
- package/dist/assets/index-BMm5AnYN.js +0 -45
- package/dist/assets/index-BNhD6i8B.js +0 -45
- package/dist/assets/index-BfcOSnqG.css +0 -1
- package/dist/assets/index-Bj6X8q5U.js +0 -46
- package/dist/assets/index-BrWX1YZT.css +0 -1
- package/dist/assets/index-BxOt05pH.css +0 -1
- package/dist/assets/index-BxbgLbxS.css +0 -1
- package/dist/assets/index-ByL08tgg.js +0 -45
- package/dist/assets/index-C7bo3ECQ.css +0 -1
- package/dist/assets/index-CCtJOkVu.js +0 -116
- package/dist/assets/index-COn-Eyu7.css +0 -1
- package/dist/assets/index-ClQ5DUcr.js +0 -46
- package/dist/assets/index-Co_u49xL.css +0 -1
- package/dist/assets/index-D5T2n3lt.css +0 -1
- package/dist/assets/index-DCMXlbkM.css +0 -1
- package/dist/assets/index-DDBvX7Ij.css +0 -1
- package/dist/assets/index-DJxCuTf-.js +0 -45
- package/dist/assets/index-DP-OAlLv.js +0 -116
- package/dist/assets/index-DS5B_viR.js +0 -116
- package/dist/assets/index-DgP1TgfA.js +0 -45
- package/dist/assets/index-DijiKhVm.js +0 -46
- package/dist/assets/index-Djf_9c_I.css +0 -1
- package/dist/assets/index-Hcd8Y1ma.js +0 -45
- package/dist/assets/index-I_FT38E3.css +0 -1
- package/dist/assets/index-IdRTHnwv.js +0 -51
- package/dist/assets/index-QutiQrzr.js +0 -45
- package/dist/assets/index-ViklVZfD.js +0 -51
- package/dist/assets/index-Xk8D-HCD.css +0 -1
- package/dist/assets/index-f56gTmVJ.css +0 -1
- package/dist/assets/index-qBnZOT-E.js +0 -45
- package/dist/assets/index-r3_O3Fa_.js +0 -45
package/dist/index.html
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
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-
|
|
10
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-C4ahV9ZS.js"></script>
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BiapsD8F.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<div id="root"></div>
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -3800,8 +3800,8 @@ var sessionAuthMiddleware = createMiddleware(async (c, next) => {
|
|
|
3800
3800
|
|
|
3801
3801
|
// server/app.ts
|
|
3802
3802
|
import { readFile as readFile2 } from "fs/promises";
|
|
3803
|
-
import { join as
|
|
3804
|
-
import { existsSync as
|
|
3803
|
+
import { join as join12 } from "path";
|
|
3804
|
+
import { existsSync as existsSync11 } from "fs";
|
|
3805
3805
|
|
|
3806
3806
|
// server/routes/health.ts
|
|
3807
3807
|
var app = new Hono2;
|
|
@@ -13812,6 +13812,30 @@ class BufferManager {
|
|
|
13812
13812
|
}
|
|
13813
13813
|
|
|
13814
13814
|
// server/terminal/terminal-session.ts
|
|
13815
|
+
var ZAI_ENV_VARS = [
|
|
13816
|
+
"ANTHROPIC_AUTH_TOKEN",
|
|
13817
|
+
"ANTHROPIC_BASE_URL",
|
|
13818
|
+
"API_TIMEOUT_MS",
|
|
13819
|
+
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
13820
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL",
|
|
13821
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
13822
|
+
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC"
|
|
13823
|
+
];
|
|
13824
|
+
function getTerminalEnv() {
|
|
13825
|
+
const { PORT: _PORT, ...envWithoutPort } = process.env;
|
|
13826
|
+
const zaiSettings = getZAiSettings();
|
|
13827
|
+
if (zaiSettings.enabled) {
|
|
13828
|
+
return envWithoutPort;
|
|
13829
|
+
}
|
|
13830
|
+
const filtered = {};
|
|
13831
|
+
for (const [key, value] of Object.entries(envWithoutPort)) {
|
|
13832
|
+
if (!ZAI_ENV_VARS.includes(key) && value !== undefined) {
|
|
13833
|
+
filtered[key] = value;
|
|
13834
|
+
}
|
|
13835
|
+
}
|
|
13836
|
+
return filtered;
|
|
13837
|
+
}
|
|
13838
|
+
|
|
13815
13839
|
class TerminalSession {
|
|
13816
13840
|
id;
|
|
13817
13841
|
_name;
|
|
@@ -13864,7 +13888,6 @@ class TerminalSession {
|
|
|
13864
13888
|
start() {
|
|
13865
13889
|
const dtach = getDtachService();
|
|
13866
13890
|
const [cmd, ...args] = dtach.getCreateCommand(this.id);
|
|
13867
|
-
const { PORT: _PORT, ...envWithoutPort } = process.env;
|
|
13868
13891
|
try {
|
|
13869
13892
|
this.pty = spawn(cmd, args, {
|
|
13870
13893
|
name: "xterm-256color",
|
|
@@ -13872,7 +13895,7 @@ class TerminalSession {
|
|
|
13872
13895
|
rows: this.rows,
|
|
13873
13896
|
cwd: this.cwd,
|
|
13874
13897
|
env: {
|
|
13875
|
-
...
|
|
13898
|
+
...getTerminalEnv(),
|
|
13876
13899
|
TERM: "xterm-256color",
|
|
13877
13900
|
COLORTERM: "truecolor"
|
|
13878
13901
|
}
|
|
@@ -13899,7 +13922,6 @@ class TerminalSession {
|
|
|
13899
13922
|
}
|
|
13900
13923
|
this.buffer.loadFromDisk();
|
|
13901
13924
|
const [cmd, ...args] = dtach.getAttachCommand(this.id);
|
|
13902
|
-
const { PORT: _PORT, ...envWithoutPort } = process.env;
|
|
13903
13925
|
try {
|
|
13904
13926
|
this.pty = spawn(cmd, args, {
|
|
13905
13927
|
name: "xterm-256color",
|
|
@@ -13907,7 +13929,7 @@ class TerminalSession {
|
|
|
13907
13929
|
rows: this.rows,
|
|
13908
13930
|
cwd: this.cwd,
|
|
13909
13931
|
env: {
|
|
13910
|
-
...
|
|
13932
|
+
...getTerminalEnv(),
|
|
13911
13933
|
TERM: "xterm-256color",
|
|
13912
13934
|
COLORTERM: "truecolor"
|
|
13913
13935
|
}
|
|
@@ -137647,24 +137669,6 @@ async function updateLinearTicketStatus(identifier, viboraStatus) {
|
|
|
137647
137669
|
}
|
|
137648
137670
|
}
|
|
137649
137671
|
|
|
137650
|
-
// server/services/task-status.ts
|
|
137651
|
-
async function updateTaskStatus(taskId, newStatus) {
|
|
137652
|
-
const existing = db.select().from(tasks).where(eq(tasks.id, taskId)).get();
|
|
137653
|
-
if (!existing)
|
|
137654
|
-
return;
|
|
137655
|
-
const oldStatus = existing.status;
|
|
137656
|
-
if (oldStatus === newStatus)
|
|
137657
|
-
return;
|
|
137658
|
-
const now = new Date().toISOString();
|
|
137659
|
-
db.update(tasks).set({ status: newStatus, updatedAt: now }).where(eq(tasks.id, taskId)).run();
|
|
137660
|
-
broadcast({ type: "task:updated", payload: { taskId } });
|
|
137661
|
-
if (existing.linearTicketId) {
|
|
137662
|
-
updateLinearTicketStatus(existing.linearTicketId, newStatus).catch((err) => {
|
|
137663
|
-
console.error(`Failed to update Linear ticket ${existing.linearTicketId}:`, err);
|
|
137664
|
-
});
|
|
137665
|
-
}
|
|
137666
|
-
}
|
|
137667
|
-
|
|
137668
137672
|
// server/services/notification-service.ts
|
|
137669
137673
|
import { spawn as spawn2 } from "child_process";
|
|
137670
137674
|
async function sendSoundNotification(config) {
|
|
@@ -137838,7 +137842,52 @@ async function testNotificationChannel(channel, settings) {
|
|
|
137838
137842
|
}
|
|
137839
137843
|
}
|
|
137840
137844
|
|
|
137845
|
+
// server/services/task-status.ts
|
|
137846
|
+
async function updateTaskStatus(taskId, newStatus, newPosition) {
|
|
137847
|
+
const existing = db.select().from(tasks).where(eq(tasks.id, taskId)).get();
|
|
137848
|
+
if (!existing)
|
|
137849
|
+
return null;
|
|
137850
|
+
const oldStatus = existing.status;
|
|
137851
|
+
const statusChanged = oldStatus !== newStatus;
|
|
137852
|
+
const now = new Date().toISOString();
|
|
137853
|
+
const updateData = {
|
|
137854
|
+
status: newStatus,
|
|
137855
|
+
updatedAt: now
|
|
137856
|
+
};
|
|
137857
|
+
if (newPosition !== undefined) {
|
|
137858
|
+
updateData.position = newPosition;
|
|
137859
|
+
}
|
|
137860
|
+
db.update(tasks).set(updateData).where(eq(tasks.id, taskId)).run();
|
|
137861
|
+
const updated = db.select().from(tasks).where(eq(tasks.id, taskId)).get();
|
|
137862
|
+
broadcast({ type: "task:updated", payload: { taskId } });
|
|
137863
|
+
if (statusChanged && updated) {
|
|
137864
|
+
if (existing.linearTicketId) {
|
|
137865
|
+
updateLinearTicketStatus(existing.linearTicketId, newStatus).catch((err) => {
|
|
137866
|
+
console.error(`Failed to update Linear ticket ${existing.linearTicketId}:`, err);
|
|
137867
|
+
});
|
|
137868
|
+
}
|
|
137869
|
+
if (newStatus === "IN_REVIEW") {
|
|
137870
|
+
sendNotification({
|
|
137871
|
+
title: "Task Ready for Review",
|
|
137872
|
+
message: `Task "${updated.title}" moved to review`,
|
|
137873
|
+
taskId: updated.id,
|
|
137874
|
+
taskTitle: updated.title,
|
|
137875
|
+
type: "task_status_change"
|
|
137876
|
+
});
|
|
137877
|
+
}
|
|
137878
|
+
if ((newStatus === "DONE" || newStatus === "CANCELED") && updated.worktreePath) {
|
|
137879
|
+
try {
|
|
137880
|
+
killClaudeInTerminalsForWorktree(updated.worktreePath);
|
|
137881
|
+
} catch (err) {
|
|
137882
|
+
console.error(`Failed to kill Claude in worktree ${updated.worktreePath}:`, err);
|
|
137883
|
+
}
|
|
137884
|
+
}
|
|
137885
|
+
}
|
|
137886
|
+
return updated ?? null;
|
|
137887
|
+
}
|
|
137888
|
+
|
|
137841
137889
|
// server/routes/tasks.ts
|
|
137890
|
+
var __dirname = "/home/runner/work/vibora/vibora/server/routes";
|
|
137842
137891
|
function createGitWorktree(repoPath, worktreePath, branch, baseBranch) {
|
|
137843
137892
|
try {
|
|
137844
137893
|
const worktreeParent = path5.dirname(worktreePath);
|
|
@@ -137911,29 +137960,8 @@ function copyFilesToWorktree(repoPath, worktreePath, patterns) {
|
|
|
137911
137960
|
function initializeWorktreeForVibora(worktreePath) {
|
|
137912
137961
|
const claudeLocalPath = path5.join(worktreePath, "CLAUDE.local.md");
|
|
137913
137962
|
const gitignorePath = path5.join(worktreePath, ".gitignore");
|
|
137914
|
-
const
|
|
137915
|
-
|
|
137916
|
-
|
|
137917
|
-
You are working inside a Vibora task worktree. Use the \`vibora\` CLI to manage this task:
|
|
137918
|
-
|
|
137919
|
-
\`\`\`bash
|
|
137920
|
-
# View current task info
|
|
137921
|
-
vibora current-task
|
|
137922
|
-
|
|
137923
|
-
# Associate a PR with this task (enables auto-completion when merged)
|
|
137924
|
-
vibora current-task pr https://github.com/owner/repo/pull/123
|
|
137925
|
-
|
|
137926
|
-
# Associate a Linear ticket with this task
|
|
137927
|
-
vibora current-task linear https://linear.app/team/issue/TEAM-123
|
|
137928
|
-
|
|
137929
|
-
# Update task status when work is complete
|
|
137930
|
-
vibora current-task review # Ready for review
|
|
137931
|
-
vibora current-task done # Task complete
|
|
137932
|
-
\`\`\`
|
|
137933
|
-
|
|
137934
|
-
When you create a PR for this work, run \`vibora current-task pr <url>\` to link it.
|
|
137935
|
-
The task will automatically complete when the PR is merged.
|
|
137936
|
-
`;
|
|
137963
|
+
const templatePath = path5.join(__dirname, "../templates/CLAUDE.local.template.md");
|
|
137964
|
+
const viboraSection = fs3.readFileSync(templatePath, "utf-8");
|
|
137937
137965
|
let claudeContent = "";
|
|
137938
137966
|
if (fs3.existsSync(claudeLocalPath)) {
|
|
137939
137967
|
claudeContent = fs3.readFileSync(claudeLocalPath, "utf-8");
|
|
@@ -138147,37 +138175,7 @@ app2.patch("/:id/status", async (c) => {
|
|
|
138147
138175
|
}
|
|
138148
138176
|
}
|
|
138149
138177
|
}
|
|
138150
|
-
|
|
138151
|
-
status: newStatus,
|
|
138152
|
-
position: newPosition,
|
|
138153
|
-
updatedAt: now
|
|
138154
|
-
}).where(eq(tasks.id, id)).run();
|
|
138155
|
-
const updated = db.select().from(tasks).where(eq(tasks.id, id)).get();
|
|
138156
|
-
broadcast({ type: "task:updated", payload: { taskId: id } });
|
|
138157
|
-
if (oldStatus !== newStatus && existing.linearTicketId) {
|
|
138158
|
-
updateLinearTicketStatus(existing.linearTicketId, newStatus).catch((err) => {
|
|
138159
|
-
console.error("Failed to update Linear ticket status:", err);
|
|
138160
|
-
});
|
|
138161
|
-
}
|
|
138162
|
-
if (oldStatus !== newStatus && updated) {
|
|
138163
|
-
if (newStatus === "IN_REVIEW") {
|
|
138164
|
-
sendNotification({
|
|
138165
|
-
title: "Task Ready for Review",
|
|
138166
|
-
message: `Task "${updated.title}" moved to review`,
|
|
138167
|
-
taskId: updated.id,
|
|
138168
|
-
taskTitle: updated.title,
|
|
138169
|
-
type: "task_status_change"
|
|
138170
|
-
});
|
|
138171
|
-
} else if (newStatus === "DONE") {
|
|
138172
|
-
sendNotification({
|
|
138173
|
-
title: "Task Completed",
|
|
138174
|
-
message: `Task "${updated.title}" marked as done`,
|
|
138175
|
-
taskId: updated.id,
|
|
138176
|
-
taskTitle: updated.title,
|
|
138177
|
-
type: "task_status_change"
|
|
138178
|
-
});
|
|
138179
|
-
}
|
|
138180
|
-
}
|
|
138178
|
+
const updated = await updateTaskStatus(id, newStatus, newPosition);
|
|
138181
138179
|
return c.json(updated ? parseViewState(updated) : null);
|
|
138182
138180
|
} catch (err) {
|
|
138183
138181
|
return c.json({ error: err instanceof Error ? err.message : "Failed to update task status" }, 400);
|
|
@@ -139096,6 +139094,23 @@ app5.post("/notifications/test/:channel", async (c) => {
|
|
|
139096
139094
|
const result = await testNotificationChannel(channel);
|
|
139097
139095
|
return c.json(result);
|
|
139098
139096
|
});
|
|
139097
|
+
app5.post("/notifications/send", async (c) => {
|
|
139098
|
+
try {
|
|
139099
|
+
const body = await c.req.json();
|
|
139100
|
+
if (!body.title || !body.message) {
|
|
139101
|
+
return c.json({ error: "title and message are required" }, 400);
|
|
139102
|
+
}
|
|
139103
|
+
const payload = {
|
|
139104
|
+
title: body.title,
|
|
139105
|
+
message: body.message,
|
|
139106
|
+
type: "task_status_change"
|
|
139107
|
+
};
|
|
139108
|
+
const results = await sendNotification(payload);
|
|
139109
|
+
return c.json({ success: true, results });
|
|
139110
|
+
} catch (err) {
|
|
139111
|
+
return c.json({ error: err instanceof Error ? err.message : "Failed to send notification" }, 400);
|
|
139112
|
+
}
|
|
139113
|
+
});
|
|
139099
139114
|
app5.get("/z-ai", (c) => {
|
|
139100
139115
|
const settings = getZAiSettings();
|
|
139101
139116
|
return c.json(settings);
|
|
@@ -143580,8 +143595,10 @@ app12.get("/check", async (c) => {
|
|
|
143580
143595
|
var auth_default = app12;
|
|
143581
143596
|
|
|
143582
143597
|
// server/routes/monitoring.ts
|
|
143583
|
-
import { readdirSync as readdirSync7, readFileSync as readFileSync7, readlinkSync as readlinkSync2 } from "fs";
|
|
143598
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync7, readlinkSync as readlinkSync2, existsSync as existsSync10 } from "fs";
|
|
143584
143599
|
import { execSync as execSync5 } from "child_process";
|
|
143600
|
+
import { homedir as homedir5 } from "os";
|
|
143601
|
+
import { join as join11 } from "path";
|
|
143585
143602
|
|
|
143586
143603
|
// server/services/metrics-collector.ts
|
|
143587
143604
|
import os5 from "os";
|
|
@@ -144152,8 +144169,8 @@ function findViboraInstances() {
|
|
|
144152
144169
|
const parentPid = getParentPid(pid);
|
|
144153
144170
|
const cmdParts = cmdline.trim().split(/\s+/);
|
|
144154
144171
|
const isBunProcess = cmdParts[0]?.includes("bun") ?? false;
|
|
144155
|
-
const isDevBackend = isBunProcess && cmdline.includes("server/index.ts");
|
|
144156
|
-
const isProdBackend = isBunProcess && !!env.VIBORA_PACKAGE_ROOT;
|
|
144172
|
+
const isDevBackend = isBunProcess && cmdline.includes("server/index.ts") && env.NODE_ENV !== "production";
|
|
144173
|
+
const isProdBackend = isBunProcess && (!!env.VIBORA_PACKAGE_ROOT || cmdline.includes("server/index.ts") && env.NODE_ENV === "production");
|
|
144157
144174
|
if (isDevBackend || isProdBackend) {
|
|
144158
144175
|
const port = parseInt(env.PORT || "3333", 10);
|
|
144159
144176
|
const cwd = getProcessCwd(pid);
|
|
@@ -144256,13 +144273,141 @@ monitoringRoutes.post("/vibora-instances/:pid/kill", async (c) => {
|
|
|
144256
144273
|
port: group.port
|
|
144257
144274
|
});
|
|
144258
144275
|
});
|
|
144276
|
+
var cachedUsage = null;
|
|
144277
|
+
var usageCacheTimestamp = 0;
|
|
144278
|
+
var USAGE_CACHE_MS = 15 * 1000;
|
|
144279
|
+
async function getClaudeOAuthToken() {
|
|
144280
|
+
const primaryPath = join11(homedir5(), ".claude", ".credentials.json");
|
|
144281
|
+
try {
|
|
144282
|
+
if (existsSync10(primaryPath)) {
|
|
144283
|
+
const content = readFileSync7(primaryPath, "utf-8");
|
|
144284
|
+
const config = JSON.parse(content);
|
|
144285
|
+
if (config.claudeAiOauth && typeof config.claudeAiOauth === "object") {
|
|
144286
|
+
const token = config.claudeAiOauth.accessToken;
|
|
144287
|
+
if (token && typeof token === "string" && token.startsWith("sk-ant-oat")) {
|
|
144288
|
+
return token;
|
|
144289
|
+
}
|
|
144290
|
+
}
|
|
144291
|
+
}
|
|
144292
|
+
} catch {}
|
|
144293
|
+
try {
|
|
144294
|
+
const result = execSync5('secret-tool lookup service "Claude Code"', {
|
|
144295
|
+
encoding: "utf-8",
|
|
144296
|
+
timeout: 5000,
|
|
144297
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
144298
|
+
});
|
|
144299
|
+
const token = result.trim();
|
|
144300
|
+
if (token && token.startsWith("sk-ant-oat")) {
|
|
144301
|
+
return token;
|
|
144302
|
+
}
|
|
144303
|
+
} catch {}
|
|
144304
|
+
return null;
|
|
144305
|
+
}
|
|
144306
|
+
async function fetchClaudeUsage(token) {
|
|
144307
|
+
try {
|
|
144308
|
+
const response = await fetch("https://api.anthropic.com/api/oauth/usage", {
|
|
144309
|
+
method: "GET",
|
|
144310
|
+
headers: {
|
|
144311
|
+
"Content-Type": "application/json",
|
|
144312
|
+
"User-Agent": "vibora/1.0.0",
|
|
144313
|
+
Authorization: `Bearer ${token}`,
|
|
144314
|
+
"anthropic-beta": "oauth-2025-04-20"
|
|
144315
|
+
}
|
|
144316
|
+
});
|
|
144317
|
+
if (!response.ok) {
|
|
144318
|
+
return { available: false, fiveHour: null, sevenDay: null, sevenDayOpus: null, sevenDaySonnet: null, error: `API returned ${response.status}` };
|
|
144319
|
+
}
|
|
144320
|
+
const data = await response.json();
|
|
144321
|
+
const parseBlock = (block) => {
|
|
144322
|
+
if (!block)
|
|
144323
|
+
return null;
|
|
144324
|
+
return {
|
|
144325
|
+
percentUsed: block.utilization ?? 0,
|
|
144326
|
+
resetAt: block.resets_at || new Date().toISOString(),
|
|
144327
|
+
isOverLimit: (block.utilization ?? 0) >= 100
|
|
144328
|
+
};
|
|
144329
|
+
};
|
|
144330
|
+
const fiveHour = parseBlock(data.five_hour);
|
|
144331
|
+
const sevenDay = parseBlock(data.seven_day);
|
|
144332
|
+
let fiveHourWithTime = null;
|
|
144333
|
+
if (fiveHour) {
|
|
144334
|
+
const now = new Date;
|
|
144335
|
+
const resetAt = new Date(fiveHour.resetAt);
|
|
144336
|
+
const timeRemainingMinutes = Math.max(0, Math.round((resetAt.getTime() - now.getTime()) / (1000 * 60)));
|
|
144337
|
+
fiveHourWithTime = { ...fiveHour, timeRemainingMinutes };
|
|
144338
|
+
}
|
|
144339
|
+
let sevenDayWithProgress = null;
|
|
144340
|
+
if (sevenDay) {
|
|
144341
|
+
const now = new Date;
|
|
144342
|
+
const resetAt = new Date(sevenDay.resetAt);
|
|
144343
|
+
const periodStart = new Date(resetAt);
|
|
144344
|
+
periodStart.setDate(periodStart.getDate() - 7);
|
|
144345
|
+
let weekProgressPercent;
|
|
144346
|
+
if (now > resetAt) {
|
|
144347
|
+
const newResetAt = new Date(resetAt);
|
|
144348
|
+
newResetAt.setDate(newResetAt.getDate() + 7);
|
|
144349
|
+
const totalMs = newResetAt.getTime() - resetAt.getTime();
|
|
144350
|
+
const elapsedMs = now.getTime() - resetAt.getTime();
|
|
144351
|
+
weekProgressPercent = Math.round(elapsedMs / totalMs * 100);
|
|
144352
|
+
} else {
|
|
144353
|
+
const totalMs = resetAt.getTime() - periodStart.getTime();
|
|
144354
|
+
const elapsedMs = now.getTime() - periodStart.getTime();
|
|
144355
|
+
weekProgressPercent = Math.max(0, Math.min(100, Math.round(elapsedMs / totalMs * 100)));
|
|
144356
|
+
}
|
|
144357
|
+
sevenDayWithProgress = { ...sevenDay, weekProgressPercent };
|
|
144358
|
+
}
|
|
144359
|
+
return {
|
|
144360
|
+
available: true,
|
|
144361
|
+
fiveHour: fiveHourWithTime,
|
|
144362
|
+
sevenDay: sevenDayWithProgress,
|
|
144363
|
+
sevenDayOpus: parseBlock(data.seven_day_opus ?? undefined),
|
|
144364
|
+
sevenDaySonnet: parseBlock(data.seven_day_sonnet ?? undefined)
|
|
144365
|
+
};
|
|
144366
|
+
} catch (err) {
|
|
144367
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
144368
|
+
return { available: false, fiveHour: null, sevenDay: null, sevenDayOpus: null, sevenDaySonnet: null, error: message };
|
|
144369
|
+
}
|
|
144370
|
+
}
|
|
144371
|
+
monitoringRoutes.get("/claude-usage", async (c) => {
|
|
144372
|
+
const now = Date.now();
|
|
144373
|
+
const zaiSettings = getZAiSettings();
|
|
144374
|
+
if (zaiSettings.enabled && zaiSettings.apiKey) {
|
|
144375
|
+
const response = {
|
|
144376
|
+
available: false,
|
|
144377
|
+
fiveHour: null,
|
|
144378
|
+
sevenDay: null,
|
|
144379
|
+
sevenDayOpus: null,
|
|
144380
|
+
sevenDaySonnet: null,
|
|
144381
|
+
error: "Usage tracking is not available when using z.ai proxy. Check your z.ai dashboard at https://z.ai for usage statistics."
|
|
144382
|
+
};
|
|
144383
|
+
cachedUsage = response;
|
|
144384
|
+
usageCacheTimestamp = now;
|
|
144385
|
+
return c.json(response);
|
|
144386
|
+
}
|
|
144387
|
+
const token = await getClaudeOAuthToken();
|
|
144388
|
+
if (!token) {
|
|
144389
|
+
const response = {
|
|
144390
|
+
available: false,
|
|
144391
|
+
fiveHour: null,
|
|
144392
|
+
sevenDay: null,
|
|
144393
|
+
sevenDayOpus: null,
|
|
144394
|
+
sevenDaySonnet: null,
|
|
144395
|
+
error: "No Claude Code OAuth token found"
|
|
144396
|
+
};
|
|
144397
|
+
return c.json(response);
|
|
144398
|
+
}
|
|
144399
|
+
const usage = await fetchClaudeUsage(token);
|
|
144400
|
+
cachedUsage = usage;
|
|
144401
|
+
usageCacheTimestamp = now;
|
|
144402
|
+
return c.json(usage);
|
|
144403
|
+
});
|
|
144259
144404
|
|
|
144260
144405
|
// server/app.ts
|
|
144261
144406
|
function getDistPath() {
|
|
144262
144407
|
if (process.env.VIBORA_PACKAGE_ROOT) {
|
|
144263
|
-
return
|
|
144408
|
+
return join12(process.env.VIBORA_PACKAGE_ROOT, "dist");
|
|
144264
144409
|
}
|
|
144265
|
-
return
|
|
144410
|
+
return join12(process.cwd(), "dist");
|
|
144266
144411
|
}
|
|
144267
144412
|
function createApp() {
|
|
144268
144413
|
const app13 = new Hono2;
|
|
@@ -144309,8 +144454,8 @@ function createApp() {
|
|
|
144309
144454
|
});
|
|
144310
144455
|
};
|
|
144311
144456
|
app13.get("/assets/*", async (c) => {
|
|
144312
|
-
const assetPath =
|
|
144313
|
-
if (
|
|
144457
|
+
const assetPath = join12(distPath, c.req.path);
|
|
144458
|
+
if (existsSync11(assetPath)) {
|
|
144314
144459
|
return serveFile(assetPath);
|
|
144315
144460
|
}
|
|
144316
144461
|
return c.notFound();
|
|
@@ -144318,8 +144463,8 @@ function createApp() {
|
|
|
144318
144463
|
const staticFiles = ["favicon.ico", "vibora-icon.png", "vibora-logo.jpeg", "vite.svg"];
|
|
144319
144464
|
for (const file of staticFiles) {
|
|
144320
144465
|
app13.get(`/${file}`, async () => {
|
|
144321
|
-
const filePath =
|
|
144322
|
-
if (
|
|
144466
|
+
const filePath = join12(distPath, file);
|
|
144467
|
+
if (existsSync11(filePath)) {
|
|
144323
144468
|
return serveFile(filePath);
|
|
144324
144469
|
}
|
|
144325
144470
|
return new Response("Not Found", { status: 404 });
|
|
@@ -144330,7 +144475,7 @@ function createApp() {
|
|
|
144330
144475
|
if (path9.startsWith("/api/") || path9.startsWith("/ws/") || path9 === "/health") {
|
|
144331
144476
|
return next();
|
|
144332
144477
|
}
|
|
144333
|
-
const html = await readFile2(
|
|
144478
|
+
const html = await readFile2(join12(distPath, "index.html"), "utf-8");
|
|
144334
144479
|
return c.html(html);
|
|
144335
144480
|
});
|
|
144336
144481
|
}
|