mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-07-01 01:23:06 +08:00
feat: add Electron desktop app
This commit is contained in:
211
server/modules/computer-use/computer-use.routes.ts
Normal file
211
server/modules/computer-use/computer-use.routes.ts
Normal file
@@ -0,0 +1,211 @@
|
||||
import express from 'express';
|
||||
|
||||
import { computerUseService } from '@/modules/computer-use/computer-use.service.js';
|
||||
import { AppError } from '@/shared/utils.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
type AuthenticatedRequest = express.Request & {
|
||||
user?: {
|
||||
id?: string | number;
|
||||
};
|
||||
};
|
||||
|
||||
function requireUser(req: AuthenticatedRequest): { id: string | number } {
|
||||
const userId = req.user?.id;
|
||||
if (userId === undefined || userId === null || String(userId).trim() === '') {
|
||||
throw new AppError('Authenticated user is required.', {
|
||||
code: 'AUTHENTICATED_USER_REQUIRED',
|
||||
statusCode: 401,
|
||||
});
|
||||
}
|
||||
return { id: userId };
|
||||
}
|
||||
|
||||
function getErrorStatusCode(error: unknown, fallbackStatusCode: number): number {
|
||||
if (error instanceof AppError) {
|
||||
return error.statusCode;
|
||||
}
|
||||
|
||||
if (error && typeof error === 'object') {
|
||||
const statusCode = 'statusCode' in error ? error.statusCode : 'status' in error ? error.status : undefined;
|
||||
if (typeof statusCode === 'number' && Number.isInteger(statusCode) && statusCode >= 400 && statusCode <= 599) {
|
||||
return statusCode;
|
||||
}
|
||||
}
|
||||
|
||||
return fallbackStatusCode;
|
||||
}
|
||||
|
||||
function readParam(value: string | string[] | undefined): string {
|
||||
return Array.isArray(value) ? value[0] || '' : value || '';
|
||||
}
|
||||
|
||||
function toButton(value: unknown): 'left' | 'right' | 'middle' {
|
||||
return value === 'right' || value === 'middle' ? value : 'left';
|
||||
}
|
||||
|
||||
router.get('/status', async (_req, res) => {
|
||||
try {
|
||||
res.json({ success: true, data: await computerUseService.getStatus() });
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to load Computer Use status.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/settings', async (req: AuthenticatedRequest, res) => {
|
||||
try {
|
||||
requireUser(req);
|
||||
res.json({ success: true, data: { settings: await computerUseService.getSettings() } });
|
||||
} catch (error) {
|
||||
res.status(getErrorStatusCode(error, 500)).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to load Computer Use settings.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.put('/settings', async (req: AuthenticatedRequest, res) => {
|
||||
try {
|
||||
requireUser(req);
|
||||
const settings = await computerUseService.updateSettings(req.body || {});
|
||||
res.json({ success: true, data: { settings } });
|
||||
} catch (error) {
|
||||
res.status(getErrorStatusCode(error, 400)).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to save Computer Use settings.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/runtime/install', async (req: AuthenticatedRequest, res) => {
|
||||
try {
|
||||
requireUser(req);
|
||||
const result = await computerUseService.installRuntime();
|
||||
res.status(result.success ? 200 : 500).json({
|
||||
success: result.success,
|
||||
data: result,
|
||||
error: result.success ? undefined : result.message,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(getErrorStatusCode(error, 500)).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to install Computer Use runtime.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/sessions', async (req: AuthenticatedRequest, res) => {
|
||||
try {
|
||||
res.json({ success: true, data: { sessions: await computerUseService.listSessions(requireUser(req)) } });
|
||||
} catch (error) {
|
||||
res.status(getErrorStatusCode(error, 500)).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to list Computer Use sessions.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/sessions/:sessionId/screenshot', async (req: AuthenticatedRequest, res) => {
|
||||
try {
|
||||
const session = await computerUseService.userScreenshot(requireUser(req), readParam(req.params.sessionId));
|
||||
res.json({ success: true, data: { session } });
|
||||
} catch (error) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to capture the screen.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/sessions/:sessionId/click', async (req: AuthenticatedRequest, res) => {
|
||||
try {
|
||||
const x = Number(req.body?.x);
|
||||
const y = Number(req.body?.y);
|
||||
if (!Number.isFinite(x) || !Number.isFinite(y)) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'Valid numeric coordinates are required.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const session = await computerUseService.userClick(requireUser(req), readParam(req.params.sessionId), {
|
||||
x,
|
||||
y,
|
||||
button: toButton(req.body?.button),
|
||||
double: req.body?.double === true,
|
||||
});
|
||||
res.json({ success: true, data: { session } });
|
||||
} catch (error) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to click.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/sessions/:sessionId/press-key', async (req: AuthenticatedRequest, res) => {
|
||||
try {
|
||||
const session = await computerUseService.userPressKey(requireUser(req), readParam(req.params.sessionId), String(req.body?.key || ''));
|
||||
res.json({ success: true, data: { session } });
|
||||
} catch (error) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to send key input.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/sessions/:sessionId/consent/grant', async (req: AuthenticatedRequest, res) => {
|
||||
try {
|
||||
const session = await computerUseService.grantAgentAccess(requireUser(req), readParam(req.params.sessionId));
|
||||
res.json({ success: true, data: { session } });
|
||||
} catch (error) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to grant control.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/sessions/:sessionId/consent/revoke', async (req: AuthenticatedRequest, res) => {
|
||||
try {
|
||||
const session = await computerUseService.revokeAgentAccess(requireUser(req), readParam(req.params.sessionId));
|
||||
res.json({ success: true, data: { session } });
|
||||
} catch (error) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to revoke control.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/sessions/:sessionId/stop', async (req: AuthenticatedRequest, res) => {
|
||||
try {
|
||||
const result = await computerUseService.stopSession(requireUser(req), readParam(req.params.sessionId));
|
||||
res.json({ success: true, data: result });
|
||||
} catch (error) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to stop Computer Use session.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.delete('/sessions/:sessionId', async (req: AuthenticatedRequest, res) => {
|
||||
try {
|
||||
const result = await computerUseService.deleteSession(requireUser(req), readParam(req.params.sessionId));
|
||||
res.json({ success: true, data: result });
|
||||
} catch (error) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to delete Computer Use session.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user