@vibe80/vibe80 0.2.1 → 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.
Files changed (55) hide show
  1. package/README.md +93 -4
  2. package/bin/vibe80.js +1728 -16
  3. package/client/dist/assets/{DiffPanel-Luk1GrAd.js → DiffPanel-BUJhQj_Q.js} +1 -1
  4. package/client/dist/assets/{ExplorerPanel-CGVmxql1.js → ExplorerPanel-DugEeaO2.js} +1 -1
  5. package/client/dist/assets/{LogsPanel-D8Z8Zam8.js → LogsPanel-BQrGxMu_.js} +1 -1
  6. package/client/dist/assets/{SettingsPanel-H2q2eoDf.js → SettingsPanel-Ci2BdIYO.js} +1 -1
  7. package/client/dist/assets/{TerminalPanel-kEz-cjes.js → TerminalPanel-C-T3t-6T.js} +1 -1
  8. package/client/dist/assets/index-cFi4LM0j.js +711 -0
  9. package/client/dist/assets/index-qNyFxUjK.css +32 -0
  10. package/client/dist/icon_square-512x512.png +0 -0
  11. package/client/dist/icon_square.svg +58 -0
  12. package/client/dist/index.html +3 -2
  13. package/client/dist/sw.js +1 -1
  14. package/client/index.html +1 -0
  15. package/client/public/icon_square-512x512.png +0 -0
  16. package/client/public/icon_square.svg +58 -0
  17. package/client/src/App.jsx +204 -2
  18. package/client/src/assets/vibe80_dark.png +0 -0
  19. package/client/src/assets/vibe80_light.png +0 -0
  20. package/client/src/components/Chat/ChatMessages.jsx +1 -1
  21. package/client/src/components/SessionGate/SessionGate.jsx +282 -90
  22. package/client/src/components/WorktreeTabs.css +11 -0
  23. package/client/src/components/WorktreeTabs.jsx +77 -47
  24. package/client/src/hooks/useChatSocket.js +8 -7
  25. package/client/src/hooks/useRepoBranchesModels.js +12 -6
  26. package/client/src/hooks/useWorktreeCloseConfirm.js +19 -7
  27. package/client/src/hooks/useWorktrees.js +3 -1
  28. package/client/src/index.css +26 -3
  29. package/client/src/locales/en.json +12 -1
  30. package/client/src/locales/fr.json +12 -1
  31. package/docs/api/openapi.json +1 -1
  32. package/package.json +2 -1
  33. package/server/scripts/rotate-workspace-secret.js +1 -1
  34. package/server/src/claudeClient.js +3 -3
  35. package/server/src/codexClient.js +3 -3
  36. package/server/src/config.js +6 -6
  37. package/server/src/index.js +14 -12
  38. package/server/src/middleware/auth.js +7 -7
  39. package/server/src/middleware/debug.js +36 -4
  40. package/server/src/providerLogger.js +2 -2
  41. package/server/src/routes/sessions.js +133 -21
  42. package/server/src/routes/workspaces.js +1 -1
  43. package/server/src/runAs.js +14 -14
  44. package/server/src/services/auth.js +3 -3
  45. package/server/src/services/session.js +182 -14
  46. package/server/src/services/workspace.js +86 -42
  47. package/server/src/storage/index.js +2 -2
  48. package/server/src/storage/redis.js +38 -36
  49. package/server/src/storage/sqlite.js +13 -13
  50. package/server/src/worktreeManager.js +87 -19
  51. package/server/tests/integration/routes/workspaces-routes.test.js +8 -8
  52. package/server/tests/setup/env.js +5 -5
  53. package/server/tests/unit/services/auth.test.js +3 -3
  54. package/client/dist/assets/index-BDQQz6SJ.css +0 -32
  55. package/client/dist/assets/index-knjescVH.js +0 -711
@@ -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,
@@ -2289,7 +2469,14 @@ function App() {
2289
2469
  workspaceSessions={workspaceSessions}
2290
2470
  workspaceSessionsError={workspaceSessionsError}
2291
2471
  workspaceSessionDeletingId={workspaceSessionDeletingId}
2472
+ workspaceSessionConfigId={sessionConfigTargetId}
2473
+ sessionConfigTarget={sessionConfigTarget}
2474
+ workspaceSessionUpdatingId={workspaceSessionUpdatingId}
2475
+ workspaceSessionConfigError={workspaceSessionConfigError}
2292
2476
  handleResumeSession={handleResumeSession}
2477
+ openSessionConfigure={openSessionConfigure}
2478
+ closeSessionConfigure={closeSessionConfigure}
2479
+ handleUpdateAndResumeSession={handleUpdateAndResumeSession}
2293
2480
  handleDeleteSession={handleDeleteSession}
2294
2481
  locale={locale}
2295
2482
  extractRepoName={extractRepoName}
@@ -2314,6 +2501,18 @@ function App() {
2314
2501
  setDefaultInternetAccess={setDefaultInternetAccess}
2315
2502
  defaultDenyGitCredentialsAccess={defaultDenyGitCredentialsAccess}
2316
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}
2317
2516
  attachmentsError={attachmentsError}
2318
2517
  sessionRequested={sessionRequested}
2319
2518
  workspaceBusy={workspaceBusy}
@@ -3053,7 +3252,7 @@ function App() {
3053
3252
  className="worktree-close-confirm-overlay"
3054
3253
  role="dialog"
3055
3254
  aria-modal="true"
3056
- onClick={closeCloseConfirm}
3255
+ onClick={closeConfirmDeleting ? undefined : closeCloseConfirm}
3057
3256
  >
3058
3257
  <div
3059
3258
  className="worktree-close-confirm-dialog"
@@ -3068,6 +3267,7 @@ function App() {
3068
3267
  className="worktree-close-confirm-close"
3069
3268
  aria-label={t("Close")}
3070
3269
  onClick={closeCloseConfirm}
3270
+ disabled={closeConfirmDeleting}
3071
3271
  >
3072
3272
  <FontAwesomeIcon icon={faXmark} />
3073
3273
  </button>
@@ -3080,6 +3280,7 @@ function App() {
3080
3280
  type="button"
3081
3281
  className="worktree-close-confirm-cancel"
3082
3282
  onClick={closeCloseConfirm}
3283
+ disabled={closeConfirmDeleting}
3083
3284
  >
3084
3285
  {t("Cancel")}
3085
3286
  </button>
@@ -3087,6 +3288,7 @@ function App() {
3087
3288
  type="button"
3088
3289
  className="worktree-close-confirm-delete"
3089
3290
  onClick={handleConfirmDelete}
3291
+ disabled={closeConfirmDeleting}
3090
3292
  >
3091
3293
  {t("Delete worktree")}
3092
3294
  </button>
@@ -610,7 +610,7 @@ export default function ChatMessages({
610
610
  ) : (
611
611
  content
612
612
  )}
613
- {filerefs.length ? (
613
+ {filerefs.length && !message?.isStreaming ? (
614
614
  <ul className="fileref-list">
615
615
  {filerefs.map((pathRef) => (
616
616
  <li