mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-22 16:00:51 +00:00
feat: refine terminal ai assistant settings (#12315)
This commit is contained in:
@@ -1395,18 +1395,15 @@ const message = {
|
||||
cursorBar: 'Bar',
|
||||
scrollback: 'Scrollback',
|
||||
scrollSensitivity: 'Scroll Sensitivity',
|
||||
aiStatus: 'AI Terminal',
|
||||
aiSettings: 'AI Terminal Settings',
|
||||
aiAssistant: 'AI Assistant',
|
||||
aiAccountHelper:
|
||||
'Use the selected model account to generate and fill commands. For local models like Ollama and vLLM, use a custom model account.',
|
||||
aiAccountRequired: 'Please select an available AI account first.',
|
||||
aiPrefix: 'Trigger Prefix',
|
||||
aiPrefixHelper: 'Enter this prefix and press Enter to trigger AI command generation.',
|
||||
aiPrefixHelper:
|
||||
'Enter the specified prefix and press Enter to trigger AI command generation, for example: {0} help me check disk usage.',
|
||||
aiRiskCommands: 'Risk Command Interception',
|
||||
aiRiskCommandsHelper:
|
||||
'Generated commands matching any of these fragments will be blocked and filled back as comments. Leave empty to disable interception. Supports add, edit, and delete.',
|
||||
aiAddRiskCommand: 'Add Risk Command',
|
||||
aiRemoveRiskCommand: 'Delete',
|
||||
'Configure risky commands to intercept, one per line. When matched, generation will be blocked and filled back as comments.',
|
||||
aiSummary: 'When a line starts with the {0} prefix and you press Enter, AI command generation is triggered.',
|
||||
aiPrefixAsciiVisible:
|
||||
'Only ASCII visible characters are supported. Spaces, CJK characters, and full-width symbols are not allowed.',
|
||||
|
||||
@@ -1434,17 +1434,15 @@ const message = {
|
||||
cursorBar: 'Barra',
|
||||
scrollback: 'Scrollback',
|
||||
scrollSensitivity: 'Sensibilidad de scroll',
|
||||
aiStatus: 'Terminal de IA',
|
||||
aiSettings: 'Configuración del terminal de IA',
|
||||
aiAssistant: 'Asistente de IA',
|
||||
aiAccountHelper:
|
||||
'Use la cuenta de modelo seleccionada para generar y rellenar comandos. Para modelos locales como Ollama y vLLM, use una cuenta de modelo personalizada.',
|
||||
aiPrefix: 'Prefijo de activación',
|
||||
aiPrefixHelper: 'Introduzca este prefijo y pulse Enter para activar la generación de comandos por IA.',
|
||||
aiPrefixHelper:
|
||||
'Introduzca el prefijo indicado y pulse Enter para activar la generación de comandos por IA; por ejemplo: {0} ayúdame a consultar la información del disco.',
|
||||
aiRiskCommands: 'Intercepción de comandos de riesgo',
|
||||
aiRiskCommandsHelper:
|
||||
'Los comandos generados que coincidan con cualquiera de estos fragmentos se bloquearán y se devolverán como comentarios. Déjelo vacío para desactivar la intercepción. Admite añadir, editar y eliminar.',
|
||||
aiAddRiskCommand: 'Añadir comando de riesgo',
|
||||
aiRemoveRiskCommand: 'Eliminar',
|
||||
'Configure los comandos de riesgo que se deben interceptar, uno por línea. Si coinciden, se bloqueará la generación y se devolverán como comentarios.',
|
||||
aiSummary:
|
||||
'Cuando una línea comienza con el prefijo {0} y pulsa Enter, se activa la generación de comandos por IA.',
|
||||
aiPrefixAsciiVisible:
|
||||
|
||||
@@ -1405,17 +1405,15 @@ const message = {
|
||||
cursorBar: 'バー',
|
||||
scrollback: 'スクロールバック',
|
||||
scrollSensitivity: 'スクロール感度',
|
||||
aiStatus: 'AI ターミナル',
|
||||
aiSettings: 'AI ターミナル設定',
|
||||
aiAssistant: 'AI アシスタント',
|
||||
aiAccountHelper:
|
||||
'選択したモデルアカウントでコマンドを生成して補完します。Ollama や vLLM などのローカルモデルはカスタムモデルアカウントを使用してください。',
|
||||
aiPrefix: 'トリガープレフィックス',
|
||||
aiPrefixHelper: 'このプレフィックスを入力して Enter を押すと、AI コマンド生成がトリガーされます。',
|
||||
aiPrefixHelper:
|
||||
'指定したプレフィックスを入力して Enter を押すと、AI コマンド生成がトリガーされます。例: {0} ディスク情報を確認して。',
|
||||
aiRiskCommands: '危険コマンドの遮断',
|
||||
aiRiskCommandsHelper:
|
||||
'これらの断片のいずれかに一致する生成コマンドはブロックされ、コメントとして埋め戻されます。空欄にすると遮断を無効化できます。追加、編集、削除に対応しています。',
|
||||
aiAddRiskCommand: '危険コマンドを追加',
|
||||
aiRemoveRiskCommand: '削除',
|
||||
'ブロック対象の危険コマンドを設定します(1 行に 1 件)。一致した場合、生成を停止しコメントとして埋め戻します。',
|
||||
aiSummary: '行が {0} プレフィックスで始まり Enter を押すと、AI コマンド生成がトリガーされます。',
|
||||
aiPrefixAsciiVisible: 'ASCII の表示可能文字のみ対応しています。スペース、CJK 文字、全角記号は使用できません。',
|
||||
saveHelper: '現在のターミナル設定を保存してもよろしいですか?',
|
||||
|
||||
@@ -1379,17 +1379,15 @@ const message = {
|
||||
cursorBar: '막대',
|
||||
scrollback: '스크롤백',
|
||||
scrollSensitivity: '스크롤 감도',
|
||||
aiStatus: 'AI 터미널',
|
||||
aiSettings: 'AI 터미널 설정',
|
||||
aiAssistant: 'AI 도우미',
|
||||
aiAccountHelper:
|
||||
'선택한 모델 계정으로 명령을 생성하고 채웁니다. Ollama, vLLM 같은 로컬 모델은 사용자 지정 모델 계정을 사용하세요.',
|
||||
aiPrefix: '트리거 접두사',
|
||||
aiPrefixHelper: '이 접두사를 입력하고 Enter를 누르면 AI 명령 생성이 트리거됩니다.',
|
||||
aiPrefixHelper:
|
||||
'지정한 접두사를 입력하고 Enter를 누르면 AI 명령 생성이 시작됩니다. 예: {0} 디스크 정보를 조회해줘.',
|
||||
aiRiskCommands: '위험 명령 차단',
|
||||
aiRiskCommandsHelper:
|
||||
'이 조각들 중 하나와 일치하는 생성 명령은 차단되고 주석으로 다시 채워집니다. 비워 두면 차단이 비활성화됩니다. 추가, 수정, 삭제를 지원합니다.',
|
||||
aiAddRiskCommand: '위험 명령 추가',
|
||||
aiRemoveRiskCommand: '삭제',
|
||||
'차단할 위험 명령을 설정하세요(한 줄에 하나씩). 일치하면 생성이 차단되고 주석으로 다시 채워집니다.',
|
||||
aiSummary: '{0} 접두사로 시작하는 줄에서 Enter를 누르면 AI 명령 생성이 트리거됩니다.',
|
||||
aiPrefixAsciiVisible: 'ASCII 표시 가능 문자만 지원합니다. 공백, CJK 문자 및 전각 기호는 사용할 수 없습니다.',
|
||||
saveHelper: '현재 터미널 설정을 저장하시겠습니까?',
|
||||
|
||||
@@ -1419,17 +1419,15 @@ const message = {
|
||||
cursorBar: 'Bar',
|
||||
scrollback: 'Skrol balik',
|
||||
scrollSensitivity: 'Kepekaan skrol',
|
||||
aiStatus: 'Terminal AI',
|
||||
aiSettings: 'Tetapan terminal AI',
|
||||
aiAssistant: 'Pembantu AI',
|
||||
aiAccountHelper:
|
||||
'Gunakan akaun model yang dipilih untuk menjana dan mengisi arahan. Untuk model tempatan seperti Ollama dan vLLM, gunakan akaun model tersuai.',
|
||||
aiPrefix: 'Awalan pencetus',
|
||||
aiPrefixHelper: 'Masukkan awalan ini dan tekan Enter untuk mencetuskan penjanaan arahan AI.',
|
||||
aiPrefixHelper:
|
||||
'Masukkan awalan yang ditetapkan dan tekan Enter untuk mencetuskan penjanaan arahan AI, contohnya: {0} bantu saya semak maklumat cakera.',
|
||||
aiRiskCommands: 'Pencegatan arahan berisiko',
|
||||
aiRiskCommandsHelper:
|
||||
'Arahan yang dijana dan sepadan dengan mana-mana fragmen ini akan disekat dan diisi semula sebagai komen. Biarkan kosong untuk mematikan pencegatan. Menyokong tambah, ubah suai dan padam.',
|
||||
aiAddRiskCommand: 'Tambah arahan berisiko',
|
||||
aiRemoveRiskCommand: 'Padam',
|
||||
'Konfigurasikan arahan berisiko yang perlu dipintas, satu bagi setiap baris. Apabila sepadan, penjanaan akan disekat dan diisi semula sebagai komen.',
|
||||
aiSummary:
|
||||
'Apabila baris bermula dengan awalan {0} dan anda menekan Enter, penjanaan arahan AI akan dicetuskan.',
|
||||
aiPrefixAsciiVisible:
|
||||
|
||||
@@ -1427,17 +1427,15 @@ const message = {
|
||||
cursorBar: 'Barra',
|
||||
scrollback: 'Scrollback',
|
||||
scrollSensitivity: 'Sensibilidade de rolagem',
|
||||
aiStatus: 'Terminal de IA',
|
||||
aiSettings: 'Configurações do terminal de IA',
|
||||
aiAssistant: 'Assistente de IA',
|
||||
aiAccountHelper:
|
||||
'Use a conta de modelo selecionada para gerar e preencher comandos. Para modelos locais como Ollama e vLLM, use uma conta de modelo personalizada.',
|
||||
aiPrefix: 'Prefixo de disparo',
|
||||
aiPrefixHelper: 'Digite este prefixo e pressione Enter para acionar a geração de comandos por IA.',
|
||||
aiPrefixHelper:
|
||||
'Digite o prefixo especificado e pressione Enter para acionar a geração de comandos por IA, por exemplo: {0} me ajude a consultar as informações do disco.',
|
||||
aiRiskCommands: 'Intercepção de comandos de risco',
|
||||
aiRiskCommandsHelper:
|
||||
'Os comandos gerados que corresponderem a qualquer um destes fragmentos serão bloqueados e preenchidos de volta como comentários. Deixe em branco para desativar a intercepção. Suporta adicionar, editar e excluir.',
|
||||
aiAddRiskCommand: 'Adicionar comando de risco',
|
||||
aiRemoveRiskCommand: 'Excluir',
|
||||
'Configure os comandos de risco que devem ser interceptados, um por linha. Quando houver correspondência, a geração será bloqueada e preenchida de volta como comentário.',
|
||||
aiSummary:
|
||||
'Quando uma linha começa com o prefixo {0} e você pressiona Enter, a geração de comandos por IA é acionada.',
|
||||
aiPrefixAsciiVisible:
|
||||
|
||||
@@ -1412,17 +1412,15 @@ const message = {
|
||||
cursorBar: 'Полоса',
|
||||
scrollback: 'Буфер прокрутки',
|
||||
scrollSensitivity: 'Чувствительность прокрутки',
|
||||
aiStatus: 'AI-терминал',
|
||||
aiSettings: 'Настройки AI-терминала',
|
||||
aiAssistant: 'AI-помощник',
|
||||
aiAccountHelper:
|
||||
'Используйте выбранный модельный аккаунт для генерации и подстановки команд. Для локальных моделей, таких как Ollama и vLLM, используйте пользовательский модельный аккаунт.',
|
||||
aiPrefix: 'Префикс запуска',
|
||||
aiPrefixHelper: 'Введите этот префикс и нажмите Enter, чтобы запустить генерацию команд ИИ.',
|
||||
aiPrefixHelper:
|
||||
'Введите указанный префикс и нажмите Enter, чтобы запустить генерацию команд ИИ, например: {0} помоги мне показать информацию о диске.',
|
||||
aiRiskCommands: 'Перехват опасных команд',
|
||||
aiRiskCommandsHelper:
|
||||
'Сгенерированные команды, совпадающие с любым из этих фрагментов, будут заблокированы и подставлены обратно в виде комментариев. Оставьте пустым, чтобы отключить перехват. Поддерживаются добавление, редактирование и удаление.',
|
||||
aiAddRiskCommand: 'Добавить опасную команду',
|
||||
aiRemoveRiskCommand: 'Удалить',
|
||||
'Настройте опасные команды для перехвата, по одной в каждой строке. При совпадении генерация будет заблокирована, а команда подставлена обратно в виде комментария.',
|
||||
aiSummary: 'Когда строка начинается с префикса {0} и вы нажимаете Enter, запускается генерация команд ИИ.',
|
||||
aiPrefixAsciiVisible:
|
||||
'Поддерживаются только видимые символы ASCII. Пробелы, символы CJK и полноширинные знаки не допускаются.',
|
||||
|
||||
@@ -1416,17 +1416,15 @@ const message = {
|
||||
cursorBar: 'Çubuk',
|
||||
scrollback: 'Geri Kaydırma',
|
||||
scrollSensitivity: 'Kaydırma Hassasiyeti',
|
||||
aiStatus: 'AI Terminali',
|
||||
aiSettings: 'AI terminal ayarları',
|
||||
aiAssistant: 'AI Asistani',
|
||||
aiAccountHelper:
|
||||
'Komut üretmek ve doldurmak için seçili model hesabını kullanın. Ollama ve vLLM gibi yerel modeller için özel model hesabı kullanın.',
|
||||
aiPrefix: 'Tetikleme öneki',
|
||||
aiPrefixHelper: 'Bu öneki girip Enter tuşuna bastığınızda AI komut üretimi tetiklenir.',
|
||||
aiPrefixHelper:
|
||||
'Belirtilen öneki girip Enter tuşuna bastığınızda AI komut üretimi tetiklenir; örneğin: {0} disk bilgisini sorgulamama yardım et.',
|
||||
aiRiskCommands: 'Riskli komut engelleme',
|
||||
aiRiskCommandsHelper:
|
||||
'Bu parçalardan herhangi biriyle eşleşen üretilmiş komutlar engellenir ve yorum olarak geri doldurulur. Engellemeyi kapatmak için boş bırakın. Ekleme, düzenleme ve silme desteklenir.',
|
||||
aiAddRiskCommand: 'Riskli komut ekle',
|
||||
aiRemoveRiskCommand: 'Sil',
|
||||
'Engellenecek riskli komutlari yapılandırın, her satıra bir tane gelecek şekilde. Eşleşme olduğunda üretim durdurulur ve yorum olarak geri doldurulur.',
|
||||
aiSummary: 'Bir satır {0} önekiyle başladığında ve Enter tuşuna bastığınızda AI komut üretimi tetiklenir.',
|
||||
aiPrefixAsciiVisible:
|
||||
'Yalnızca görünür ASCII karakterleri desteklenir. Boşluklara, CJK karakterlerine ve tam genişlikli sembollere izin verilmez.',
|
||||
|
||||
@@ -1317,15 +1317,12 @@ const message = {
|
||||
cursorBar: '條形',
|
||||
scrollback: '滾動行數',
|
||||
scrollSensitivity: '滾動速度',
|
||||
aiStatus: 'AI 終端',
|
||||
aiSettings: 'AI 終端設定',
|
||||
aiAssistant: 'AI 助手',
|
||||
aiAccountHelper: '使用所選模型帳號生成並回填命令。Ollama、vLLM 本地模型請使用自訂模型帳號。',
|
||||
aiPrefix: '觸發前綴',
|
||||
aiPrefixHelper: '輸入該前綴並按下 Enter 後,將觸發 AI 指令生成。',
|
||||
aiPrefixHelper: '輸入指定前綴並按下 Enter 後,將觸發 AI 指令生成,例如:{0} 幫我查詢磁碟資訊。',
|
||||
aiRiskCommands: '風險命令攔截',
|
||||
aiRiskCommandsHelper: '命中以下片段的生成命令會被攔截,並以註解形式回填;留空表示不攔截,支援增刪改',
|
||||
aiAddRiskCommand: '新增風險命令',
|
||||
aiRemoveRiskCommand: '刪除',
|
||||
aiRiskCommandsHelper: '配置需攔截的風險命令(每行一條),命中後將阻止生成並以註解回填。',
|
||||
aiSummary: '以 {0} 前綴開頭並按下 Enter 時,會觸發 AI 命令生成',
|
||||
aiPrefixAsciiVisible: '僅支援 ASCII 可見字元,不支援空格、中文或全形符號',
|
||||
saveHelper: '是否確認儲存目前終端設定?',
|
||||
|
||||
@@ -1326,15 +1326,12 @@ const message = {
|
||||
cursorBar: '条形',
|
||||
scrollback: '滚动行数',
|
||||
scrollSensitivity: '滚动速度',
|
||||
aiStatus: 'AI 终端',
|
||||
aiSettings: 'AI 终端设置',
|
||||
aiAssistant: 'AI 助手',
|
||||
aiAccountHelper: '使用所选模型账号生成并回填命令。Ollama、vLLM 本地模型请用自定义模型账号。',
|
||||
aiPrefix: '触发前缀',
|
||||
aiPrefixHelper: '输入该前缀并按回车后,将触发 AI 命令生成。',
|
||||
aiPrefixHelper: '输入指定前缀并按回车后,将触发 AI 命令生成,例如:{0} 帮我查询磁盘信息。',
|
||||
aiRiskCommands: '风险命令拦截',
|
||||
aiRiskCommandsHelper: '命中以下片段的生成命令会被拦截,并以注释形式回填;留空表示不拦截,支持增删改',
|
||||
aiAddRiskCommand: '新增风险命令',
|
||||
aiRemoveRiskCommand: '删除',
|
||||
aiRiskCommandsHelper: '配置需拦截的风险命令(每行一条),命中后将阻止生成并以注释回填。',
|
||||
aiSummary: '以 {0} 前缀开头并回车时,会触发 AI 命令生成',
|
||||
aiPrefixAsciiVisible: '仅支持 ASCII 可见字符,不支持空格、中文或全角符号',
|
||||
saveHelper: '是否确认保存当前终端配置?',
|
||||
|
||||
@@ -12,7 +12,10 @@ export const parseRiskCommands = (value: string): string[] => {
|
||||
}
|
||||
return parsed.map((item) => String(item).trim()).filter((item) => item.length > 0);
|
||||
} catch {
|
||||
return [];
|
||||
return value
|
||||
.split(/\r?\n/)
|
||||
.map((item) => String(item).trim())
|
||||
.filter((item) => item.length > 0);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -29,3 +32,7 @@ export const normalizeRiskCommands = (riskCommands: string[]): string[] => {
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export const formatRiskCommandsText = (riskCommands: string[]): string => {
|
||||
return normalizeRiskCommands(riskCommands).join('\n');
|
||||
};
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
<template>
|
||||
<el-form-item :label="$t('terminal.aiSettings')">
|
||||
<el-input :value="aiSummary" disabled>
|
||||
<template #append>
|
||||
<el-button @click="openDrawer" icon="Setting">
|
||||
{{ $t('commons.button.set') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-button @click="openDrawer" type="primary">
|
||||
{{ $t('terminal.aiAssistant') }}
|
||||
</el-button>
|
||||
|
||||
<DrawerPro v-model="drawerVisible" :header="$t('terminal.aiSettings')" size="60%" @close="handleClose">
|
||||
<DrawerPro v-model="drawerVisible" :header="$t('terminal.aiAssistant')" size="60%" @close="handleClose">
|
||||
<div v-loading="loading">
|
||||
<el-form ref="formRef" :model="formModel" label-position="top">
|
||||
<el-form-item :label="$t('terminal.aiStatus')">
|
||||
<el-form-item :label="$t('terminal.aiAssistant')">
|
||||
<el-switch v-model="formModel.status" active-value="Enable" inactive-value="Disable" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
@@ -47,20 +41,18 @@
|
||||
<el-select class="formInput" v-model="formModel.prefix">
|
||||
<el-option v-for="item in prefixOptions" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
<span class="input-help">{{ $t('terminal.aiPrefixHelper') }}</span>
|
||||
<span class="input-help">
|
||||
{{ $t('terminal.aiPrefixHelper', [formModel.prefix || DEFAULT_AI_PREFIX]) }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('terminal.aiRiskCommands')" prop="riskCommands" :rules="riskCommandRules">
|
||||
<div class="risk-command-list">
|
||||
<div class="risk-command-item" v-for="(command, index) in formModel.riskCommands" :key="index">
|
||||
<el-input :model-value="command" @update:model-value="updateRiskCommand(index, $event)" />
|
||||
<el-button link type="danger" @click="removeRiskCommand(index)">
|
||||
{{ $t('terminal.aiRemoveRiskCommand') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="risk-command-actions">
|
||||
<el-button plain @click="addRiskCommand">{{ $t('terminal.aiAddRiskCommand') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-form-item :label="$t('terminal.aiRiskCommands')">
|
||||
<CodemirrorPro
|
||||
v-model="formModel.riskCommands"
|
||||
mode="shell"
|
||||
line-wrapping
|
||||
:height-diff="560"
|
||||
:min-height="180"
|
||||
></CodemirrorPro>
|
||||
<span class="input-help">{{ $t('terminal.aiRiskCommandsHelper') }}</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -78,14 +70,21 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, nextTick, reactive, ref, watch } from 'vue';
|
||||
import { nextTick, reactive, ref } from 'vue';
|
||||
import type { ElForm } from 'element-plus';
|
||||
import CodemirrorPro from '@/components/codemirror-pro/index.vue';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import i18n from '@/lang';
|
||||
import { pageAgentAccounts } from '@/api/modules/ai';
|
||||
import { updateAgentTerminalAIInfo } from '@/api/modules/setting';
|
||||
import { getAgentTerminalAIInfo, updateAgentTerminalAIInfo } from '@/api/modules/setting';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { AI_PREFIX_OPTIONS, DEFAULT_AI_PREFIX, normalizeRiskCommands } from '@/views/terminal/setting/ai/helper';
|
||||
import {
|
||||
AI_PREFIX_OPTIONS,
|
||||
DEFAULT_AI_PREFIX,
|
||||
formatRiskCommandsText,
|
||||
normalizeRiskCommands,
|
||||
parseRiskCommands,
|
||||
} from '@/views/terminal/setting/ai/helper';
|
||||
|
||||
interface AgentAccountOption {
|
||||
id: number | string;
|
||||
@@ -95,63 +94,37 @@ interface AgentAccountOption {
|
||||
verified?: boolean;
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
status: string;
|
||||
accountId: string;
|
||||
prefix: string;
|
||||
riskCommands: string[];
|
||||
defaultRiskCommands: string[];
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'refresh'): void;
|
||||
}>();
|
||||
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
const formRef = ref<FormInstance>();
|
||||
const drawerVisible = ref(false);
|
||||
const loading = ref(false);
|
||||
const agentAccountOptions = ref<AgentAccountOption[]>([]);
|
||||
const defaultRiskCommands = ref<string[]>([]);
|
||||
const prefixOptions = AI_PREFIX_OPTIONS;
|
||||
const formModel = reactive({
|
||||
status: props.status,
|
||||
accountId: props.accountId,
|
||||
prefix: props.prefix,
|
||||
riskCommands: [...props.riskCommands],
|
||||
status: 'Disable',
|
||||
accountId: '',
|
||||
prefix: DEFAULT_AI_PREFIX,
|
||||
riskCommands: '',
|
||||
});
|
||||
|
||||
const syncFormFromProps = () => {
|
||||
formModel.status = props.status;
|
||||
formModel.accountId = props.accountId;
|
||||
formModel.prefix = props.prefix;
|
||||
formModel.riskCommands = [...props.riskCommands];
|
||||
const loadTerminalAISettings = async () => {
|
||||
const res = await getAgentTerminalAIInfo();
|
||||
formModel.status = res.data.aiStatus || 'Disable';
|
||||
formModel.accountId = res.data.aiAccountId || '';
|
||||
formModel.prefix = res.data.aiPrefix || DEFAULT_AI_PREFIX;
|
||||
formModel.riskCommands = formatRiskCommandsText(parseRiskCommands(res.data.aiRiskCommands || ''));
|
||||
defaultRiskCommands.value = parseRiskCommands(res.data.aiRiskCommandsDefault || '');
|
||||
};
|
||||
|
||||
const openDrawer = () => {
|
||||
syncFormFromProps();
|
||||
loadAgentAccounts();
|
||||
drawerVisible.value = true;
|
||||
loading.value = true;
|
||||
Promise.all([loadTerminalAISettings(), loadAgentAccounts()]).finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => [props.status, props.accountId, props.prefix, props.riskCommands],
|
||||
() => {
|
||||
if (drawerVisible.value) {
|
||||
return;
|
||||
}
|
||||
syncFormFromProps();
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
const aiSummary = computed(() => {
|
||||
if (props.status !== 'Enable') {
|
||||
return i18n.global.t('setting.unSetting');
|
||||
}
|
||||
const prefix = String(props.prefix || '').trim();
|
||||
return i18n.global.t('terminal.aiSummary', [prefix]);
|
||||
});
|
||||
|
||||
const isVerificationSkipped = (provider?: string) => {
|
||||
const key = (provider || '').toLowerCase();
|
||||
return key === 'custom' || key === 'vllm' || key === 'ollama' || key === 'kimi-coding';
|
||||
@@ -199,37 +172,9 @@ const accountRules = [
|
||||
},
|
||||
];
|
||||
|
||||
const riskCommandRules = [
|
||||
{
|
||||
validator: (_rule, value, callback) => {
|
||||
const commands = Array.isArray(value) ? value : [];
|
||||
if (commands.some((item) => String(item ?? '').trim().length === 0)) {
|
||||
callback(new Error(i18n.global.t('commons.rule.requiredInput')));
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
];
|
||||
|
||||
const addRiskCommand = () => {
|
||||
formModel.riskCommands = [...formModel.riskCommands, ''];
|
||||
};
|
||||
|
||||
const updateRiskCommand = (index: number, value: string) => {
|
||||
formModel.riskCommands = formModel.riskCommands.map((item, currentIndex) =>
|
||||
currentIndex === index ? value : item,
|
||||
);
|
||||
};
|
||||
|
||||
const removeRiskCommand = (index: number) => {
|
||||
formModel.riskCommands = formModel.riskCommands.filter((_, currentIndex) => currentIndex !== index);
|
||||
};
|
||||
|
||||
const resetRiskCommands = () => {
|
||||
formModel.prefix = DEFAULT_AI_PREFIX;
|
||||
formModel.riskCommands = [...props.defaultRiskCommands];
|
||||
formModel.riskCommands = formatRiskCommandsText(defaultRiskCommands.value);
|
||||
};
|
||||
|
||||
const handleConfirm = async () => {
|
||||
@@ -248,8 +193,9 @@ const handleConfirm = async () => {
|
||||
aiStatus: formModel.status,
|
||||
aiAccountId: formModel.status === 'Enable' ? formModel.accountId : '',
|
||||
aiPrefix: formModel.prefix.trim() || DEFAULT_AI_PREFIX,
|
||||
aiRiskCommands: JSON.stringify(normalizeRiskCommands(formModel.riskCommands)),
|
||||
aiRiskCommands: JSON.stringify(normalizeRiskCommands(parseRiskCommands(formModel.riskCommands))),
|
||||
});
|
||||
await loadTerminalAISettings();
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
drawerVisible.value = false;
|
||||
} finally {
|
||||
@@ -258,8 +204,7 @@ const handleConfirm = async () => {
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
syncFormFromProps();
|
||||
emit('refresh');
|
||||
formRef.value?.clearValidate();
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -268,24 +213,6 @@ const handleClose = () => {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.risk-command-list {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.risk-command-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.risk-command-item {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.account-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -114,17 +114,6 @@
|
||||
|
||||
<el-divider border-style="dashed" />
|
||||
|
||||
<AiSetting
|
||||
:status="aiForm.aiStatus"
|
||||
:account-id="aiForm.aiAccountId"
|
||||
:prefix="aiForm.aiPrefix"
|
||||
:risk-commands="aiForm.aiRiskCommands"
|
||||
:default-risk-commands="aiForm.aiRiskCommandsDefault"
|
||||
@refresh="loadAISettings"
|
||||
/>
|
||||
|
||||
<el-divider border-style="dashed" />
|
||||
|
||||
<el-form-item :label="$t('terminal.defaultConn')">
|
||||
<el-switch v-model="form.showDefaultConn" @change="changeShow" />
|
||||
</el-form-item>
|
||||
@@ -158,11 +147,9 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch, onMounted, onBeforeUnmount } from 'vue';
|
||||
import { getAgentTerminalAIInfo, getTerminalInfo, UpdateTerminalInfo } from '@/api/modules/setting';
|
||||
import { getTerminalInfo, UpdateTerminalInfo } from '@/api/modules/setting';
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
import OperateDialog from '@/views/terminal/setting/default_conn/index.vue';
|
||||
import AiSetting from '@/views/terminal/setting/ai/index.vue';
|
||||
import { DEFAULT_AI_PREFIX, parseRiskCommands } from '@/views/terminal/setting/ai/helper';
|
||||
import '@xterm/xterm/css/xterm.css';
|
||||
import { FitAddon } from '@xterm/addon-fit';
|
||||
import i18n from '@/lang';
|
||||
@@ -208,14 +195,6 @@ const form = reactive({
|
||||
showDefaultConn: false,
|
||||
defaultConn: '',
|
||||
});
|
||||
const aiForm = reactive({
|
||||
aiStatus: 'Disable',
|
||||
aiAccountId: '',
|
||||
aiPrefix: '',
|
||||
aiRiskCommands: [],
|
||||
aiRiskCommandsDefault: [],
|
||||
});
|
||||
|
||||
const resetConn = ref(false);
|
||||
const opRef = ref();
|
||||
|
||||
@@ -252,7 +231,6 @@ watch(
|
||||
|
||||
const acceptParams = () => {
|
||||
search(true);
|
||||
loadAISettings();
|
||||
loadConnShow();
|
||||
iniTerm();
|
||||
};
|
||||
@@ -298,21 +276,6 @@ const search = async (withReset?: boolean) => {
|
||||
});
|
||||
};
|
||||
|
||||
const loadAISettings = async () => {
|
||||
loading.value = true;
|
||||
await getAgentTerminalAIInfo()
|
||||
.then((res) => {
|
||||
aiForm.aiStatus = res.data.aiStatus || 'Disable';
|
||||
aiForm.aiAccountId = res.data.aiAccountId || '';
|
||||
aiForm.aiPrefix = res.data.aiPrefix || DEFAULT_AI_PREFIX;
|
||||
aiForm.aiRiskCommands = parseRiskCommands(res.data.aiRiskCommands || '');
|
||||
aiForm.aiRiskCommandsDefault = parseRiskCommands(res.data.aiRiskCommandsDefault || '');
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const loadConnShow = async () => {
|
||||
await loadLocalConn().then((res) => {
|
||||
form.showDefaultConn = res.data.localSSHConnShow === 'Enable';
|
||||
|
||||
@@ -110,12 +110,9 @@
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<div class="flex items-center gap-3 w-full py-2 flex-wrap">
|
||||
<el-button
|
||||
@click="cmdPanelVisible = !cmdPanelVisible"
|
||||
type="primary"
|
||||
class="min-w-[120px] max-w-[150px] shrink-0"
|
||||
>
|
||||
<div class="flex items-center gap-2 w-full py-2 flex-wrap">
|
||||
<AiSetting v-if="!mobile" class="shrink-0" />
|
||||
<el-button @click="cmdPanelVisible = !cmdPanelVisible" class="min-w-[120px] max-w-[150px] shrink-0">
|
||||
{{ $t('terminal.quickCommand') }}
|
||||
<el-icon class="ml-1">
|
||||
<component :is="cmdPanelVisible ? 'ArrowUp' : 'ArrowDown'" />
|
||||
@@ -283,6 +280,7 @@ import { GlobalStore } from '@/store';
|
||||
import router from '@/routers';
|
||||
import { getCommandTree } from '@/api/modules/command';
|
||||
import { getAgentSettingByKey } from '@/api/modules/setting';
|
||||
import AiSetting from '@/views/terminal/setting/ai/index.vue';
|
||||
|
||||
const dialogRef = ref();
|
||||
const ctx = getCurrentInstance() as any;
|
||||
|
||||
Reference in New Issue
Block a user