feat(server): add open-in-terminal endpoint

Add POST /open-in-terminal endpoint to open a system terminal in the
worktree directory using the cross-platform openInTerminal utility.

The endpoint validates that worktreePath is provided and is an
absolute path for security.

Extracted from PR #558.
This commit is contained in:
Stefan de Vogelaere
2026-01-17 20:00:35 +01:00
parent 16f244958d
commit af69ca719b
2 changed files with 56 additions and 0 deletions

View File

@@ -29,6 +29,7 @@ import {
createGetAvailableEditorsHandler,
createRefreshEditorsHandler,
} from './routes/open-in-editor.js';
import { createOpenInTerminalHandler } from './routes/open-in-terminal.js';
import { createInitGitHandler } from './routes/init-git.js';
import { createMigrateHandler } from './routes/migrate.js';
import { createStartDevHandler } from './routes/start-dev.js';
@@ -97,6 +98,11 @@ export function createWorktreeRoutes(
);
router.post('/switch-branch', requireValidWorktree, createSwitchBranchHandler());
router.post('/open-in-editor', validatePathParams('worktreePath'), createOpenInEditorHandler());
router.post(
'/open-in-terminal',
validatePathParams('worktreePath'),
createOpenInTerminalHandler()
);
router.get('/default-editor', createGetDefaultEditorHandler());
router.get('/available-editors', createGetAvailableEditorsHandler());
router.post('/refresh-editors', createRefreshEditorsHandler());

View File

@@ -0,0 +1,50 @@
/**
* POST /open-in-terminal endpoint - Open a terminal in a worktree directory
*
* This module uses @automaker/platform for cross-platform terminal launching.
*/
import type { Request, Response } from 'express';
import { isAbsolute } from 'path';
import { openInTerminal } from '@automaker/platform';
import { getErrorMessage, logError } from '../common.js';
export function createOpenInTerminalHandler() {
return async (req: Request, res: Response): Promise<void> => {
try {
const { worktreePath } = req.body as {
worktreePath: string;
};
if (!worktreePath) {
res.status(400).json({
success: false,
error: 'worktreePath required',
});
return;
}
// Security: Validate that worktreePath is an absolute path
if (!isAbsolute(worktreePath)) {
res.status(400).json({
success: false,
error: 'worktreePath must be an absolute path',
});
return;
}
// Use the platform utility to open in terminal
const result = await openInTerminal(worktreePath);
res.json({
success: true,
result: {
message: `Opened terminal in ${worktreePath}`,
terminalName: result.terminalName,
},
});
} catch (error) {
logError(error, 'Open in terminal failed');
res.status(500).json({ success: false, error: getErrorMessage(error) });
}
};
}