diff --git a/shared/modelConstants.js b/shared/modelConstants.js index 7d4347f..335d1ad 100644 --- a/shared/modelConstants.js +++ b/shared/modelConstants.js @@ -63,3 +63,10 @@ export const CODEX_MODELS = { DEFAULT: 'gpt-5.2' }; + + +/** + * Environment Flag: Is Platform + * Indicates if the app is running in Platform mode (hosted) or OSS mode (self-hosted) + */ +export const IS_PLATFORM = import.meta.env.VITE_IS_PLATFORM === 'true'; \ No newline at end of file diff --git a/src/components/LoginModal.jsx b/src/components/LoginModal.jsx index 6861313..963efa4 100644 --- a/src/components/LoginModal.jsx +++ b/src/components/LoginModal.jsx @@ -1,5 +1,6 @@ import { X } from 'lucide-react'; import StandaloneShell from './StandaloneShell'; +import { IS_PLATFORM } from '../../shared/modelConstants'; /** * Reusable login modal component for Claude, Cursor, and Codex CLI authentication @@ -27,15 +28,13 @@ function LoginModal({ const getCommand = () => { if (customCommand) return customCommand; - const isPlatform = import.meta.env.VITE_IS_PLATFORM === 'true'; - switch (provider) { case 'claude': return isAuthenticated ? 'claude setup-token --dangerously-skip-permissions' : 'claude /exit --dangerously-skip-permissions'; case 'cursor': return 'cursor-agent login'; case 'codex': - return isPlatform ? 'codex login --device-auth' : 'codex login'; + return IS_PLATFORM ? 'codex login --device-auth' : 'codex login'; default: return isAuthenticated ? 'claude setup-token --dangerously-skip-permissions' : 'claude /exit --dangerously-skip-permissions'; } diff --git a/src/components/Onboarding.jsx b/src/components/Onboarding.jsx index 29bf762..02a0050 100644 --- a/src/components/Onboarding.jsx +++ b/src/components/Onboarding.jsx @@ -6,6 +6,7 @@ import CodexLogo from './CodexLogo'; import LoginModal from './LoginModal'; import { authenticatedFetch } from '../utils/api'; import { useAuth } from '../contexts/AuthContext'; +import { IS_PLATFORM } from '../../shared/modelConstants'; const Onboarding = ({ onComplete }) => { const [currentStep, setCurrentStep] = useState(0); @@ -15,8 +16,7 @@ const Onboarding = ({ onComplete }) => { const [error, setError] = useState(''); const [activeLoginProvider, setActiveLoginProvider] = useState(null); - const isPlatform = import.meta.env.VITE_IS_PLATFORM === 'true'; - const [selectedProject] = useState({ name: 'default', fullPath: isPlatform ? '/workspace' : '' }); + const [selectedProject] = useState({ name: 'default', fullPath: IS_PLATFORM ? '/workspace' : '' }); const [claudeAuthStatus, setClaudeAuthStatus] = useState({ authenticated: false, diff --git a/src/components/ProtectedRoute.jsx b/src/components/ProtectedRoute.jsx index 56343ae..1a09d26 100644 --- a/src/components/ProtectedRoute.jsx +++ b/src/components/ProtectedRoute.jsx @@ -4,6 +4,7 @@ import SetupForm from './SetupForm'; import LoginForm from './LoginForm'; import Onboarding from './Onboarding'; import { MessageSquare } from 'lucide-react'; +import { IS_PLATFORM } from '../../shared/modelConstants'; const LoadingScreen = () => (
@@ -27,7 +28,7 @@ const LoadingScreen = () => ( const ProtectedRoute = ({ children }) => { const { user, isLoading, needsSetup, hasCompletedOnboarding, refreshOnboardingStatus } = useAuth(); - if (import.meta.env.VITE_IS_PLATFORM === 'true') { + if (IS_PLATFORM) { if (isLoading) { return ; } diff --git a/src/components/Shell.jsx b/src/components/Shell.jsx index 59810ad..5a6d72a 100644 --- a/src/components/Shell.jsx +++ b/src/components/Shell.jsx @@ -5,6 +5,7 @@ import { WebglAddon } from '@xterm/addon-webgl'; import { WebLinksAddon } from '@xterm/addon-web-links'; import '@xterm/xterm/css/xterm.css'; import { useTranslation } from 'react-i18next'; +import { IS_PLATFORM } from '../../shared/modelConstants'; const xtermStyles = ` .xterm .xterm-screen { @@ -55,10 +56,9 @@ function Shell({ selectedProject, selectedSession, initialCommand, isPlainShell if (isConnecting || isConnected) return; try { - const isPlatform = import.meta.env.VITE_IS_PLATFORM === 'true'; let wsUrl; - if (isPlatform) { + if (IS_PLATFORM) { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; wsUrl = `${protocol}//${window.location.host}/shell`; } else { diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index ef74465..a8d1299 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -16,6 +16,7 @@ import ProjectCreationWizard from './ProjectCreationWizard'; import { api } from '../utils/api'; import { useTaskMaster } from '../contexts/TaskMasterContext'; import { useTasksSettings } from '../contexts/TasksSettingsContext'; +import { IS_PLATFORM } from '../../shared/modelConstants'; // Move formatTimeAgo outside component to avoid recreation on every render const formatTimeAgo = (dateString, currentTime, t) => { @@ -622,7 +623,7 @@ function Sidebar({
{/* Desktop Header */}
- {import.meta.env.VITE_IS_PLATFORM === 'true' ? ( + {IS_PLATFORM ? (
- {import.meta.env.VITE_IS_PLATFORM === 'true' ? ( + {IS_PLATFORM ? ( { const [error, setError] = useState(null); useEffect(() => { - if (import.meta.env.VITE_IS_PLATFORM === 'true') { + if (IS_PLATFORM) { setUser({ username: 'platform-user' }); setNeedsSetup(false); checkOnboardingStatus(); diff --git a/src/contexts/WebSocketContext.tsx b/src/contexts/WebSocketContext.tsx index 9905d62..5e48c7e 100644 --- a/src/contexts/WebSocketContext.tsx +++ b/src/contexts/WebSocketContext.tsx @@ -1,4 +1,6 @@ import { createContext, useContext, useEffect, useRef, useState } from 'react'; +import { useAuth } from './AuthContext'; +import { IS_PLATFORM } from '../../shared/modelConstants'; type WebSocketContextType = { ws: WebSocket | null; @@ -18,9 +20,8 @@ export const useWebSocket = () => { }; const buildWebSocketUrl = (token: string | null) => { - const isPlatform = import.meta.env.VITE_IS_PLATFORM === 'true'; const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; - if (isPlatform) return `${protocol}//${window.location.host}/ws`; // Platform mode: Use same domain as the page (goes through proxy) + if (IS_PLATFORM) return `${protocol}//${window.location.host}/ws`; // Platform mode: Use same domain as the page (goes through proxy) if (!token) return null; return `${protocol}//${window.location.host}/ws?token=${encodeURIComponent(token)}`; // OSS mode: Use same host:port that served the page }; @@ -31,6 +32,7 @@ const useWebSocketProviderState = (): WebSocketContextType => { const [messages, setMessages] = useState([]); const [isConnected, setIsConnected] = useState(false); const reconnectTimeoutRef = useRef(null); + const { token } = useAuth(); useEffect(() => { connect(); @@ -49,10 +51,8 @@ const useWebSocketProviderState = (): WebSocketContextType => { const connect = () => { if (unmountedRef.current) return; // Prevent connection if unmounted try { - const isPlatform = import.meta.env.VITE_IS_PLATFORM === 'true'; - // Construct WebSocket URL - const wsUrl = buildWebSocketUrl(isPlatform ? null : localStorage.getItem('authToken')); + const wsUrl = buildWebSocketUrl(token); if (!wsUrl) return console.warn('No authentication token found for WebSocket connection');