@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
@@ -47,7 +47,6 @@ export default function useChatSocket({
47
47
  maybeNotify,
48
48
  normalizeAttachments,
49
49
  loadRepoLastCommit,
50
- loadBranches,
51
50
  loadWorktreeLastCommit,
52
51
  openAiLoginRequest,
53
52
  setOpenAiLoginRequest,
@@ -264,12 +263,14 @@ export default function useChatSocket({
264
263
  id: payload.itemId,
265
264
  role: "assistant",
266
265
  text: payload.delta,
266
+ isStreaming: true,
267
267
  });
268
268
  return next;
269
269
  }
270
270
 
271
271
  const updated = { ...next[existingIndex] };
272
272
  updated.text += payload.delta;
273
+ updated.isStreaming = true;
273
274
  next[existingIndex] = updated;
274
275
  return next;
275
276
  });
@@ -294,6 +295,7 @@ export default function useChatSocket({
294
295
  id: payload.itemId,
295
296
  role: "assistant",
296
297
  text: payload.text,
298
+ isStreaming: false,
297
299
  });
298
300
  return next;
299
301
  }
@@ -301,6 +303,7 @@ export default function useChatSocket({
301
303
  next[existingIndex] = {
302
304
  ...next[existingIndex],
303
305
  text: payload.text,
306
+ isStreaming: false,
304
307
  };
305
308
  return next;
306
309
  });
@@ -366,9 +369,6 @@ export default function useChatSocket({
366
369
  });
367
370
  if (payload.request === "run" || payload.request === "git") {
368
371
  void loadRepoLastCommit();
369
- if (typeof loadBranches === "function") {
370
- void loadBranches();
371
- }
372
372
  }
373
373
  }
374
374
 
@@ -823,9 +823,6 @@ export default function useChatSocket({
823
823
  });
824
824
  if (payload.request === "run" || payload.request === "git") {
825
825
  void loadWorktreeLastCommit(wtId);
826
- if (typeof loadBranches === "function" && wtId === "main") {
827
- void loadBranches();
828
- }
829
826
  }
830
827
  }
831
828
 
@@ -924,12 +921,14 @@ export default function useChatSocket({
924
921
  id: payload.itemId,
925
922
  role: "assistant",
926
923
  text: payload.delta || "",
924
+ isStreaming: true,
927
925
  });
928
926
  } else {
929
927
  messages[existingIdx] = {
930
928
  ...messages[existingIdx],
931
929
  text:
932
930
  (messages[existingIdx].text || "") + (payload.delta || ""),
931
+ isStreaming: true,
933
932
  };
934
933
  }
935
934
  } else {
@@ -938,11 +937,13 @@ export default function useChatSocket({
938
937
  id: payload.itemId,
939
938
  role: "assistant",
940
939
  text: payload.text || "",
940
+ isStreaming: false,
941
941
  });
942
942
  } else {
943
943
  messages[existingIdx] = {
944
944
  ...messages[existingIdx],
945
945
  text: payload.text || "",
946
+ isStreaming: false,
946
947
  };
947
948
  }
948
949
  }
@@ -23,12 +23,14 @@ export default function useRepoBranchesModels({
23
23
  const [branchError, setBranchError] = useState("");
24
24
  const initialBranchRef = useRef("");
25
25
 
26
- const loadBranches = useCallback(async () => {
26
+ const loadBranches = useCallback(async ({ background = false } = {}) => {
27
27
  if (!attachmentSessionId) {
28
28
  return;
29
29
  }
30
- setBranchLoading(true);
31
- setBranchError("");
30
+ if (!background) {
31
+ setBranchLoading(true);
32
+ setBranchError("");
33
+ }
32
34
  try {
33
35
  const response = await apiFetch(
34
36
  `/api/v1/sessions/${encodeURIComponent(
@@ -46,9 +48,13 @@ export default function useRepoBranchesModels({
46
48
  setDefaultBranch(payload.current);
47
49
  }
48
50
  } catch (error) {
49
- setBranchError(error.message || t("Unable to load branches."));
51
+ if (!background) {
52
+ setBranchError(error.message || t("Unable to load branches."));
53
+ }
50
54
  } finally {
51
- setBranchLoading(false);
55
+ if (!background) {
56
+ setBranchLoading(false);
57
+ }
52
58
  }
53
59
  }, [attachmentSessionId, apiFetch, t]);
54
60
 
@@ -110,7 +116,7 @@ export default function useRepoBranchesModels({
110
116
  initialBranchRef.current = "";
111
117
  setDefaultBranch("");
112
118
  setProviderModelState({});
113
- loadBranches();
119
+ loadBranches({ background: true });
114
120
  }, [attachmentSessionId, loadBranches]);
115
121
 
116
122
  useEffect(() => {
@@ -1,4 +1,4 @@
1
- import { useCallback } from "react";
1
+ import { useCallback, useState } from "react";
2
2
 
3
3
  export default function useWorktreeCloseConfirm({
4
4
  closeConfirm,
@@ -7,6 +7,8 @@ export default function useWorktreeCloseConfirm({
7
7
  activeWorktreeIdRef,
8
8
  closeWorktree,
9
9
  }) {
10
+ const [closeConfirmDeleting, setCloseConfirmDeleting] = useState(false);
11
+
10
12
  const openCloseConfirm = useCallback(
11
13
  (worktreeId) => {
12
14
  if (!worktreeId || worktreeId === "main") {
@@ -18,21 +20,30 @@ export default function useWorktreeCloseConfirm({
18
20
  );
19
21
 
20
22
  const closeCloseConfirm = useCallback(() => {
23
+ if (closeConfirmDeleting) {
24
+ return;
25
+ }
21
26
  setCloseConfirm(null);
22
- }, [setCloseConfirm]);
27
+ }, [closeConfirmDeleting, setCloseConfirm]);
23
28
 
24
29
  const handleConfirmDelete = useCallback(async () => {
25
- if (!closeConfirm?.worktreeId) {
30
+ if (!closeConfirm?.worktreeId || closeConfirmDeleting) {
26
31
  return;
27
32
  }
28
- if (activeWorktreeIdRef.current === closeConfirm.worktreeId) {
29
- setActiveWorktreeId("main");
33
+ setCloseConfirmDeleting(true);
34
+ try {
35
+ if (activeWorktreeIdRef.current === closeConfirm.worktreeId) {
36
+ setActiveWorktreeId("main");
37
+ }
38
+ await closeWorktree(closeConfirm.worktreeId);
39
+ setCloseConfirm(null);
40
+ } finally {
41
+ setCloseConfirmDeleting(false);
30
42
  }
31
- await closeWorktree(closeConfirm.worktreeId);
32
- setCloseConfirm(null);
33
43
  }, [
34
44
  activeWorktreeIdRef,
35
45
  closeConfirm,
46
+ closeConfirmDeleting,
36
47
  closeWorktree,
37
48
  setActiveWorktreeId,
38
49
  setCloseConfirm,
@@ -42,5 +53,6 @@ export default function useWorktreeCloseConfirm({
42
53
  openCloseConfirm,
43
54
  closeCloseConfirm,
44
55
  handleConfirmDelete,
56
+ closeConfirmDeleting,
45
57
  };
46
58
  }
@@ -249,7 +249,7 @@ export default function useWorktrees({
249
249
  }) => {
250
250
  if (!attachmentSessionId) {
251
251
  showToast?.(t("Session not found."), "error");
252
- return;
252
+ return false;
253
253
  }
254
254
  try {
255
255
  const response = await apiFetch(
@@ -313,11 +313,13 @@ export default function useWorktrees({
313
313
  }));
314
314
  setActiveWorktreeId(payload.worktreeId);
315
315
  void requestWorktreesList();
316
+ return true;
316
317
  } catch (error) {
317
318
  showToast?.(
318
319
  error.message || t("Failed to create parallel request."),
319
320
  "error"
320
321
  );
322
+ return false;
321
323
  }
322
324
  },
323
325
  [
@@ -633,14 +633,21 @@ textarea {
633
633
 
634
634
  .session-item {
635
635
  display: flex;
636
- justify-content: space-between;
637
- align-items: center;
638
- gap: 12px;
636
+ flex-direction: column;
637
+ gap: 10px;
639
638
  padding: 10px 12px;
640
639
  border-radius: 12px;
641
640
  background: rgba(20, 19, 17, 0.05);
642
641
  }
643
642
 
643
+ .session-item-row {
644
+ display: flex;
645
+ justify-content: space-between;
646
+ align-items: center;
647
+ gap: 12px;
648
+ width: 100%;
649
+ }
650
+
644
651
  .session-item-meta {
645
652
  display: flex;
646
653
  flex-direction: column;
@@ -669,6 +676,15 @@ textarea {
669
676
  cursor: pointer;
670
677
  }
671
678
 
679
+ .session-list-icon-button {
680
+ width: 36px;
681
+ height: 36px;
682
+ padding: 0;
683
+ display: inline-flex;
684
+ align-items: center;
685
+ justify-content: center;
686
+ }
687
+
672
688
  .session-list-button.is-danger {
673
689
  background: #e05a4f;
674
690
  }
@@ -684,6 +700,13 @@ textarea {
684
700
  align-items: center;
685
701
  }
686
702
 
703
+ .session-config-actions {
704
+ display: flex;
705
+ justify-content: flex-end;
706
+ gap: 8px;
707
+ margin-top: 10px;
708
+ }
709
+
687
710
  .toast-container {
688
711
  position: fixed;
689
712
  right: 24px;
@@ -33,5 +33,16 @@
33
33
  "Annotations": "Annotations",
34
34
  "Only sent with the next message.": "Only sent with the next message.",
35
35
  "No annotations yet.": "No annotations yet.",
36
- "Write annotation...": "Write annotation..."
36
+ "Write annotation...": "Write annotation...",
37
+ "Configure session": "Configurer la session",
38
+ "Keep current credentials": "Conserver les identifiants actuels",
39
+ "Session name": "Nom de la session",
40
+ "Session name is required.": "Le nom de session est requis.",
41
+ "Session updated.": "Session mise a jour.",
42
+ "Failed to update session.": "Echec de la mise a jour de la session.",
43
+ "Private SSH key is required.": "La cle SSH privee est requise.",
44
+ "Validate": "Valider",
45
+ "Keep unchanged": "Conserver tel quel",
46
+ "Update & Resume": "Mettre a jour et reprendre",
47
+ "Updating...": "Mise a jour..."
37
48
  }
@@ -317,5 +317,16 @@
317
317
  "{{count}} item(s)": "{{count}} élément(s)",
318
318
  "{{count}} KB": "{{count}} Ko",
319
319
  "{{count}} lines": "{{count}} lignes",
320
- "{{count}} MB": "{{count}} Mo"
320
+ "{{count}} MB": "{{count}} Mo",
321
+ "Configure session": "Configurer la session",
322
+ "Keep current credentials": "Conserver les identifiants actuels",
323
+ "Session name": "Nom de la session",
324
+ "Session name is required.": "Le nom de session est requis.",
325
+ "Session updated.": "Session mise a jour.",
326
+ "Failed to update session.": "Echec de la mise a jour de la session.",
327
+ "Private SSH key is required.": "La cle SSH privee est requise.",
328
+ "Validate": "Valider",
329
+ "Keep unchanged": "Conserver tel quel",
330
+ "Update & Resume": "Mettre a jour et reprendre",
331
+ "Updating...": "Mise a jour..."
321
332
  }
@@ -3057,7 +3057,7 @@
3057
3057
  "attachments": {
3058
3058
  "type": "array",
3059
3059
  "items": {
3060
- "$ref": "#/components/schemas/Attachment"
3060
+ "$ref": "#/components/schemas/AttachmentRef"
3061
3061
  }
3062
3062
  }
3063
3063
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibe80/vibe80",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "private": false,
5
5
  "workspaces": [
6
6
  "server",
@@ -31,6 +31,7 @@
31
31
  "prepack": "npm --workspace client run build"
32
32
  },
33
33
  "dependencies": {
34
+ "commander": "^12.1.0",
34
35
  "concurrently": "^8.2.2",
35
36
  "docker-names": "^1.2.1",
36
37
  "express": "^4.19.2",
@@ -13,7 +13,7 @@ const printUsage = () => {
13
13
  " node server/scripts/rotate-workspace-secret.js --workspace-id <workspaceId> [--workspace-secret <secret>] [--json]",
14
14
  "",
15
15
  "Requirements:",
16
- " - SERVER env vars must be set (e.g. STORAGE_BACKEND, DEPLOYMENT_MODE, etc.)",
16
+ " - SERVER env vars must be set (e.g. VIBE80_STORAGE_BACKEND, VIBE80_DEPLOYMENT_MODE, etc.)",
17
17
  ].join("\n")
18
18
  );
19
19
  };
@@ -2,13 +2,13 @@ import { spawn } from "child_process";
2
2
  import { EventEmitter } from "events";
3
3
  import crypto from "crypto";
4
4
  import path from "path";
5
- import { SYSTEM_PROMPT } from "./config.js";
5
+ import { VIBE80_SYSTEM_PROMPT } from "./config.js";
6
6
  import { createProviderLogger } from "./providerLogger.js";
7
7
  import { buildSandboxArgs, getWorkspaceHome, runAsCommand } from "./runAs.js";
8
8
 
9
9
  const RUN_AS_HELPER = process.env.VIBE80_RUN_AS_HELPER || "/usr/local/bin/vibe80-run-as";
10
10
  const SUDO_PATH = process.env.VIBE80_SUDO_PATH || "sudo";
11
- const isMonoUser = process.env.DEPLOYMENT_MODE === "mono_user";
11
+ const isMonoUser = process.env.VIBE80_DEPLOYMENT_MODE === "mono_user";
12
12
 
13
13
  const createTurnId = () =>
14
14
  typeof crypto.randomUUID === "function"
@@ -62,7 +62,7 @@ export class ClaudeCliClient extends EventEmitter {
62
62
  sessionId: this.sessionId,
63
63
  worktreeId: this.worktreeId,
64
64
  });
65
- this.systemPrompt = SYSTEM_PROMPT;
65
+ this.systemPrompt = VIBE80_SYSTEM_PROMPT;
66
66
  this.activeProcess = null;
67
67
  }
68
68
 
@@ -1,13 +1,13 @@
1
1
  import { spawn } from "child_process";
2
2
  import { EventEmitter } from "events";
3
3
  import path from "path";
4
- import { SYSTEM_PROMPT } from "./config.js";
4
+ import { VIBE80_SYSTEM_PROMPT } from "./config.js";
5
5
  import { createProviderLogger } from "./providerLogger.js";
6
6
  import { buildSandboxArgs, getWorkspaceHome } from "./runAs.js";
7
7
 
8
8
  const RUN_AS_HELPER = process.env.VIBE80_RUN_AS_HELPER || "/usr/local/bin/vibe80-run-as";
9
9
  const SUDO_PATH = process.env.VIBE80_SUDO_PATH || "sudo";
10
- const isMonoUser = process.env.DEPLOYMENT_MODE === "mono_user";
10
+ const isMonoUser = process.env.VIBE80_DEPLOYMENT_MODE === "mono_user";
11
11
 
12
12
  export class CodexAppServerClient extends EventEmitter {
13
13
  constructor({
@@ -430,7 +430,7 @@ export class CodexAppServerClient extends EventEmitter {
430
430
  "sandbox_workspace_write.network_access": Boolean(this.internetAccess),
431
431
  "web_search": this.internetAccess ? "live" : "disabled"
432
432
  },
433
- baseInstructions: SYSTEM_PROMPT,
433
+ baseInstructions: VIBE80_SYSTEM_PROMPT,
434
434
  sandbox: sandboxMode,
435
435
  approvalPolicy: "never"
436
436
  };
@@ -4,8 +4,8 @@ import { fileURLToPath } from "url";
4
4
  const __filename = fileURLToPath(import.meta.url);
5
5
  const __dirname = path.dirname(__filename);
6
6
 
7
- export const SYSTEM_PROMPT =
8
- process.env.SYSTEM_PROMPT ||
7
+ export const VIBE80_SYSTEM_PROMPT =
8
+ process.env.VIBE80_SYSTEM_PROMPT ||
9
9
  "output markdown format for inline generated text;" +
10
10
  "Reference files using relative paths when possible; " +
11
11
  "When proposing possible next steps, use: " +
@@ -18,10 +18,10 @@ export const SYSTEM_PROMPT =
18
18
  "Use <!-- vibe80:task <short_task_description> --> to notify the user about what you are doing;" +
19
19
  "Use <!-- vibe80:fileref <filepath> --> to reference any file in the current repository";
20
20
 
21
- export const DEFAULT_GIT_AUTHOR_NAME =
22
- process.env.DEFAULT_GIT_AUTHOR_NAME || "Vibe80 agent";
23
- export const DEFAULT_GIT_AUTHOR_EMAIL =
24
- process.env.DEFAULT_GIT_AUTHOR_EMAIL || "vibe80@example.org";
21
+ export const VIBE80_DEFAULT_GIT_AUTHOR_NAME =
22
+ process.env.VIBE80_DEFAULT_GIT_AUTHOR_NAME || "Vibe80 agent";
23
+ export const VIBE80_DEFAULT_GIT_AUTHOR_EMAIL =
24
+ process.env.VIBE80_DEFAULT_GIT_AUTHOR_EMAIL || "vibe80@example.org";
25
25
 
26
26
  export const GIT_HOOKS_DIR = process.env.VIBE80_GIT_HOOKS_DIR
27
27
  || path.resolve(__dirname, "../../git_hooks");
@@ -85,7 +85,7 @@ const __dirname = path.dirname(__filename);
85
85
  const app = express();
86
86
  const server = http.createServer(app);
87
87
  const wss = new WebSocketServer({ noServer: true });
88
- const trustProxySetting = process.env.TRUST_PROXY;
88
+ const trustProxySetting = process.env.VIBE80_TRUST_PROXY;
89
89
  if (trustProxySetting !== undefined) {
90
90
  const normalized = trustProxySetting.trim().toLowerCase();
91
91
  if (normalized === "true") {
@@ -102,7 +102,7 @@ if (trustProxySetting !== undefined) {
102
102
  }
103
103
  }
104
104
  const terminalRequested = !/^(0|false|no|off)$/i.test(
105
- process.env.TERMINAL_ENABLED || ""
105
+ process.env.VIBE80_TERMINAL_ENABLED || ""
106
106
  );
107
107
  let pty = null;
108
108
  let terminalAvailable = false;
@@ -119,17 +119,17 @@ if (terminalRequested) {
119
119
  }
120
120
  const terminalEnabled = terminalRequested && terminalAvailable;
121
121
  const allowRunSlashCommand = !/^(0|false|no|off)$/i.test(
122
- process.env.ALLOW_RUN_SLASH_COMMAND || ""
122
+ process.env.VIBE80_ALLOW_RUN_SLASH_COMMAND || ""
123
123
  );
124
124
  const allowGitSlashCommand = !/^(0|false|no|off)$/i.test(
125
- process.env.ALLOW_GIT_SLASH_COMMAND || ""
125
+ process.env.VIBE80_ALLOW_GIT_SLASH_COMMAND || ""
126
126
  );
127
127
  const codexIdleTtlSeconds = Number.parseInt(
128
- process.env.CODEX_IDLE_TTL_SECONDS || "300",
128
+ process.env.VIBE80_CODEX_IDLE_TTL_SECONDS || "300",
129
129
  10
130
130
  );
131
131
  const codexIdleGcIntervalSeconds = Number.parseInt(
132
- process.env.CODEX_IDLE_GC_INTERVAL_SECONDS || "60",
132
+ process.env.VIBE80_CODEX_IDLE_GC_INTERVAL_SECONDS || "60",
133
133
  10
134
134
  );
135
135
  const worktreeStatusIntervalMs = 10 * 1000;
@@ -157,9 +157,9 @@ const resolveWorkspaceTokenErrorCode = (error) => {
157
157
  return "WORKSPACE_TOKEN_INVALID";
158
158
  };
159
159
 
160
- const deploymentMode = process.env.DEPLOYMENT_MODE || "mono_user";
160
+ const deploymentMode = process.env.VIBE80_DEPLOYMENT_MODE || "mono_user";
161
161
  if (deploymentMode !== "mono_user" && deploymentMode !== "multi_user") {
162
- console.error(`Invalid DEPLOYMENT_MODE: ${deploymentMode}. Use mono_user or multi_user.`);
162
+ console.error(`Invalid VIBE80_DEPLOYMENT_MODE: ${deploymentMode}. Use mono_user or multi_user.`);
163
163
  process.exit(1);
164
164
  }
165
165
 
@@ -182,8 +182,10 @@ const apiLimiter = rateLimit({
182
182
  });
183
183
 
184
184
  const authLimiter = rateLimit({
185
- windowMs: 60 * 1000,
186
- max: 10,
185
+ windowMs: 5 * 60 * 1000,
186
+ max: 5,
187
+ skipSuccessfulRequests: true,
188
+ requestWasSuccessful: (_req, res) => res.statusCode !== 401,
187
189
  standardHeaders: true,
188
190
  legacyHeaders: false,
189
191
  });
@@ -1508,12 +1510,12 @@ process.on("SIGINT", () => {
1508
1510
  void gracefulShutdown("SIGINT");
1509
1511
  });
1510
1512
 
1511
- const port = process.env.PORT || 5179;
1513
+ const port = process.env.VIBE80_PORT || 5179;
1512
1514
  server.listen(port, async () => {
1513
1515
  console.log(`Server listening on http://localhost:${port}`);
1514
1516
  if (deploymentMode === "mono_user") {
1515
1517
  const monoAuthRecord = createMonoAuthToken("default");
1516
- const monoAuthOrigin = process.env.MONO_AUTH_ORIGIN || `http://127.0.0.1:${port}`;
1518
+ const monoAuthOrigin = process.env.VIBE80_MONO_AUTH_ORIGIN || `http://127.0.0.1:${port}`;
1517
1519
  const monoAuthUrl = `${monoAuthOrigin.replace(/\/+$/, "")}/#mono_auth=${encodeURIComponent(
1518
1520
  monoAuthRecord.token
1519
1521
  )}`;
@@ -5,20 +5,20 @@ import path from "path";
5
5
  import jwt from "jsonwebtoken";
6
6
 
7
7
  const homeDir = process.env.HOME || os.homedir();
8
- const isMonoUser = process.env.DEPLOYMENT_MODE === "mono_user";
8
+ const isMonoUser = process.env.VIBE80_DEPLOYMENT_MODE === "mono_user";
9
9
  const defaultDataDirectory = isMonoUser
10
10
  ? path.join(homeDir, ".vibe80")
11
11
  : "/var/lib/vibe80";
12
12
  const dataDirectory = process.env.VIBE80_DATA_DIRECTORY || defaultDataDirectory;
13
- const jwtKeyPath = process.env.JWT_KEY_PATH || path.join(dataDirectory, "jwt.key");
14
- const jwtIssuer = process.env.JWT_ISSUER || "vibe80";
15
- const jwtAudience = process.env.JWT_AUDIENCE || "workspace";
13
+ const jwtKeyPath = process.env.VIBE80_JWT_KEY_PATH || path.join(dataDirectory, "jwt.key");
14
+ const jwtIssuer = process.env.VIBE80_JWT_ISSUER || "vibe80";
15
+ const jwtAudience = process.env.VIBE80_JWT_AUDIENCE || "workspace";
16
16
  const accessTokenTtlSeconds =
17
- Number(process.env.ACCESS_TOKEN_TTL_SECONDS) || 60 * 60;
17
+ Number(process.env.VIBE80_ACCESS_TOKEN_TTL_SECONDS) || 60 * 60;
18
18
 
19
19
  const loadJwtKey = () => {
20
- if (process.env.JWT_KEY) {
21
- return process.env.JWT_KEY;
20
+ if (process.env.VIBE80_JWT_KEY) {
21
+ return process.env.VIBE80_JWT_KEY;
22
22
  }
23
23
  if (fs.existsSync(jwtKeyPath)) {
24
24
  return fs.readFileSync(jwtKeyPath, "utf8").trim();
@@ -1,10 +1,10 @@
1
1
  import { createDebugId, formatDebugPayload } from "../helpers.js";
2
2
 
3
3
  const debugApiWsLog = /^(1|true|yes|on)$/i.test(
4
- process.env.DEBUG_API_WS_LOG || ""
4
+ process.env.VIBE80_DEBUG_API_WS_LOG || ""
5
5
  );
6
- const debugLogMaxBody = Number.isFinite(Number(process.env.DEBUG_API_WS_LOG_MAX_BODY))
7
- ? Number(process.env.DEBUG_API_WS_LOG_MAX_BODY)
6
+ const debugLogMaxBody = Number.isFinite(Number(process.env.VIBE80_DEBUG_API_WS_LOG_MAX_BODY))
7
+ ? Number(process.env.VIBE80_DEBUG_API_WS_LOG_MAX_BODY)
8
8
  : 2000;
9
9
 
10
10
  export { debugApiWsLog };
@@ -14,6 +14,38 @@ export const logDebug = (...args) => {
14
14
  console.log(...args);
15
15
  };
16
16
 
17
+ const SENSITIVE_KEYS = new Set([
18
+ "password",
19
+ "privatekey",
20
+ "token",
21
+ "refreshtoken",
22
+ "workspaceSecret",
23
+ "workspacesecret",
24
+ "httpPassword",
25
+ "httppassword",
26
+ "sshkey",
27
+ "auth",
28
+ ]);
29
+
30
+ const redactSensitive = (value) => {
31
+ if (Array.isArray(value)) {
32
+ return value.map((item) => redactSensitive(item));
33
+ }
34
+ if (!value || typeof value !== "object") {
35
+ return value;
36
+ }
37
+ const next = {};
38
+ Object.entries(value).forEach(([key, entry]) => {
39
+ const normalized = String(key || "").toLowerCase();
40
+ if (SENSITIVE_KEYS.has(key) || SENSITIVE_KEYS.has(normalized)) {
41
+ next[key] = "[REDACTED]";
42
+ return;
43
+ }
44
+ next[key] = redactSensitive(entry);
45
+ });
46
+ return next;
47
+ };
48
+
17
49
  export const attachWebSocketDebug = (socket, req, label) => {
18
50
  if (!debugApiWsLog) return;
19
51
  const connectionId = createDebugId();
@@ -61,7 +93,7 @@ export function debugMiddleware(req, res, next) {
61
93
  method: req.method,
62
94
  url: req.originalUrl,
63
95
  query: req.query,
64
- body: formatDebugPayload(req.body, debugLogMaxBody),
96
+ body: formatDebugPayload(redactSensitive(req.body), debugLogMaxBody),
65
97
  });
66
98
 
67
99
  let responseBody;
@@ -3,7 +3,7 @@ import os from "os";
3
3
  import path from "path";
4
4
 
5
5
  const isLoggingEnabled = () => {
6
- const value = process.env.ACTIVATE_PROVIDER_LOG;
6
+ const value = process.env.VIBE80_ACTIVATE_PROVIDER_LOG;
7
7
  if (!value) {
8
8
  return false;
9
9
  }
@@ -19,7 +19,7 @@ export const createProviderLogger = ({ provider, sessionId, worktreeId }) => {
19
19
  }
20
20
  const safeWorktreeId = worktreeId || "main";
21
21
  const baseDir =
22
- process.env.PROVIDER_LOG_DIRECTORY || path.join(os.homedir(), "logs");
22
+ process.env.VIBE80_PROVIDER_LOG_DIRECTORY || path.join(os.homedir(), "logs");
23
23
  try {
24
24
  fs.mkdirSync(baseDir, { recursive: true, mode: 0o700 });
25
25
  const filePath = path.join(