@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.
Files changed (55) hide show
  1. package/README.md +132 -16
  2. package/bin/vibe80.js +1728 -16
  3. package/client/dist/assets/{DiffPanel-BKLnyIAZ.js → DiffPanel-BUJhQj_Q.js} +1 -1
  4. package/client/dist/assets/{ExplorerPanel-D3IbBsXz.js → ExplorerPanel-DugEeaO2.js} +1 -1
  5. package/client/dist/assets/{LogsPanel-BwJAFHRP.js → LogsPanel-BQrGxMu_.js} +1 -1
  6. package/client/dist/assets/{SettingsPanel-BfkchMnR.js → SettingsPanel-Ci2BdIYO.js} +1 -1
  7. package/client/dist/assets/{TerminalPanel-BQfMEm-u.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 +205 -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 +295 -91
  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-D1UJw1oP.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,
@@ -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>
@@ -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