diff --git a/src/api/routes/agents.ts b/src/api/routes/agents.ts index 5efc372..20c956d 100644 --- a/src/api/routes/agents.ts +++ b/src/api/routes/agents.ts @@ -8,7 +8,7 @@ import { agents, tasks } from '../../db/schema' import { eq, and } from 'drizzle-orm' import { randomUUID } from 'crypto' import { authenticateRequest } from '../middleware/auth' -import { createAgentPod, deleteAgentPod } from '../../lib/k8s' +import { createAgentPod, deleteAgentPod, createAgentService, deleteAgentService } from '../../lib/k8s' /** * Handle all agent routes @@ -332,12 +332,13 @@ async function unregisterAgent(agentId: string, userId: string): Promise { await db.insert(agents).values(newAgent) - // Create K8s pod + // Create K8s pod and service try { - await createAgentPod(podName, userId) + await createAgentPod(podName, userId, agentId) + await createAgentService(podName, agentId) } catch (k8sError: any) { - // If pod creation fails, rollback DB entry + // If pod/service creation fails, rollback DB entry await db.delete(agents).where(eq(agents.id, agentId)) throw new Error(`Failed to create pod: ${k8sError.message}`) } diff --git a/src/index.ts b/src/index.ts index d45eb25..5c2da00 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,7 +9,7 @@ import { handleAuthRoutes, handleProjectRoutes, handleTaskRoutes, handleAgentRou import { authenticateRequest } from './api/middleware/auth' import { agents } from './db/schema' import { eq, and } from 'drizzle-orm' -import { initK8sClient, getPodIP } from './lib/k8s' +import { initK8sClient } from './lib/k8s' console.log('🚀 Starting AiWorker Backend...') console.log(`Bun version: ${Bun.version}`) @@ -110,24 +110,15 @@ const server = Bun.serve({ ) } - // Get pod IP - const podIP = await getPodIP(agent.podName) - if (!podIP) { - console.error(`Pod ${agent.podName} not found or has no IP`) - return Response.json( - { success: false, message: 'Agent pod not ready' }, - { status: 503 } - ) - } - - // Proxy to agent terminal (ttyd on port 7681) + // Proxy to agent terminal via service DNS + // Service name: {podName}-terminal.agents.svc.cluster.local:7681 const agentPath = url.pathname.replace(`/agent-terminal/${agentId}`, '') || '/' - const agentUrl = `http://${podIP}:7681${agentPath}${url.search}` + const serviceUrl = `http://${agent.podName}-terminal.agents.svc.cluster.local:7681${agentPath}${url.search}` - console.log(`🔄 Proxying terminal request to ${agentUrl}`) + console.log(`🔄 Proxying terminal request to ${serviceUrl}`) try { - const response = await fetch(agentUrl, { + const response = await fetch(serviceUrl, { method: req.method, headers: req.headers, body: req.body, diff --git a/src/lib/k8s.ts b/src/lib/k8s.ts index e25648d..15bc62c 100644 --- a/src/lib/k8s.ts +++ b/src/lib/k8s.ts @@ -95,6 +95,7 @@ export function createAgentPodSpec(podName: string, userId: string): k8s.V1Pod { labels: { app: 'claude-agent', userId: userId, + podName: podName, 'aiworker.io/agent': 'true', }, }, @@ -187,10 +188,73 @@ export function createAgentPodSpec(podName: string, userId: string): k8s.V1Pod { } } +/** + * Create service for agent pod (for terminal access) + */ +export async function createAgentService(podName: string, agentId: string): Promise { + const client = getK8sClient() + + const serviceSpec: k8s.V1Service = { + metadata: { + name: `${podName}-terminal`, + namespace: 'agents', + labels: { + app: 'claude-agent-terminal', + agentId: agentId, + } + }, + spec: { + selector: { + app: 'claude-agent', + podName: podName, + }, + ports: [{ + name: 'terminal', + port: 7681, + targetPort: 7681 as any, + protocol: 'TCP' + }], + type: 'ClusterIP' + } + } + + try { + await client.createNamespacedService({ + namespace: 'agents', + body: serviceSpec + }) + console.log(`✅ Service ${podName}-terminal created`) + } catch (error: any) { + console.error(`❌ Failed to create service:`, error.message) + throw error + } +} + +/** + * Delete agent service + */ +export async function deleteAgentService(podName: string): Promise { + const client = getK8sClient() + + try { + await client.deleteNamespacedService({ + name: `${podName}-terminal`, + namespace: 'agents' + }) + console.log(`✅ Service ${podName}-terminal deleted`) + } catch (error: any) { + if (error.statusCode === 404 || error.response?.statusCode === 404) { + console.log(`⚠️ Service ${podName}-terminal not found`) + return + } + console.error(`❌ Error deleting service:`, error.message) + } +} + /** * Create agent pod in Kubernetes */ -export async function createAgentPod(podName: string, userId: string): Promise { +export async function createAgentPod(podName: string, userId: string, agentId: string): Promise { const { client, options } = getK8sClientWithOptions() const podSpec = createAgentPodSpec(podName, userId)