mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-03 08:53:36 +00:00
refactor(06-03): migrate Batch 5 secondary routes and wire router index
- features/routes/list.ts: Add facadeFactory parameter, use facade.detectOrphanedFeatures - projects/routes/overview.ts: Add facadeFactory parameter, use facade.getRunningAgents/getStatusForProject - features/index.ts: Pass facadeFactory to list handler - projects/index.ts: Pass facadeFactory to overview handler - auto-mode/index.ts: Accept facadeFactory parameter and wire to all route handlers - All routes maintain backward compatibility with autoModeService fallback
This commit is contained in:
@@ -1,11 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
* Auto Mode routes - HTTP API for autonomous feature implementation
|
* Auto Mode routes - HTTP API for autonomous feature implementation
|
||||||
*
|
*
|
||||||
* Uses the AutoModeService for real feature execution with Claude Agent SDK
|
* Uses the AutoModeService for real feature execution with Claude Agent SDK.
|
||||||
|
* Supports optional facadeFactory for per-project facade creation during migration.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import type { AutoModeService } from '../../services/auto-mode-service.js';
|
import type { AutoModeService } from '../../services/auto-mode-service.js';
|
||||||
|
import type { AutoModeServiceFacade } from '../../services/auto-mode/index.js';
|
||||||
import { validatePathParams } from '../../middleware/validate-paths.js';
|
import { validatePathParams } from '../../middleware/validate-paths.js';
|
||||||
import { createStopFeatureHandler } from './routes/stop-feature.js';
|
import { createStopFeatureHandler } from './routes/stop-feature.js';
|
||||||
import { createStatusHandler } from './routes/status.js';
|
import { createStatusHandler } from './routes/status.js';
|
||||||
@@ -21,59 +23,82 @@ import { createCommitFeatureHandler } from './routes/commit-feature.js';
|
|||||||
import { createApprovePlanHandler } from './routes/approve-plan.js';
|
import { createApprovePlanHandler } from './routes/approve-plan.js';
|
||||||
import { createResumeInterruptedHandler } from './routes/resume-interrupted.js';
|
import { createResumeInterruptedHandler } from './routes/resume-interrupted.js';
|
||||||
|
|
||||||
export function createAutoModeRoutes(autoModeService: AutoModeService): Router {
|
/**
|
||||||
|
* Create auto-mode routes with optional facade factory.
|
||||||
|
*
|
||||||
|
* @param autoModeService - The AutoModeService instance (for backward compatibility)
|
||||||
|
* @param facadeFactory - Optional factory for creating per-project facades
|
||||||
|
*/
|
||||||
|
export function createAutoModeRoutes(
|
||||||
|
autoModeService: AutoModeService,
|
||||||
|
facadeFactory?: (projectPath: string) => AutoModeServiceFacade
|
||||||
|
): Router {
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
// Auto loop control routes
|
// Auto loop control routes
|
||||||
router.post('/start', validatePathParams('projectPath'), createStartHandler(autoModeService));
|
router.post(
|
||||||
router.post('/stop', validatePathParams('projectPath'), createStopHandler(autoModeService));
|
'/start',
|
||||||
|
validatePathParams('projectPath'),
|
||||||
|
createStartHandler(autoModeService, facadeFactory)
|
||||||
|
);
|
||||||
|
router.post(
|
||||||
|
'/stop',
|
||||||
|
validatePathParams('projectPath'),
|
||||||
|
createStopHandler(autoModeService, facadeFactory)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Note: stop-feature doesn't have projectPath, so we pass undefined for facade.
|
||||||
|
// When we fully migrate, we can update stop-feature to use a different approach.
|
||||||
router.post('/stop-feature', createStopFeatureHandler(autoModeService));
|
router.post('/stop-feature', createStopFeatureHandler(autoModeService));
|
||||||
router.post('/status', validatePathParams('projectPath?'), createStatusHandler(autoModeService));
|
router.post(
|
||||||
|
'/status',
|
||||||
|
validatePathParams('projectPath?'),
|
||||||
|
createStatusHandler(autoModeService, facadeFactory)
|
||||||
|
);
|
||||||
router.post(
|
router.post(
|
||||||
'/run-feature',
|
'/run-feature',
|
||||||
validatePathParams('projectPath'),
|
validatePathParams('projectPath'),
|
||||||
createRunFeatureHandler(autoModeService)
|
createRunFeatureHandler(autoModeService, facadeFactory)
|
||||||
);
|
);
|
||||||
router.post(
|
router.post(
|
||||||
'/verify-feature',
|
'/verify-feature',
|
||||||
validatePathParams('projectPath'),
|
validatePathParams('projectPath'),
|
||||||
createVerifyFeatureHandler(autoModeService)
|
createVerifyFeatureHandler(autoModeService, facadeFactory)
|
||||||
);
|
);
|
||||||
router.post(
|
router.post(
|
||||||
'/resume-feature',
|
'/resume-feature',
|
||||||
validatePathParams('projectPath'),
|
validatePathParams('projectPath'),
|
||||||
createResumeFeatureHandler(autoModeService)
|
createResumeFeatureHandler(autoModeService, facadeFactory)
|
||||||
);
|
);
|
||||||
router.post(
|
router.post(
|
||||||
'/context-exists',
|
'/context-exists',
|
||||||
validatePathParams('projectPath'),
|
validatePathParams('projectPath'),
|
||||||
createContextExistsHandler(autoModeService)
|
createContextExistsHandler(autoModeService, facadeFactory)
|
||||||
);
|
);
|
||||||
router.post(
|
router.post(
|
||||||
'/analyze-project',
|
'/analyze-project',
|
||||||
validatePathParams('projectPath'),
|
validatePathParams('projectPath'),
|
||||||
createAnalyzeProjectHandler(autoModeService)
|
createAnalyzeProjectHandler(autoModeService, facadeFactory)
|
||||||
);
|
);
|
||||||
router.post(
|
router.post(
|
||||||
'/follow-up-feature',
|
'/follow-up-feature',
|
||||||
validatePathParams('projectPath', 'imagePaths[]'),
|
validatePathParams('projectPath', 'imagePaths[]'),
|
||||||
createFollowUpFeatureHandler(autoModeService)
|
createFollowUpFeatureHandler(autoModeService, facadeFactory)
|
||||||
);
|
);
|
||||||
router.post(
|
router.post(
|
||||||
'/commit-feature',
|
'/commit-feature',
|
||||||
validatePathParams('projectPath', 'worktreePath?'),
|
validatePathParams('projectPath', 'worktreePath?'),
|
||||||
createCommitFeatureHandler(autoModeService)
|
createCommitFeatureHandler(autoModeService, facadeFactory)
|
||||||
);
|
);
|
||||||
router.post(
|
router.post(
|
||||||
'/approve-plan',
|
'/approve-plan',
|
||||||
validatePathParams('projectPath'),
|
validatePathParams('projectPath'),
|
||||||
createApprovePlanHandler(autoModeService)
|
createApprovePlanHandler(autoModeService, facadeFactory)
|
||||||
);
|
);
|
||||||
router.post(
|
router.post(
|
||||||
'/resume-interrupted',
|
'/resume-interrupted',
|
||||||
validatePathParams('projectPath'),
|
validatePathParams('projectPath'),
|
||||||
createResumeInterruptedHandler(autoModeService)
|
createResumeInterruptedHandler(autoModeService, facadeFactory)
|
||||||
);
|
);
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { Router } from 'express';
|
|||||||
import { FeatureLoader } from '../../services/feature-loader.js';
|
import { FeatureLoader } from '../../services/feature-loader.js';
|
||||||
import type { SettingsService } from '../../services/settings-service.js';
|
import type { SettingsService } from '../../services/settings-service.js';
|
||||||
import type { AutoModeService } from '../../services/auto-mode-service.js';
|
import type { AutoModeService } from '../../services/auto-mode-service.js';
|
||||||
|
import type { AutoModeServiceFacade } from '../../services/auto-mode/index.js';
|
||||||
import type { EventEmitter } from '../../lib/events.js';
|
import type { EventEmitter } from '../../lib/events.js';
|
||||||
import { validatePathParams } from '../../middleware/validate-paths.js';
|
import { validatePathParams } from '../../middleware/validate-paths.js';
|
||||||
import { createListHandler } from './routes/list.js';
|
import { createListHandler } from './routes/list.js';
|
||||||
@@ -24,14 +25,15 @@ export function createFeaturesRoutes(
|
|||||||
featureLoader: FeatureLoader,
|
featureLoader: FeatureLoader,
|
||||||
settingsService?: SettingsService,
|
settingsService?: SettingsService,
|
||||||
events?: EventEmitter,
|
events?: EventEmitter,
|
||||||
autoModeService?: AutoModeService
|
autoModeService?: AutoModeService,
|
||||||
|
facadeFactory?: (projectPath: string) => AutoModeServiceFacade
|
||||||
): Router {
|
): Router {
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
'/list',
|
'/list',
|
||||||
validatePathParams('projectPath'),
|
validatePathParams('projectPath'),
|
||||||
createListHandler(featureLoader, autoModeService)
|
createListHandler(featureLoader, autoModeService, facadeFactory)
|
||||||
);
|
);
|
||||||
router.post('/get', validatePathParams('projectPath'), createGetHandler(featureLoader));
|
router.post('/get', validatePathParams('projectPath'), createGetHandler(featureLoader));
|
||||||
router.post(
|
router.post(
|
||||||
|
|||||||
@@ -8,12 +8,17 @@
|
|||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
import { FeatureLoader } from '../../../services/feature-loader.js';
|
import { FeatureLoader } from '../../../services/feature-loader.js';
|
||||||
import type { AutoModeService } from '../../../services/auto-mode-service.js';
|
import type { AutoModeService } from '../../../services/auto-mode-service.js';
|
||||||
|
import type { AutoModeServiceFacade } from '../../../services/auto-mode/index.js';
|
||||||
import { getErrorMessage, logError } from '../common.js';
|
import { getErrorMessage, logError } from '../common.js';
|
||||||
import { createLogger } from '@automaker/utils';
|
import { createLogger } from '@automaker/utils';
|
||||||
|
|
||||||
const logger = createLogger('FeaturesListRoute');
|
const logger = createLogger('FeaturesListRoute');
|
||||||
|
|
||||||
export function createListHandler(featureLoader: FeatureLoader, autoModeService?: AutoModeService) {
|
export function createListHandler(
|
||||||
|
featureLoader: FeatureLoader,
|
||||||
|
autoModeService?: AutoModeService,
|
||||||
|
facadeFactory?: (projectPath: string) => AutoModeServiceFacade
|
||||||
|
) {
|
||||||
return async (req: Request, res: Response): Promise<void> => {
|
return async (req: Request, res: Response): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const { projectPath } = req.body as { projectPath: string };
|
const { projectPath } = req.body as { projectPath: string };
|
||||||
@@ -29,7 +34,21 @@ export function createListHandler(featureLoader: FeatureLoader, autoModeService?
|
|||||||
// This detects features whose branches no longer exist (e.g., after merge/delete)
|
// This detects features whose branches no longer exist (e.g., after merge/delete)
|
||||||
// We don't await this to keep the list response fast
|
// We don't await this to keep the list response fast
|
||||||
// Note: detectOrphanedFeatures handles errors internally and always resolves
|
// Note: detectOrphanedFeatures handles errors internally and always resolves
|
||||||
if (autoModeService) {
|
if (facadeFactory) {
|
||||||
|
const facade = facadeFactory(projectPath);
|
||||||
|
facade.detectOrphanedFeatures().then((orphanedFeatures) => {
|
||||||
|
if (orphanedFeatures.length > 0) {
|
||||||
|
logger.info(
|
||||||
|
`[ProjectLoad] Detected ${orphanedFeatures.length} orphaned feature(s) in ${projectPath}`
|
||||||
|
);
|
||||||
|
for (const { feature, missingBranch } of orphanedFeatures) {
|
||||||
|
logger.info(
|
||||||
|
`[ProjectLoad] Orphaned: ${feature.title || feature.id} - branch "${missingBranch}" no longer exists`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (autoModeService) {
|
||||||
autoModeService.detectOrphanedFeatures(projectPath).then((orphanedFeatures) => {
|
autoModeService.detectOrphanedFeatures(projectPath).then((orphanedFeatures) => {
|
||||||
if (orphanedFeatures.length > 0) {
|
if (orphanedFeatures.length > 0) {
|
||||||
logger.info(
|
logger.info(
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import type { FeatureLoader } from '../../services/feature-loader.js';
|
import type { FeatureLoader } from '../../services/feature-loader.js';
|
||||||
import type { AutoModeService } from '../../services/auto-mode-service.js';
|
import type { AutoModeService } from '../../services/auto-mode-service.js';
|
||||||
|
import type { AutoModeServiceFacade } from '../../services/auto-mode/index.js';
|
||||||
import type { SettingsService } from '../../services/settings-service.js';
|
import type { SettingsService } from '../../services/settings-service.js';
|
||||||
import type { NotificationService } from '../../services/notification-service.js';
|
import type { NotificationService } from '../../services/notification-service.js';
|
||||||
import { createOverviewHandler } from './routes/overview.js';
|
import { createOverviewHandler } from './routes/overview.js';
|
||||||
@@ -13,14 +14,21 @@ export function createProjectsRoutes(
|
|||||||
featureLoader: FeatureLoader,
|
featureLoader: FeatureLoader,
|
||||||
autoModeService: AutoModeService,
|
autoModeService: AutoModeService,
|
||||||
settingsService: SettingsService,
|
settingsService: SettingsService,
|
||||||
notificationService: NotificationService
|
notificationService: NotificationService,
|
||||||
|
facadeFactory?: (projectPath: string) => AutoModeServiceFacade
|
||||||
): Router {
|
): Router {
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
// GET /overview - Get aggregate status for all projects
|
// GET /overview - Get aggregate status for all projects
|
||||||
router.get(
|
router.get(
|
||||||
'/overview',
|
'/overview',
|
||||||
createOverviewHandler(featureLoader, autoModeService, settingsService, notificationService)
|
createOverviewHandler(
|
||||||
|
featureLoader,
|
||||||
|
autoModeService,
|
||||||
|
settingsService,
|
||||||
|
notificationService,
|
||||||
|
facadeFactory
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
|
|||||||
@@ -10,6 +10,11 @@
|
|||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
import type { FeatureLoader } from '../../../services/feature-loader.js';
|
import type { FeatureLoader } from '../../../services/feature-loader.js';
|
||||||
import type { AutoModeService } from '../../../services/auto-mode-service.js';
|
import type { AutoModeService } from '../../../services/auto-mode-service.js';
|
||||||
|
import type {
|
||||||
|
AutoModeServiceFacade,
|
||||||
|
RunningAgentInfo,
|
||||||
|
ProjectAutoModeStatus,
|
||||||
|
} from '../../../services/auto-mode/index.js';
|
||||||
import type { SettingsService } from '../../../services/settings-service.js';
|
import type { SettingsService } from '../../../services/settings-service.js';
|
||||||
import type { NotificationService } from '../../../services/notification-service.js';
|
import type { NotificationService } from '../../../services/notification-service.js';
|
||||||
import type {
|
import type {
|
||||||
@@ -149,7 +154,8 @@ export function createOverviewHandler(
|
|||||||
featureLoader: FeatureLoader,
|
featureLoader: FeatureLoader,
|
||||||
autoModeService: AutoModeService,
|
autoModeService: AutoModeService,
|
||||||
settingsService: SettingsService,
|
settingsService: SettingsService,
|
||||||
notificationService: NotificationService
|
notificationService: NotificationService,
|
||||||
|
facadeFactory?: (projectPath: string) => AutoModeServiceFacade
|
||||||
) {
|
) {
|
||||||
return async (_req: Request, res: Response): Promise<void> => {
|
return async (_req: Request, res: Response): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
@@ -158,7 +164,15 @@ export function createOverviewHandler(
|
|||||||
const projectRefs: ProjectRef[] = settings.projects || [];
|
const projectRefs: ProjectRef[] = settings.projects || [];
|
||||||
|
|
||||||
// Get all running agents once to count live running features per project
|
// Get all running agents once to count live running features per project
|
||||||
const allRunningAgents = await autoModeService.getRunningAgents();
|
// Use facade if available, otherwise fall back to autoModeService
|
||||||
|
let allRunningAgents: RunningAgentInfo[];
|
||||||
|
if (facadeFactory && projectRefs.length > 0) {
|
||||||
|
// For running agents, we can use any project's facade since it's a global query
|
||||||
|
const facade = facadeFactory(projectRefs[0].path);
|
||||||
|
allRunningAgents = await facade.getRunningAgents();
|
||||||
|
} else {
|
||||||
|
allRunningAgents = await autoModeService.getRunningAgents();
|
||||||
|
}
|
||||||
|
|
||||||
// Collect project statuses in parallel
|
// Collect project statuses in parallel
|
||||||
const projectStatusPromises = projectRefs.map(async (projectRef): Promise<ProjectStatus> => {
|
const projectStatusPromises = projectRefs.map(async (projectRef): Promise<ProjectStatus> => {
|
||||||
@@ -169,7 +183,13 @@ export function createOverviewHandler(
|
|||||||
const totalFeatures = features.length;
|
const totalFeatures = features.length;
|
||||||
|
|
||||||
// Get auto-mode status for this project (main worktree, branchName = null)
|
// Get auto-mode status for this project (main worktree, branchName = null)
|
||||||
const autoModeStatus = autoModeService.getStatusForProject(projectRef.path, null);
|
let autoModeStatus: ProjectAutoModeStatus;
|
||||||
|
if (facadeFactory) {
|
||||||
|
const facade = facadeFactory(projectRef.path);
|
||||||
|
autoModeStatus = facade.getStatusForProject(null);
|
||||||
|
} else {
|
||||||
|
autoModeStatus = autoModeService.getStatusForProject(projectRef.path, null);
|
||||||
|
}
|
||||||
const isAutoModeRunning = autoModeStatus.isAutoLoopRunning;
|
const isAutoModeRunning = autoModeStatus.isAutoLoopRunning;
|
||||||
|
|
||||||
// Count live running features for this project (across all branches)
|
// Count live running features for this project (across all branches)
|
||||||
|
|||||||
Reference in New Issue
Block a user