import React, { memo, useCallback, useEffect, useMemo, useState } from 'react'; import { PreferenceService, useInjectable } from '@opensumi/ide-core-browser'; import { Button, Icon } from '@opensumi/ide-core-browser/lib/components'; import { EnhancePopover } from '@opensumi/ide-core-browser/lib/components/ai-native/popover'; import { Loading } from '@opensumi/ide-core-browser/lib/components/loading'; import { CommandService, localize } from '@opensumi/ide-core-common'; import { AINativeSettingSectionsId } from '@opensumi/ide-core-common/lib/settings'; import { ETerminalAutoExecutionPolicy } from '../../../preferences/schema'; import { IMCPServerToolComponentProps } from '../../../types'; import { RunCommandHandler } from '../handlers/RunCommand'; import { computeAnsiLogString } from './computeAnsiLogString'; import styles from './index.module.less'; function getResult(raw: string) { const result: { isError?: boolean; text?: string; } = {}; try { const data: { content: { type: string; text: string }[]; isError?: boolean; } = JSON.parse(raw); if (data.isError) { result.isError = data.isError; } if (data.content) { result.text = data.content.map((item) => item.text).join('\n'); } return result; } catch { return null; } } const autoExecutionPolicyLabels: { [k in ETerminalAutoExecutionPolicy]: string } = { [ETerminalAutoExecutionPolicy.always]: 'ai.native.terminal.autorun.always', [ETerminalAutoExecutionPolicy.auto]: 'ai.native.terminal.autorun.auto', [ETerminalAutoExecutionPolicy.off]: 'ai.native.terminal.autorun.off', }; function getAutoExecutionPolicyLabels(k: ETerminalAutoExecutionPolicy) { return localize(autoExecutionPolicyLabels[k]); } export const TerminalToolComponent = memo((props: IMCPServerToolComponentProps) => { const { args, toolCallId } = props; const handler = useInjectable(RunCommandHandler); const preferenceService: PreferenceService = useInjectable(PreferenceService); const commandService = useInjectable(CommandService); const [running, toggleRunning] = useState(false); const terminalAutoExecution = preferenceService.get( AINativeSettingSectionsId.TerminalAutoRun, ); const needApproval = useMemo(() => { // 值为 off 或 auto 且 args.require_user_approval if ( terminalAutoExecution === ETerminalAutoExecutionPolicy.off || (terminalAutoExecution === ETerminalAutoExecutionPolicy.auto && props.args?.require_user_approval) ) { return true; } return false; }, [props.args]); useEffect(() => { if (props.state === 'result') { toggleRunning(false); } else if (!needApproval) { toggleRunning(true); } }, [props]); const openCommandAutoExecutionConfig = useCallback(() => { commandService.executeCommand('workbench.action.openSettings', 'ai.native.terminal.autorun'); }, []); const handleClick = useCallback((approval: boolean) => { if (!toolCallId) { return; } handler.handleApproval(toolCallId, approval); toggleRunning(true); }, []); const output = useMemo(() => { if (props.result) { return getResult(props.result); } return null; }, [props]); return (
{needApproval ? localize('ai.native.mcp.terminal.allow-question') : localize('ai.native.mcp.terminal.command')}

$ {args?.command}

{args?.explanation}

{props.state === 'complete' && needApproval && args && !running && (
)} {props.state === 'result' && output && ( <>
{getAutoExecutionPolicyLabels(terminalAutoExecution || ETerminalAutoExecutionPolicy.auto)}
)}
{running && (
{localize('ai.native.terminal.autorun.running')}
)}
); });