@vibe80/vibe80 0.2.0 → 0.2.2
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 +132 -16
- package/bin/vibe80.js +1728 -16
- package/client/dist/assets/{DiffPanel-BKLnyIAZ.js → DiffPanel-BUJhQj_Q.js} +1 -1
- package/client/dist/assets/{ExplorerPanel-D3IbBsXz.js → ExplorerPanel-DugEeaO2.js} +1 -1
- package/client/dist/assets/{LogsPanel-BwJAFHRP.js → LogsPanel-BQrGxMu_.js} +1 -1
- package/client/dist/assets/{SettingsPanel-BfkchMnR.js → SettingsPanel-Ci2BdIYO.js} +1 -1
- package/client/dist/assets/{TerminalPanel-BQfMEm-u.js → TerminalPanel-C-T3t-6T.js} +1 -1
- package/client/dist/assets/index-cFi4LM0j.js +711 -0
- package/client/dist/assets/index-qNyFxUjK.css +32 -0
- package/client/dist/icon_square-512x512.png +0 -0
- package/client/dist/icon_square.svg +58 -0
- package/client/dist/index.html +3 -2
- package/client/dist/sw.js +1 -1
- package/client/index.html +1 -0
- package/client/public/icon_square-512x512.png +0 -0
- package/client/public/icon_square.svg +58 -0
- package/client/src/App.jsx +205 -2
- package/client/src/assets/vibe80_dark.png +0 -0
- package/client/src/assets/vibe80_light.png +0 -0
- package/client/src/components/Chat/ChatMessages.jsx +1 -1
- package/client/src/components/SessionGate/SessionGate.jsx +295 -91
- package/client/src/components/WorktreeTabs.css +11 -0
- package/client/src/components/WorktreeTabs.jsx +77 -47
- package/client/src/hooks/useChatSocket.js +8 -7
- package/client/src/hooks/useRepoBranchesModels.js +12 -6
- package/client/src/hooks/useWorktreeCloseConfirm.js +19 -7
- package/client/src/hooks/useWorktrees.js +3 -1
- package/client/src/index.css +26 -3
- package/client/src/locales/en.json +12 -1
- package/client/src/locales/fr.json +12 -1
- package/docs/api/openapi.json +1 -1
- package/package.json +2 -1
- package/server/scripts/rotate-workspace-secret.js +1 -1
- package/server/src/claudeClient.js +3 -3
- package/server/src/codexClient.js +3 -3
- package/server/src/config.js +6 -6
- package/server/src/index.js +14 -12
- package/server/src/middleware/auth.js +7 -7
- package/server/src/middleware/debug.js +36 -4
- package/server/src/providerLogger.js +2 -2
- package/server/src/routes/sessions.js +133 -21
- package/server/src/routes/workspaces.js +1 -1
- package/server/src/runAs.js +14 -14
- package/server/src/services/auth.js +3 -3
- package/server/src/services/session.js +182 -14
- package/server/src/services/workspace.js +86 -42
- package/server/src/storage/index.js +2 -2
- package/server/src/storage/redis.js +38 -36
- package/server/src/storage/sqlite.js +13 -13
- package/server/src/worktreeManager.js +87 -19
- package/server/tests/integration/routes/workspaces-routes.test.js +8 -8
- package/server/tests/setup/env.js +5 -5
- package/server/tests/unit/services/auth.test.js +3 -3
- package/client/dist/assets/index-BDQQz6SJ.css +0 -32
- package/client/dist/assets/index-D1UJw1oP.js +0 -711
package/client/src/App.jsx
CHANGED
|
@@ -172,6 +172,7 @@ const extractVibe80Blocks = (text, t = (value) => value) => {
|
|
|
172
172
|
const trimmed = String(filePath || "").trim();
|
|
173
173
|
if (trimmed) {
|
|
174
174
|
filerefs.push(trimmed);
|
|
175
|
+
return `\`${trimmed.replace(/`/g, "\\`")}\``;
|
|
175
176
|
}
|
|
176
177
|
return "";
|
|
177
178
|
})
|
|
@@ -662,6 +663,16 @@ function App() {
|
|
|
662
663
|
const [sshKeyInput, setSshKeyInput] = useState("");
|
|
663
664
|
const [httpUsername, setHttpUsername] = useState("");
|
|
664
665
|
const [httpPassword, setHttpPassword] = useState("");
|
|
666
|
+
const [sessionConfigTargetId, setSessionConfigTargetId] = useState("");
|
|
667
|
+
const [sessionConfigAuthMode, setSessionConfigAuthMode] = useState("keep");
|
|
668
|
+
const [sessionConfigSshKey, setSessionConfigSshKey] = useState("");
|
|
669
|
+
const [sessionConfigHttpUsername, setSessionConfigHttpUsername] = useState("");
|
|
670
|
+
const [sessionConfigHttpPassword, setSessionConfigHttpPassword] = useState("");
|
|
671
|
+
const [sessionConfigInternetAccess, setSessionConfigInternetAccess] = useState(true);
|
|
672
|
+
const [sessionConfigDenyGitCredentialsAccess, setSessionConfigDenyGitCredentialsAccess] =
|
|
673
|
+
useState(false);
|
|
674
|
+
const [workspaceSessionUpdatingId, setWorkspaceSessionUpdatingId] = useState(null);
|
|
675
|
+
const [workspaceSessionConfigError, setWorkspaceSessionConfigError] = useState("");
|
|
665
676
|
const [sessionMode, setSessionMode] = useState("new");
|
|
666
677
|
const [annotationMode, setAnnotationMode] = useState(false);
|
|
667
678
|
const [annotationsByScope, setAnnotationsByScope] = useState({});
|
|
@@ -1340,6 +1351,175 @@ function App() {
|
|
|
1340
1351
|
setOpenAiLoginRequest,
|
|
1341
1352
|
});
|
|
1342
1353
|
const explorerStatusByPath = activeExplorer.statusByPath || {};
|
|
1354
|
+
const sessionConfigTarget = useMemo(
|
|
1355
|
+
() =>
|
|
1356
|
+
Array.isArray(workspaceSessions)
|
|
1357
|
+
? workspaceSessions.find((item) => item?.sessionId === sessionConfigTargetId) || null
|
|
1358
|
+
: null,
|
|
1359
|
+
[workspaceSessions, sessionConfigTargetId]
|
|
1360
|
+
);
|
|
1361
|
+
|
|
1362
|
+
const openSessionConfigure = useCallback(
|
|
1363
|
+
(session) => {
|
|
1364
|
+
const sessionId = session?.sessionId || "";
|
|
1365
|
+
if (!sessionId) {
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
setWorkspaceSessionConfigError("");
|
|
1369
|
+
setSessionConfigTargetId(sessionId);
|
|
1370
|
+
setSessionConfigAuthMode("keep");
|
|
1371
|
+
setSessionConfigSshKey("");
|
|
1372
|
+
setSessionConfigHttpUsername("");
|
|
1373
|
+
setSessionConfigHttpPassword("");
|
|
1374
|
+
setSessionConfigInternetAccess(
|
|
1375
|
+
typeof session?.defaultInternetAccess === "boolean"
|
|
1376
|
+
? session.defaultInternetAccess
|
|
1377
|
+
: true
|
|
1378
|
+
);
|
|
1379
|
+
setSessionConfigDenyGitCredentialsAccess(
|
|
1380
|
+
typeof session?.defaultDenyGitCredentialsAccess === "boolean"
|
|
1381
|
+
? session.defaultDenyGitCredentialsAccess
|
|
1382
|
+
: true
|
|
1383
|
+
);
|
|
1384
|
+
},
|
|
1385
|
+
[]
|
|
1386
|
+
);
|
|
1387
|
+
|
|
1388
|
+
const closeSessionConfigure = useCallback(() => {
|
|
1389
|
+
if (workspaceSessionUpdatingId) {
|
|
1390
|
+
return;
|
|
1391
|
+
}
|
|
1392
|
+
setSessionConfigTargetId("");
|
|
1393
|
+
setWorkspaceSessionConfigError("");
|
|
1394
|
+
setSessionConfigAuthMode("keep");
|
|
1395
|
+
setSessionConfigSshKey("");
|
|
1396
|
+
setSessionConfigHttpUsername("");
|
|
1397
|
+
setSessionConfigHttpPassword("");
|
|
1398
|
+
}, [workspaceSessionUpdatingId]);
|
|
1399
|
+
|
|
1400
|
+
const handleUpdateSession = useCallback(async () => {
|
|
1401
|
+
if (!sessionConfigTarget?.sessionId) {
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
const sessionId = sessionConfigTarget.sessionId;
|
|
1405
|
+
const payload = {};
|
|
1406
|
+
if (sessionConfigInternetAccess !== Boolean(sessionConfigTarget?.defaultInternetAccess)) {
|
|
1407
|
+
payload.defaultInternetAccess = sessionConfigInternetAccess;
|
|
1408
|
+
}
|
|
1409
|
+
if (
|
|
1410
|
+
sessionConfigDenyGitCredentialsAccess
|
|
1411
|
+
!== Boolean(sessionConfigTarget?.defaultDenyGitCredentialsAccess)
|
|
1412
|
+
) {
|
|
1413
|
+
payload.defaultDenyGitCredentialsAccess = sessionConfigDenyGitCredentialsAccess;
|
|
1414
|
+
}
|
|
1415
|
+
if (sessionConfigAuthMode !== "keep") {
|
|
1416
|
+
if (sessionConfigAuthMode === "ssh") {
|
|
1417
|
+
const privateKey = sessionConfigSshKey.trim();
|
|
1418
|
+
if (!privateKey) {
|
|
1419
|
+
setWorkspaceSessionConfigError(t("Private SSH key is required."));
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
payload.auth = { type: "ssh", privateKey };
|
|
1423
|
+
} else if (sessionConfigAuthMode === "http") {
|
|
1424
|
+
const username = sessionConfigHttpUsername.trim();
|
|
1425
|
+
if (!username || !sessionConfigHttpPassword) {
|
|
1426
|
+
setWorkspaceSessionConfigError(t("Username and password required."));
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
payload.auth = {
|
|
1430
|
+
type: "http",
|
|
1431
|
+
username,
|
|
1432
|
+
password: sessionConfigHttpPassword,
|
|
1433
|
+
};
|
|
1434
|
+
} else {
|
|
1435
|
+
payload.auth = { type: "none" };
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
if (!Object.keys(payload).length) {
|
|
1439
|
+
closeSessionConfigure();
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1442
|
+
try {
|
|
1443
|
+
setWorkspaceSessionUpdatingId(sessionId);
|
|
1444
|
+
setWorkspaceSessionConfigError("");
|
|
1445
|
+
const response = await apiFetch(
|
|
1446
|
+
`/api/v1/sessions/${encodeURIComponent(sessionId)}`,
|
|
1447
|
+
{
|
|
1448
|
+
method: "PATCH",
|
|
1449
|
+
headers: { "Content-Type": "application/json" },
|
|
1450
|
+
body: JSON.stringify(payload),
|
|
1451
|
+
}
|
|
1452
|
+
);
|
|
1453
|
+
if (!response.ok) {
|
|
1454
|
+
const err = await response.json().catch(() => null);
|
|
1455
|
+
throw new Error(err?.error || t("Failed to update session."));
|
|
1456
|
+
}
|
|
1457
|
+
await response.json().catch(() => null);
|
|
1458
|
+
await loadWorkspaceSessions();
|
|
1459
|
+
showToast?.(t("Session updated."), "success");
|
|
1460
|
+
setSessionConfigAuthMode("keep");
|
|
1461
|
+
setSessionConfigSshKey("");
|
|
1462
|
+
setSessionConfigHttpUsername("");
|
|
1463
|
+
setSessionConfigHttpPassword("");
|
|
1464
|
+
return { ok: true, sessionId };
|
|
1465
|
+
} catch (error) {
|
|
1466
|
+
setWorkspaceSessionConfigError(error.message || t("Failed to update session."));
|
|
1467
|
+
return { ok: false, sessionId };
|
|
1468
|
+
} finally {
|
|
1469
|
+
setWorkspaceSessionUpdatingId(null);
|
|
1470
|
+
}
|
|
1471
|
+
}, [
|
|
1472
|
+
apiFetch,
|
|
1473
|
+
closeSessionConfigure,
|
|
1474
|
+
loadWorkspaceSessions,
|
|
1475
|
+
sessionConfigAuthMode,
|
|
1476
|
+
sessionConfigDenyGitCredentialsAccess,
|
|
1477
|
+
sessionConfigHttpPassword,
|
|
1478
|
+
sessionConfigHttpUsername,
|
|
1479
|
+
sessionConfigInternetAccess,
|
|
1480
|
+
sessionConfigSshKey,
|
|
1481
|
+
sessionConfigTarget,
|
|
1482
|
+
showToast,
|
|
1483
|
+
t,
|
|
1484
|
+
]);
|
|
1485
|
+
|
|
1486
|
+
const handleUpdateAndResumeSession = useCallback(async () => {
|
|
1487
|
+
const sessionId = sessionConfigTarget?.sessionId || "";
|
|
1488
|
+
if (!sessionId) {
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1491
|
+
const result = await handleUpdateSession();
|
|
1492
|
+
if (!result?.ok) {
|
|
1493
|
+
return;
|
|
1494
|
+
}
|
|
1495
|
+
try {
|
|
1496
|
+
setSessionRequested(true);
|
|
1497
|
+
setAttachmentsError("");
|
|
1498
|
+
const response = await apiFetch(
|
|
1499
|
+
`/api/v1/sessions/${encodeURIComponent(sessionId)}`
|
|
1500
|
+
);
|
|
1501
|
+
if (!response.ok) {
|
|
1502
|
+
const payload = await response.json().catch(() => null);
|
|
1503
|
+
throw new Error(payload?.error || t("Unable to resume the session."));
|
|
1504
|
+
}
|
|
1505
|
+
const data = await response.json();
|
|
1506
|
+
setAttachmentSession(data);
|
|
1507
|
+
setSessionConfigTargetId("");
|
|
1508
|
+
} catch (error) {
|
|
1509
|
+
setWorkspaceSessionConfigError(
|
|
1510
|
+
error?.message || t("Unable to resume the session.")
|
|
1511
|
+
);
|
|
1512
|
+
setSessionRequested(false);
|
|
1513
|
+
}
|
|
1514
|
+
}, [
|
|
1515
|
+
apiFetch,
|
|
1516
|
+
handleUpdateSession,
|
|
1517
|
+
sessionConfigTarget?.sessionId,
|
|
1518
|
+
setAttachmentSession,
|
|
1519
|
+
setAttachmentsError,
|
|
1520
|
+
setSessionRequested,
|
|
1521
|
+
t,
|
|
1522
|
+
]);
|
|
1343
1523
|
const explorerDirStatus = useMemo(() => {
|
|
1344
1524
|
const dirStatus = {};
|
|
1345
1525
|
const setStatus = (dirPath, type) => {
|
|
@@ -1441,7 +1621,6 @@ function App() {
|
|
|
1441
1621
|
maybeNotify,
|
|
1442
1622
|
normalizeAttachments,
|
|
1443
1623
|
loadRepoLastCommit,
|
|
1444
|
-
loadBranches,
|
|
1445
1624
|
loadWorktreeLastCommit,
|
|
1446
1625
|
openAiLoginRequest,
|
|
1447
1626
|
setOpenAiLoginRequest,
|
|
@@ -1733,6 +1912,7 @@ function App() {
|
|
|
1733
1912
|
openCloseConfirm,
|
|
1734
1913
|
closeCloseConfirm,
|
|
1735
1914
|
handleConfirmDelete,
|
|
1915
|
+
closeConfirmDeleting,
|
|
1736
1916
|
} = useWorktreeCloseConfirm({
|
|
1737
1917
|
closeConfirm,
|
|
1738
1918
|
setCloseConfirm,
|
|
@@ -2218,6 +2398,7 @@ function App() {
|
|
|
2218
2398
|
"For subscription plans, use auth.json from the Codex CLI login (ChatGPT) or a long-lived token from `claude setup-token` (Claude)."
|
|
2219
2399
|
),
|
|
2220
2400
|
],
|
|
2401
|
+
setupLink: true,
|
|
2221
2402
|
}
|
|
2222
2403
|
: showStep3
|
|
2223
2404
|
? {
|
|
@@ -2288,7 +2469,14 @@ function App() {
|
|
|
2288
2469
|
workspaceSessions={workspaceSessions}
|
|
2289
2470
|
workspaceSessionsError={workspaceSessionsError}
|
|
2290
2471
|
workspaceSessionDeletingId={workspaceSessionDeletingId}
|
|
2472
|
+
workspaceSessionConfigId={sessionConfigTargetId}
|
|
2473
|
+
sessionConfigTarget={sessionConfigTarget}
|
|
2474
|
+
workspaceSessionUpdatingId={workspaceSessionUpdatingId}
|
|
2475
|
+
workspaceSessionConfigError={workspaceSessionConfigError}
|
|
2291
2476
|
handleResumeSession={handleResumeSession}
|
|
2477
|
+
openSessionConfigure={openSessionConfigure}
|
|
2478
|
+
closeSessionConfigure={closeSessionConfigure}
|
|
2479
|
+
handleUpdateAndResumeSession={handleUpdateAndResumeSession}
|
|
2292
2480
|
handleDeleteSession={handleDeleteSession}
|
|
2293
2481
|
locale={locale}
|
|
2294
2482
|
extractRepoName={extractRepoName}
|
|
@@ -2313,6 +2501,18 @@ function App() {
|
|
|
2313
2501
|
setDefaultInternetAccess={setDefaultInternetAccess}
|
|
2314
2502
|
defaultDenyGitCredentialsAccess={defaultDenyGitCredentialsAccess}
|
|
2315
2503
|
setDefaultDenyGitCredentialsAccess={setDefaultDenyGitCredentialsAccess}
|
|
2504
|
+
sessionConfigAuthMode={sessionConfigAuthMode}
|
|
2505
|
+
setSessionConfigAuthMode={setSessionConfigAuthMode}
|
|
2506
|
+
sessionConfigSshKey={sessionConfigSshKey}
|
|
2507
|
+
setSessionConfigSshKey={setSessionConfigSshKey}
|
|
2508
|
+
sessionConfigHttpUsername={sessionConfigHttpUsername}
|
|
2509
|
+
setSessionConfigHttpUsername={setSessionConfigHttpUsername}
|
|
2510
|
+
sessionConfigHttpPassword={sessionConfigHttpPassword}
|
|
2511
|
+
setSessionConfigHttpPassword={setSessionConfigHttpPassword}
|
|
2512
|
+
sessionConfigInternetAccess={sessionConfigInternetAccess}
|
|
2513
|
+
setSessionConfigInternetAccess={setSessionConfigInternetAccess}
|
|
2514
|
+
sessionConfigDenyGitCredentialsAccess={sessionConfigDenyGitCredentialsAccess}
|
|
2515
|
+
setSessionConfigDenyGitCredentialsAccess={setSessionConfigDenyGitCredentialsAccess}
|
|
2316
2516
|
attachmentsError={attachmentsError}
|
|
2317
2517
|
sessionRequested={sessionRequested}
|
|
2318
2518
|
workspaceBusy={workspaceBusy}
|
|
@@ -3052,7 +3252,7 @@ function App() {
|
|
|
3052
3252
|
className="worktree-close-confirm-overlay"
|
|
3053
3253
|
role="dialog"
|
|
3054
3254
|
aria-modal="true"
|
|
3055
|
-
onClick={closeCloseConfirm}
|
|
3255
|
+
onClick={closeConfirmDeleting ? undefined : closeCloseConfirm}
|
|
3056
3256
|
>
|
|
3057
3257
|
<div
|
|
3058
3258
|
className="worktree-close-confirm-dialog"
|
|
@@ -3067,6 +3267,7 @@ function App() {
|
|
|
3067
3267
|
className="worktree-close-confirm-close"
|
|
3068
3268
|
aria-label={t("Close")}
|
|
3069
3269
|
onClick={closeCloseConfirm}
|
|
3270
|
+
disabled={closeConfirmDeleting}
|
|
3070
3271
|
>
|
|
3071
3272
|
<FontAwesomeIcon icon={faXmark} />
|
|
3072
3273
|
</button>
|
|
@@ -3079,6 +3280,7 @@ function App() {
|
|
|
3079
3280
|
type="button"
|
|
3080
3281
|
className="worktree-close-confirm-cancel"
|
|
3081
3282
|
onClick={closeCloseConfirm}
|
|
3283
|
+
disabled={closeConfirmDeleting}
|
|
3082
3284
|
>
|
|
3083
3285
|
{t("Cancel")}
|
|
3084
3286
|
</button>
|
|
@@ -3086,6 +3288,7 @@ function App() {
|
|
|
3086
3288
|
type="button"
|
|
3087
3289
|
className="worktree-close-confirm-delete"
|
|
3088
3290
|
onClick={handleConfirmDelete}
|
|
3291
|
+
disabled={closeConfirmDeleting}
|
|
3089
3292
|
>
|
|
3090
3293
|
{t("Delete worktree")}
|
|
3091
3294
|
</button>
|
|
Binary file
|
|
Binary file
|