feat: Add Cursor CLI configuration and status endpoints

- Implemented new routes for managing Cursor CLI configuration, including getting current settings and updating default models.
- Created status endpoint to check Cursor CLI installation and authentication status.
- Updated HttpApiClient to include methods for interacting with the new Cursor API endpoints.
- Marked completion of the setup routes and status endpoints phase in the integration plan.
This commit is contained in:
Shirone
2025-12-28 00:53:31 +01:00
parent 6e9468a56e
commit 59612231bb
6 changed files with 272 additions and 19 deletions

View File

@@ -12,6 +12,12 @@ import { createApiKeysHandler } from './routes/api-keys.js';
import { createPlatformHandler } from './routes/platform.js';
import { createVerifyClaudeAuthHandler } from './routes/verify-claude-auth.js';
import { createGhStatusHandler } from './routes/gh-status.js';
import { createCursorStatusHandler } from './routes/cursor-status.js';
import {
createGetCursorConfigHandler,
createSetCursorDefaultModelHandler,
createSetCursorModelsHandler,
} from './routes/cursor-config.js';
export function createSetupRoutes(): Router {
const router = Router();
@@ -26,5 +32,11 @@ export function createSetupRoutes(): Router {
router.post('/verify-claude-auth', createVerifyClaudeAuthHandler());
router.get('/gh-status', createGhStatusHandler());
// Cursor CLI routes
router.get('/cursor-status', createCursorStatusHandler());
router.get('/cursor-config', createGetCursorConfigHandler());
router.post('/cursor-config/default-model', createSetCursorDefaultModelHandler());
router.post('/cursor-config/models', createSetCursorModelsHandler());
return router;
}

View File

@@ -0,0 +1,136 @@
/**
* Cursor CLI configuration routes
*
* Provides endpoints for managing Cursor CLI configuration:
* - GET /api/setup/cursor-config - Get current configuration
* - POST /api/setup/cursor-config/default-model - Set default model
* - POST /api/setup/cursor-config/models - Set enabled models
*/
import type { Request, Response } from 'express';
import { CursorConfigManager } from '../../../providers/cursor-config-manager.js';
import { CURSOR_MODEL_MAP, type CursorModelId } from '@automaker/types';
import { getErrorMessage, logError } from '../common.js';
/**
* Creates handler for GET /api/setup/cursor-config
* Returns current Cursor configuration and available models
*/
export function createGetCursorConfigHandler() {
return async (req: Request, res: Response): Promise<void> => {
try {
const projectPath = req.query.projectPath as string;
if (!projectPath) {
res.status(400).json({
success: false,
error: 'projectPath query parameter is required',
});
return;
}
const configManager = new CursorConfigManager(projectPath);
res.json({
success: true,
config: configManager.getConfig(),
availableModels: Object.values(CURSOR_MODEL_MAP),
});
} catch (error) {
logError(error, 'Get Cursor config failed');
res.status(500).json({
success: false,
error: getErrorMessage(error),
});
}
};
}
/**
* Creates handler for POST /api/setup/cursor-config/default-model
* Sets the default Cursor model
*/
export function createSetCursorDefaultModelHandler() {
return async (req: Request, res: Response): Promise<void> => {
try {
const { model, projectPath } = req.body;
if (!projectPath) {
res.status(400).json({
success: false,
error: 'projectPath is required',
});
return;
}
if (!model || !(model in CURSOR_MODEL_MAP)) {
res.status(400).json({
success: false,
error: `Invalid model ID. Valid models: ${Object.keys(CURSOR_MODEL_MAP).join(', ')}`,
});
return;
}
const configManager = new CursorConfigManager(projectPath);
configManager.setDefaultModel(model as CursorModelId);
res.json({ success: true, model });
} catch (error) {
logError(error, 'Set Cursor default model failed');
res.status(500).json({
success: false,
error: getErrorMessage(error),
});
}
};
}
/**
* Creates handler for POST /api/setup/cursor-config/models
* Sets the enabled Cursor models list
*/
export function createSetCursorModelsHandler() {
return async (req: Request, res: Response): Promise<void> => {
try {
const { models, projectPath } = req.body;
if (!projectPath) {
res.status(400).json({
success: false,
error: 'projectPath is required',
});
return;
}
if (!Array.isArray(models)) {
res.status(400).json({
success: false,
error: 'Models must be an array',
});
return;
}
// Filter to valid models only
const validModels = models.filter((m): m is CursorModelId => m in CURSOR_MODEL_MAP);
if (validModels.length === 0) {
res.status(400).json({
success: false,
error: 'No valid models provided',
});
return;
}
const configManager = new CursorConfigManager(projectPath);
configManager.setEnabledModels(validModels);
res.json({ success: true, models: validModels });
} catch (error) {
logError(error, 'Set Cursor models failed');
res.status(500).json({
success: false,
error: getErrorMessage(error),
});
}
};
}

View File

@@ -0,0 +1,52 @@
/**
* GET /cursor-status endpoint - Get Cursor CLI installation and auth status
*/
import type { Request, Response } from 'express';
import { CursorProvider } from '../../../providers/cursor-provider.js';
import { getErrorMessage, logError } from '../common.js';
/**
* Creates handler for GET /api/setup/cursor-status
* Returns Cursor CLI installation and authentication status
*/
export function createCursorStatusHandler() {
const installCommand = 'curl https://cursor.com/install -fsS | bash';
const loginCommand = 'cursor-agent login';
return async (_req: Request, res: Response): Promise<void> => {
try {
const provider = new CursorProvider();
const [installed, version, auth] = await Promise.all([
provider.isInstalled(),
provider.getVersion(),
provider.checkAuth(),
]);
// Get CLI path from provider (using type assertion since cliPath is private)
const cliPath = installed
? (provider as unknown as { cliPath: string | null }).cliPath
: null;
res.json({
success: true,
installed,
version: version || null,
path: cliPath,
auth: {
authenticated: auth.authenticated,
method: auth.method,
},
installCommand,
loginCommand,
});
} catch (error) {
logError(error, 'Get Cursor status failed');
res.status(500).json({
success: false,
error: getErrorMessage(error),
});
}
};
}

View File

@@ -508,6 +508,59 @@ export class HttpApiClient implements ElectronAPI {
error?: string;
}> => this.get('/api/setup/gh-status'),
// Cursor CLI methods
getCursorStatus: (): Promise<{
success: boolean;
installed?: boolean;
version?: string | null;
path?: string | null;
auth?: {
authenticated: boolean;
method: string;
};
installCommand?: string;
loginCommand?: string;
error?: string;
}> => this.get('/api/setup/cursor-status'),
getCursorConfig: (
projectPath: string
): Promise<{
success: boolean;
config?: {
defaultModel?: string;
models?: string[];
mcpServers?: string[];
rules?: string[];
};
availableModels?: Array<{
id: string;
label: string;
description: string;
hasThinking: boolean;
tier: 'free' | 'pro';
}>;
error?: string;
}> => this.get(`/api/setup/cursor-config?projectPath=${encodeURIComponent(projectPath)}`),
setCursorDefaultModel: (
projectPath: string,
model: string
): Promise<{
success: boolean;
model?: string;
error?: string;
}> => this.post('/api/setup/cursor-config/default-model', { projectPath, model }),
setCursorModels: (
projectPath: string,
models: string[]
): Promise<{
success: boolean;
models?: string[];
error?: string;
}> => this.post('/api/setup/cursor-config/models', { projectPath, models }),
onInstallProgress: (callback: (progress: unknown) => void) => {
return this.subscribeToEvent('agent:stream', callback);
},