mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-03-17 22:13:08 +00:00
- Introduced a new red theme with custom color variables for a bold aesthetic. - Updated the theme management to include the new red theme option. - Added a BoardBackgroundModal component for managing board background settings, including image uploads and opacity controls. - Enhanced KanbanCard and KanbanColumn components to support new background settings such as opacity and border visibility. - Updated API client to handle saving and deleting board backgrounds. - Refactored theme application logic to accommodate the new preview theme functionality.
176 lines
7.0 KiB
TypeScript
176 lines
7.0 KiB
TypeScript
/**
|
|
* Automaker Backend Server
|
|
*
|
|
* Provides HTTP/WebSocket API for both web and Electron modes.
|
|
* In Electron mode, this server runs locally.
|
|
* In web mode, this server runs on a remote host.
|
|
*/
|
|
|
|
import express from "express";
|
|
import cors from "cors";
|
|
import { WebSocketServer, WebSocket } from "ws";
|
|
import { createServer } from "http";
|
|
import dotenv from "dotenv";
|
|
|
|
import { createEventEmitter, type EventEmitter } from "./lib/events.js";
|
|
import { initAllowedPaths } from "./lib/security.js";
|
|
import { authMiddleware, getAuthStatus } from "./lib/auth.js";
|
|
import { createFsRoutes } from "./routes/fs.js";
|
|
import { createHealthRoutes } from "./routes/health.js";
|
|
import { createAgentRoutes } from "./routes/agent.js";
|
|
import { createSessionsRoutes } from "./routes/sessions.js";
|
|
import { createFeaturesRoutes } from "./routes/features.js";
|
|
import { createAutoModeRoutes } from "./routes/auto-mode.js";
|
|
import { createWorktreeRoutes } from "./routes/worktree.js";
|
|
import { createGitRoutes } from "./routes/git.js";
|
|
import { createSetupRoutes } from "./routes/setup.js";
|
|
import { createSuggestionsRoutes } from "./routes/suggestions.js";
|
|
import { createModelsRoutes } from "./routes/models.js";
|
|
import { createSpecRegenerationRoutes } from "./routes/spec-regeneration.js";
|
|
import { createRunningAgentsRoutes } from "./routes/running-agents.js";
|
|
import { createWorkspaceRoutes } from "./routes/workspace.js";
|
|
import { createTemplatesRoutes } from "./routes/templates.js";
|
|
import { AgentService } from "./services/agent-service.js";
|
|
import { FeatureLoader } from "./services/feature-loader.js";
|
|
import { AutoModeService } from "./services/auto-mode-service.js";
|
|
|
|
// Load environment variables
|
|
dotenv.config();
|
|
|
|
const PORT = parseInt(process.env.PORT || "3008", 10);
|
|
const DATA_DIR = process.env.DATA_DIR || "./data";
|
|
|
|
// Check for required environment variables
|
|
// Claude Agent SDK supports EITHER OAuth token (subscription) OR API key (pay-per-use)
|
|
const hasAnthropicKey = !!process.env.ANTHROPIC_API_KEY;
|
|
const hasOAuthToken = !!process.env.CLAUDE_CODE_OAUTH_TOKEN;
|
|
|
|
if (!hasAnthropicKey && !hasOAuthToken) {
|
|
console.warn(`
|
|
╔═══════════════════════════════════════════════════════════════════════╗
|
|
║ ⚠️ WARNING: No Claude authentication configured ║
|
|
║ ║
|
|
║ The Claude Agent SDK requires authentication to function. ║
|
|
║ ║
|
|
║ Option 1 - Subscription (OAuth Token): ║
|
|
║ export CLAUDE_CODE_OAUTH_TOKEN="your-oauth-token" ║
|
|
║ ║
|
|
║ Option 2 - Pay-per-use (API Key): ║
|
|
║ export ANTHROPIC_API_KEY="sk-ant-..." ║
|
|
║ ║
|
|
║ Or use the setup wizard in Settings to configure authentication. ║
|
|
╚═══════════════════════════════════════════════════════════════════════╝
|
|
`);
|
|
} else if (hasOAuthToken) {
|
|
console.log("[Server] ✓ CLAUDE_CODE_OAUTH_TOKEN detected (subscription auth)");
|
|
} else {
|
|
console.log("[Server] ✓ ANTHROPIC_API_KEY detected (API key auth)");
|
|
}
|
|
|
|
// Initialize security
|
|
initAllowedPaths();
|
|
|
|
// Create Express app
|
|
const app = express();
|
|
|
|
// Middleware
|
|
app.use(
|
|
cors({
|
|
origin: process.env.CORS_ORIGIN || "*",
|
|
credentials: true,
|
|
})
|
|
);
|
|
app.use(express.json({ limit: "50mb" }));
|
|
|
|
// Create shared event emitter for streaming
|
|
const events: EventEmitter = createEventEmitter();
|
|
|
|
// Create services
|
|
const agentService = new AgentService(DATA_DIR, events);
|
|
const featureLoader = new FeatureLoader();
|
|
const autoModeService = new AutoModeService(events);
|
|
|
|
// Initialize services
|
|
(async () => {
|
|
await agentService.initialize();
|
|
console.log("[Server] Agent service initialized");
|
|
})();
|
|
|
|
// Mount API routes - health is unauthenticated for monitoring
|
|
app.use("/api/health", createHealthRoutes());
|
|
|
|
// Apply authentication to all other routes
|
|
app.use("/api", authMiddleware);
|
|
|
|
app.use("/api/fs", createFsRoutes(events));
|
|
app.use("/api/agent", createAgentRoutes(agentService, events));
|
|
app.use("/api/sessions", createSessionsRoutes(agentService));
|
|
app.use("/api/features", createFeaturesRoutes(featureLoader));
|
|
app.use("/api/auto-mode", createAutoModeRoutes(autoModeService));
|
|
app.use("/api/worktree", createWorktreeRoutes());
|
|
app.use("/api/git", createGitRoutes());
|
|
app.use("/api/setup", createSetupRoutes());
|
|
app.use("/api/suggestions", createSuggestionsRoutes(events));
|
|
app.use("/api/models", createModelsRoutes());
|
|
app.use("/api/spec-regeneration", createSpecRegenerationRoutes(events));
|
|
app.use("/api/running-agents", createRunningAgentsRoutes(autoModeService));
|
|
app.use("/api/workspace", createWorkspaceRoutes());
|
|
app.use("/api/templates", createTemplatesRoutes());
|
|
|
|
// Create HTTP server
|
|
const server = createServer(app);
|
|
|
|
// WebSocket server for streaming events
|
|
const wss = new WebSocketServer({ server, path: "/api/events" });
|
|
|
|
wss.on("connection", (ws: WebSocket) => {
|
|
console.log("[WebSocket] Client connected");
|
|
|
|
// Subscribe to all events and forward to this client
|
|
const unsubscribe = events.subscribe((type, payload) => {
|
|
if (ws.readyState === WebSocket.OPEN) {
|
|
ws.send(JSON.stringify({ type, payload }));
|
|
}
|
|
});
|
|
|
|
ws.on("close", () => {
|
|
console.log("[WebSocket] Client disconnected");
|
|
unsubscribe();
|
|
});
|
|
|
|
ws.on("error", (error) => {
|
|
console.error("[WebSocket] Error:", error);
|
|
unsubscribe();
|
|
});
|
|
});
|
|
|
|
// Start server
|
|
server.listen(PORT, () => {
|
|
console.log(`
|
|
╔═══════════════════════════════════════════════════════╗
|
|
║ Automaker Backend Server ║
|
|
╠═══════════════════════════════════════════════════════╣
|
|
║ HTTP API: http://localhost:${PORT} ║
|
|
║ WebSocket: ws://localhost:${PORT}/api/events ║
|
|
║ Health: http://localhost:${PORT}/api/health ║
|
|
╚═══════════════════════════════════════════════════════╝
|
|
`);
|
|
});
|
|
|
|
// Graceful shutdown
|
|
process.on("SIGTERM", () => {
|
|
console.log("SIGTERM received, shutting down...");
|
|
server.close(() => {
|
|
console.log("Server closed");
|
|
process.exit(0);
|
|
});
|
|
});
|
|
|
|
process.on("SIGINT", () => {
|
|
console.log("SIGINT received, shutting down...");
|
|
server.close(() => {
|
|
console.log("Server closed");
|
|
process.exit(0);
|
|
});
|
|
});
|