V4/theia Frontend Integration Plan
Date: 2025-10-06 Goal: Wrap theia IDE with V4 frontend components (Header, Footer, layout) and integrate pre-theia work
🎯 Architecture Overview
Current State
V4 Frontend (src/frontend-original/):
- ✅ Complete React app with Chakra UI
- ✅ Header with auth, theme toggle, user menu
- ✅ Footer with links and branding
- ✅ 50+ documentation pages
- ✅ Login/Register pages
- ✅ Admin dashboard
- ✅ workspace management UI
theia IDE (src/browser/):
- ✅ Eclipse theia framework
- ✅ llm integration extension (partially complete)
- ✅ Monaco editor, xterm.js terminal
- ✅ File explorer (built-in)
Pre-theia Components (need to map):
- React prototype with 4 llm workflow modes
- File explorer UI
- llm chat panel
- Model selector
Target Architecture
┌─────────────────────────────────────────────────────────┐
│ V4 Header (React/Chakra) │
│ Logo | Nav | Theme Toggle | User Menu (Auth) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ Main Content Area (Flexbox) │
│ ┌─────────────────────┬─────────────────────────────┐ │
│ │ V4 Sidebar (React) │ theia IDE (iframe/embed) │ │
│ │ - Sessions │ - Monaco editor │ │
│ │ - Projects │ - File Explorer │ │
│ │ - Models │ - terminal │ │
│ │ - llm Chat Panel │ - Extensions │ │
│ └─────────────────────┴─────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ V4 Footer (React/Chakra) │
│ Links | Docs | Support | © Coditect │
└─────────────────────────────────────────────────────────┘
📦 Component Mapping
V4 Components to Reuse
| Component | Location | Purpose in V5 |
|---|---|---|
| header.tsx | frontend-original/src/components/ | Top nav with auth, theme, user menu |
| footer.tsx | frontend-original/src/components/ | Bottom links, branding |
| layout.tsx | frontend-original/src/components/ | Wrapper layout for all pages |
| login-page.tsx | frontend-original/src/pages/ | User login (integrate with auth-service) |
| register-page.tsx | frontend-original/src/pages/ | User registration |
| profile-page.tsx | frontend-original/src/pages/ | User profile management |
| settings-page.tsx | frontend-original/src/pages/ | IDE/user settings |
| documentation-page.tsx | frontend-original/src/pages/ | Docs landing page |
| workspace-page.tsx | frontend-original/src/pages/ | workspace selector/manager |
| coditect-workspace.tsx | frontend-original/src/pages/ | Main workspace container |
Pre-theia Components to Integrate
| Component | Current Location | Integration Strategy |
|---|---|---|
| llm Chat Panel | Phase 2 React prototype | → theia sidebar widget |
| Workflow Mode Selector | Phase 2 React prototype | → theia toolbar contribution |
| Model Selector | Phase 2 React prototype | → theia statusbar item |
| File Explorer (custom) | Phase 2 React prototype | ❌ Use theia's built-in explorer instead |
| Monaco editor (custom) | Phase 2 React prototype | ❌ Use theia's built-in editor instead |
theia Components (Built-in)
| Component | Source | Notes |
|---|---|---|
| File Explorer | @theia/navigator | Use as-is, already better than custom |
| Monaco editor | @theia/monaco | Use as-is, feature-complete |
| terminal | @theia/terminal | Use as-is, xterm.js integration |
| Search | @theia/search-in-workspace | Built-in full-text search |
| SCM | @theia/scm | Git integration |
🏗️ Implementation Strategy
Phase 1: Wrapper Foundation (Week 1)
Goal: Embed theia inside V4 layout
-
Create theia Embed Component:
// src/components/theia-embed.tsx
import React, { useEffect, useRef } from 'react';
import { Box } from '@chakra-ui/react';
interface theiaEmbedProps {
sessionId: string;
userId: string;
}
export const theiaEmbed: React.FC<theiaEmbedProps> = ({ sessionId, userId }) => {
const iframeRef = useRef<HTMLIFrameElement>(null);
useEffect(() => {
// Initialize theia iframe communication
if (iframeRef.current) {
const theiaUrl = `${import.meta.env.VITE_THEIA_URL}?session=${sessionId}&user=${userId}`;
iframeRef.current.src = theiaUrl;
}
}, [sessionId, userId]);
return (
<Box
as="iframe"
ref={iframeRef}
w="100%"
h="100%"
border="none"
bg="transparent"
title="theia IDE"
/>
);
}; -
Update V4 layout for theia:
// src/components/layout.tsx (modified from V4)
import React from 'react';
import { Box, Flex } from '@chakra-ui/react';
import { Header } from './Header';
import { Footer } from './Footer';
import { theiaEmbed } from './theiaEmbed';
import { SidePanel } from './SidePanel';
import { useAuthStore } from '../stores/authStore';
interface layoutProps {
children?: React.ReactNode;
showIDE?: boolean;
}
export const layout: React.FC<layoutProps> = ({ children, showIDE = false }) => {
const { user, sessionId } = useAuthStore();
return (
<Flex direction="column" minH="100vh">
<Header />
<Flex flex={1} overflow="hidden">
{showIDE ? (
<>
<SidePanel user={user} />
<Box flex={1} position="relative">
<theiaEmbed
sessionId={sessionId}
userId={user?.id}
/>
</Box>
</>
) : (
<Box flex={1} overflow="auto">
{children}
</Box>
)}
</Flex>
<Footer />
</Flex>
);
}; -
Create SidePanel Component:
// src/components/side-panel.tsx
import React from 'react';
import { Box, VStack, Button, Text, useColorModeValue } from '@chakra-ui/react';
import { FiFolder, FiMessageSquare, FiCpu, FiBox } from 'react-icons/fi';
export const SidePanel: React.FC<{ user: any }> = ({ user }) => {
const bgColor = useColorModeValue('gray.50', 'gray.900');
const borderColor = useColorModeValue('gray.200', 'gray.700');
return (
<Box
w="250px"
bg={bgColor}
borderRight="1px"
borderColor={borderColor}
p={4}
overflow="auto"
>
<VStack spacing={4} align="stretch">
<Box>
<Text fontSize="xs" fontWeight="bold" mb={2} textTransform="uppercase">
workspace
</Text>
<Button leftIcon={<FiFolder />} variant="ghost" size="sm" justifyContent="flex-start">
My Projects
</Button>
<Button leftIcon={<FiBox />} variant="ghost" size="sm" justifyContent="flex-start">
Sessions
</Button>
</Box>
<Box>
<Text fontSize="xs" fontWeight="bold" mb={2} textTransform="uppercase">
AI Tools
</Text>
<Button leftIcon={<FiMessageSquare />} variant="ghost" size="sm" justifyContent="flex-start">
llm Chat
</Button>
<Button leftIcon={<FiCpu />} variant="ghost" size="sm" justifyContent="flex-start">
Model Selector
</Button>
</Box>
</VStack>
</Box>
);
}; -
Update App Routes:
// src/app.tsx (modified from V4)
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import { ChakraProvider } from '@chakra-ui/react';
import { layout } from './components/layout';
import { LoginPage } from './pages/LoginPage';
import { RegisterPage } from './pages/RegisterPage';
import { workspacePage } from './pages/workspacePage';
import { DocumentationPage } from './pages/DocumentationPage';
import { useAuthStore } from './stores/authStore';
import theme from './theme';
function App() {
const { isAuthenticated } = useAuthStore();
return (
<ChakraProvider theme={theme}>
<BrowserRouter>
<Routes>
{/* Public routes */}
<Route path="/login" element={<LoginPage />} />
<Route path="/register" element={<RegisterPage />} />
<Route path="/docs/*" element={<layout><DocumentationPage /></layout>} />
{/* Protected routes */}
<Route
path="/workspace"
element={
isAuthenticated ? (
<layout showIDE={true} />
) : (
<Navigate to="/login" />
)
}
/>
<Route
path="/projects"
element={
isAuthenticated ? (
<layout><workspacePage /></layout>
) : (
<Navigate to="/login" />
)
}
/>
<Route path="/" element={<Navigate to="/workspace" />} />
</Routes>
</BrowserRouter>
</ChakraProvider>
);
}
Deliverables:
- theiaEmbed component created
- V4 layout modified for theia
- SidePanel component created
- App routes updated
- theia loads inside V4 wrapper
Phase 2: Theme Unification (Week 1-2)
Goal: Make theia match V4 Chakra UI theme
-
Extract V4 Theme:
// src/theme.ts (from V4)
import { extendTheme } from '@chakra-ui/react';
const theme = extendTheme({
config: {
initialColorMode: 'dark',
useSystemColorMode: false,
},
colors: {
brand: {
50: '#e6f7ff',
100: '#bae7ff',
200: '#91d5ff',
300: '#69c0ff',
400: '#40a9ff',
500: '#1890ff', // Primary
600: '#096dd9',
700: '#0050b3',
800: '#003a8c',
900: '#002766',
},
},
styles: {
global: (props: any) => ({
body: {
bg: props.colorMode === 'dark' ? 'gray.900' : 'white',
color: props.colorMode === 'dark' ? 'white' : 'gray.800',
},
}),
},
});
export default theme; -
Create theia Theme CSS:
/* public/theia-theme.css */
:root {
/* Match Chakra UI colors */
--theia-ui-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
--theia-brand-primary: #1890ff;
--theia-brand-secondary: #096dd9;
}
/* Dark mode (default) */
[data-theme='dark'] {
--theia-editor-background: #1a202c; /* gray.900 */
--theia-editor-foreground: #ffffff;
--theia-sideBar-background: #171923; /* gray.950 */
--theia-statusBar-background: #0f1419;
--theia-activityBar-background: #0f1419;
--theia-menu-background: #2d3748; /* gray.700 */
--theia-input-background: #2d3748;
}
/* Light mode */
[data-theme='light'] {
--theia-editor-background: #ffffff;
--theia-editor-foreground: #1a202c; /* gray.900 */
--theia-sideBar-background: #f7fafc; /* gray.50 */
--theia-statusBar-background: #edf2f7;
--theia-activityBar-background: #edf2f7;
--theia-menu-background: #ffffff;
--theia-input-background: #ffffff;
} -
Sync Theme Between V4 and theia:
// src/hooks/use-theia-theme.ts
import { useEffect } from 'react';
import { useColorMode } from '@chakra-ui/react';
export const usetheiaTheme = () => {
const { colorMode } = useColorMode();
useEffect(() => {
// Send theme to theia iframe
const theiaIframe = document.querySelector('iframe[title="theia IDE"]') as HTMLIFrameElement;
if (theiaIframe?.contentWindow) {
theiaIframe.contentWindow.postMessage(
{ type: 'SET_THEME', theme: colorMode },
'*'
);
}
// Also set CSS data attribute for theia CSS
document.documentElement.setAttribute('data-theme', colorMode);
}, [colorMode]);
}; -
theia Theme Receiver (in theia extension):
// src/browser/theme-sync-contribution.ts
import { injectable, postConstruct } from '@theia/core/shared/inversify';
import { CommandContribution, CommandRegistry } from '@theia/core';
import { ColorTheme } from '@theia/core/lib/common/theme';
@injectable()
export class ThemeSyncContribution implements CommandContribution {
@postConstruct()
protected init(): void {
// Listen for theme changes from parent window
window.addEventListener('message', (event) => {
if (event.data.type === 'SET_THEME') {
const theme = event.data.theme === 'dark' ? 'dark' : 'light';
this.settheiaTheme(theme);
}
});
}
private settheiaTheme(theme: string): void {
// Apply theia theme
document.body.classList.remove('theia-light', 'theia-dark');
document.body.classList.add(`theia-${theme}`);
}
registerCommands(commands: CommandRegistry): void {
// No commands needed
}
}
Deliverables:
- V4 Chakra theme extracted
- theia CSS theme created
- Theme sync hook implemented
- theia visually matches V4 wrapper
Phase 3: Component Integration (Week 2)
Goal: Integrate pre-theia llm components
-
Port llm Chat Panel to theia Widget:
// src/browser/llm-chat-panel/llm-chat-panel-widget.tsx
import * as React from 'react';
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
import { injectable, inject } from '@theia/core/shared/inversify';
import { llmService } from '../../services/llm-service';
import { ChakraProvider, VStack, Input, Button, Box, Text } from '@chakra-ui/react';
import theme from '../../theme'; // Reuse V4 theme
@injectable()
export class llmChatPanelWidget extends ReactWidget {
static readonly ID = 'llm-chat-panel';
static readonly LABEL = 'llm Chat';
@inject(llmService) llmService!: llmService;
constructor() {
super();
this.id = llmChatPanelWidget.ID;
this.title.label = llmChatPanelWidget.LABEL;
this.title.closable = true;
this.title.iconClass = 'fa fa-comments';
}
protected render(): React.ReactNode {
return (
<ChakraProvider theme={theme}>
<VStack spacing={4} p={4} h="100%" align="stretch">
<Box flex={1} overflow="auto">
{/* Chat messages */}
<Text>Chat history...</Text>
</Box>
<Input placeholder="Type a message..." />
<Button colorScheme="brand">Send</Button>
</VStack>
</ChakraProvider>
);
}
} -
Port Model Selector to Statusbar:
// src/browser/model-selector/model-selector-contribution.ts
import { injectable, inject } from '@theia/core/shared/inversify';
import { StatusBar, StatusBarAlignment } from '@theia/core/lib/browser/status-bar/status-bar';
import { FrontendApplicationContribution } from '@theia/core/lib/browser';
import { llmService } from '../../services/llm-service';
@injectable()
export class ModelSelectorContribution implements FrontendApplicationContribution {
@inject(StatusBar) statusBar!: StatusBar;
@inject(llmService) llmService!: llmService;
async onStart(): Promise<void> {
const models = await this.llmService.listLMStudioModels();
const selectedModel = models[0]?.id || 'No models';
this.statusBar.setElement('model-selector', {
text: `🤖 ${selectedModel}`,
alignment: StatusBarAlignment.RIGHT,
priority: 100,
command: 'model-selector.select',
tooltip: 'Click to select llm model'
});
}
} -
Port Workflow Mode Selector to Toolbar:
// src/browser/workflow-mode/workflow-mode-contribution.ts
import { injectable } from '@theia/core/shared/inversify';
import { CommandContribution, CommandRegistry, MenuContribution, MenuModelRegistry } from '@theia/core';
import { CommonMenus } from '@theia/core/lib/browser';
export const WorkflowModes = {
SINGLE: 'single',
PARALLEL: 'parallel',
SEQUENTIAL: 'sequential',
CONSENSUS: 'consensus'
};
@injectable()
export class WorkflowModeContribution implements CommandContribution, MenuContribution {
registerCommands(commands: CommandRegistry): void {
commands.registerCommand({
id: 'workflow-mode.single',
label: 'Single Mode'
}, {
execute: () => this.setWorkflowMode('single')
});
commands.registerCommand({
id: 'workflow-mode.parallel',
label: 'Parallel Mode'
}, {
execute: () => this.setWorkflowMode('parallel')
});
// ... other modes
}
registerMenus(menus: MenuModelRegistry): void {
menus.registerMenuAction(CommonMenus.VIEW, {
commandId: 'workflow-mode.single',
label: 'Workflow: Single',
order: '0'
});
// ... other modes
}
private setWorkflowMode(mode: string): void {
console.log('Workflow mode set to:', mode);
// Trigger workflow change in llm service
}
}
Deliverables:
- llm chat panel as theia widget
- Model selector in theia statusbar
- Workflow mode in theia toolbar
- All components use Chakra theme
Phase 4: Authentication Integration (Week 2-3)
Goal: Connect V4 auth UI to theia backend
-
Update V4 Auth Store (use FDBService):
// src/stores/auth-store.ts (modified from V4)
import create from 'zustand';
import { persist } from 'zustand/middleware';
interface User {
id: string;
tenantId: string;
email: string;
name: string;
avatar?: string;
}
interface AuthState {
user: User | null;
token: string | null;
sessionId: string | null;
isAuthenticated: boolean;
login: (email: string, password: string) => Promise<void>;
register: (email: string, password: string, name: string) => Promise<void>;
logout: () => void;
}
export const useAuthStore = create<AuthState>()(
persist(
(set) => ({
user: null,
token: null,
sessionId: null,
isAuthenticated: false,
login: async (email, password) => {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
const data = await response.json();
set({
user: data.user,
token: data.token,
sessionId: data.sessionId,
isAuthenticated: true
});
},
register: async (email, password, name) => {
const response = await fetch('/api/auth/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password, name })
});
const data = await response.json();
set({
user: data.user,
token: data.token,
sessionId: data.sessionId,
isAuthenticated: true
});
},
logout: () => {
set({
user: null,
token: null,
sessionId: null,
isAuthenticated: false
});
}
}),
{ name: 'auth-storage' }
)
); -
Create Auth Backend Endpoint (Node.js/Express):
// server/auth.ts
import express from 'express';
import { FDBService } from './services/fdb-service';
import { PodProvisioningService } from './services/pod-provisioning-service';
import * as argon2 from 'argon2';
import * as jwt from 'jsonwebtoken';
const router = express.Router();
const fdb = new FDBService();
const podService = new PodProvisioningService();
router.post('/register', async (req, res) => {
const { email, password, name } = req.body;
const userId = `usr-${Date.now()}`;
const tenantId = `tenant-${Date.now()}`;
const passwordHash = await argon2.hash(password);
const user = {
userId,
tenantId,
email,
name,
passwordHash,
createdAt: new Date().toISOString()
};
// Store user in FDB
await fdb.set(`${tenantId}/user/${userId}`, user);
// Auto-provision pod
const pod = await podService.createUserPod(userId, tenantId);
// Create session
const sessionId = `ses-${Date.now()}`;
await fdb.set(`${tenantId}/session/${sessionId}`, {
sessionId,
userId,
tenantId,
podNamespace: pod.namespace,
createdAt: new Date().toISOString()
});
// Generate JWT
const token = jwt.sign({ userId, tenantId }, process.env.JWT_SECRET || 'dev-secret', {
expiresIn: '7d'
});
res.json({
user: { id: userId, tenantId, email, name },
token,
sessionId
});
});
router.post('/login', async (req, res) => {
const { email, password } = req.body;
// Find user by email (scan all tenants)
const users = await fdb.scan('*/user/*');
const user = users.find(u => u.email === email);
if (!user) {
return res.status(401).json({ error: 'User not found' });
}
const valid = await argon2.verify(user.passwordHash, password);
if (!valid) {
return res.status(401).json({ error: 'Invalid password' });
}
// Create session
const sessionId = `ses-${Date.now()}`;
const pod = await fdb.get(`${user.tenantId}/pod/${user.userId}`);
await fdb.set(`${user.tenantId}/session/${sessionId}`, {
sessionId,
userId: user.userId,
tenantId: user.tenantId,
podNamespace: pod?.namespace,
createdAt: new Date().toISOString()
});
// Generate JWT
const token = jwt.sign(
{ userId: user.userId, tenantId: user.tenantId },
process.env.JWT_SECRET || 'dev-secret',
{ expiresIn: '7d' }
);
res.json({
user: { id: user.userId, tenantId: user.tenantId, email: user.email, name: user.name },
token,
sessionId
});
});
export default router;
Deliverables:
- Auth store updated for FDB backend
- Auth backend endpoints created
- Login/Register pages working
- Pod auto-provisioning on register
Phase 5: Docker Persistence (Week 3)
Goal: Persist gcloud SDK and other tools in Docker image
-
Update Debian Dockerfile:
# Dockerfile (update existing)
FROM debian:13-slim
# Install base dependencies
RUN apt-get update && apt-get install -y \
curl wget git vim build-essential \
python3 python3-pip nodejs npm \
&& rm -rf /var/lib/apt/lists/*
# Install Google Cloud SDK
RUN curl https://sdk.cloud.google.com | bash -s -- \
--disable-prompts \
--install-dir=/opt/google-cloud-sdk
# Add gcloud to PATH
ENV PATH="/opt/google-cloud-sdk/google-cloud-sdk/bin:${PATH}"
# Install kubectl
RUN gcloud components install kubectl --quiet
# Install Rust (for CODI2 builds)
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"
# Install FoundationDB client
RUN wget https://github.com/apple/foundationdb/releases/download/7.1.61/foundationdb-clients_7.1.61-1_amd64.deb && \
dpkg -i foundationdb-clients_7.1.61-1_amd64.deb && \
rm foundationdb-clients_7.1.61-1_amd64.deb
# Copy project files
WORKDIR /workspace
COPY . .
# Install Node.js dependencies
RUN npm install
# Build theia
RUN npm run theia:build
# Expose ports
EXPOSE 3000 5173 8080
# Default command
CMD ["npm", "run", "theia:start"] -
Update docker-compose.yml:
version: '3.8'
services:
claude-code-dev:
build:
context: .
dockerfile: Dockerfile
container_name: claude-code-dev
volumes:
# Persist workspace
- ./:/workspace
# Persist gcloud config
- gcloud-config:/root/.config/gcloud
# Persist npm cache
- npm-cache:/root/.npm
ports:
- "3000:3000" # theia
- "5173:5173" # Vite dev server
- "8080:8080" # Backend API
environment:
- FDB_CLUSTER_FILE=/etc/foundationdb/fdb.cluster
- FDB_CLUSTER_STRING=coditect:production@10.128.0.8:4500
- GCP_PROJECT=serene-voltage-464305-n2
networks:
- coditect-network
volumes:
gcloud-config:
npm-cache:
networks:
coditect-network:
driver: bridge -
Rebuild and Test:
# Rebuild Docker image with persistence
docker-compose down
docker-compose build --no-cache
docker-compose up -d
# Verify gcloud persists
docker exec -it claude-code-dev gcloud --version
docker exec -it claude-code-dev gcloud config list
# Verify kubectl
docker exec -it claude-code-dev kubectl version --client
Deliverables:
- Dockerfile updated with gcloud SDK
- docker-compose.yml updated with volumes
- Container rebuilt with persistent tools
- gcloud/kubectl working after rebuild
📋 Complete Task Checklist
Phase 1: Wrapper Foundation
- Create theiaEmbed component
- Modify V4 layout for theia
- Create SidePanel component
- Update App routes
- Test theia loading in V4 wrapper
Phase 2: Theme Unification
- Extract V4 Chakra theme
- Create theia CSS theme
- Implement theme sync hook
- Create theia theme receiver
- Verify visual consistency
Phase 3: Component Integration
- Port llm chat panel to theia widget
- Port model selector to statusbar
- Port workflow mode to toolbar
- Apply Chakra theme to all widgets
- Test component interactions
Phase 4: Authentication Integration
- Update auth store for FDB
- Create auth backend endpoints
- Wire up Login/Register pages
- Test pod auto-provisioning
- Verify session persistence
Phase 5: Docker Persistence
- Update Dockerfile with gcloud SDK
- Update docker-compose.yml volumes
- Rebuild Docker image
- Test gcloud persistence
- Test kubectl persistence
🎯 Success Criteria
Integration Complete When:
- ✅ theia loads inside V4 layout (Header + Footer)
- ✅ Theme is consistent across V4 wrapper and theia
- ✅ llm chat panel, model selector, workflow mode integrated
- ✅ Login/Register pages working with FDB backend
- ✅ User pod auto-provisioned on registration
- ✅ gcloud SDK persists across Docker rebuilds
- ✅ All 50+ V4 docs pages accessible
- ✅ Mobile-responsive (V4 hamburger menu works)
🔗 Key Files
V4 Components (Reuse)
src/frontend-original/src/components/header.tsxsrc/frontend-original/src/components/footer.tsxsrc/frontend-original/src/components/layout.tsxsrc/frontend-original/src/pages/login-page.tsxsrc/frontend-original/src/pages/register-page.tsx
New Integration Components (Create)
src/components/theia-embed.tsxsrc/components/side-panel.tsxsrc/hooks/use-theia-theme.tssrc/browser/theme-sync-contribution.tsserver/auth.ts
Docker (Update)
Dockerfile(add gcloud SDK)docker-compose.yml(add volumes)
Timeline: 3 weeks to complete V4/theia integration with Docker persistence.