mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-23 00:00:52 +00:00
feat: add browser config for Agent (#11958)
This commit is contained in:
@@ -353,6 +353,47 @@ func (b *BaseApi) UpdateAgentDiscordConfig(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Get Agent Browser config
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentBrowserConfigReq true "request"
|
||||
// @Success 200 {object} dto.AgentBrowserConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/browser/get [post]
|
||||
func (b *BaseApi) GetAgentBrowserConfig(c *gin.Context) {
|
||||
var req dto.AgentBrowserConfigReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := agentService.GetBrowserConfig(req)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Update Agent Browser config
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentBrowserConfigUpdateReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/browser/update [post]
|
||||
func (b *BaseApi) UpdateAgentBrowserConfig(c *gin.Context) {
|
||||
var req dto.AgentBrowserConfigUpdateReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.UpdateBrowserConfig(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Approve Agent Feishu pairing code
|
||||
// @Accept json
|
||||
|
||||
@@ -216,3 +216,23 @@ type AgentDiscordConfig struct {
|
||||
Token string `json:"token"`
|
||||
Proxy string `json:"proxy"`
|
||||
}
|
||||
|
||||
type AgentBrowserConfigReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentBrowserConfigUpdateReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Headless bool `json:"headless"`
|
||||
NoSandbox bool `json:"noSandbox"`
|
||||
DefaultProfile string `json:"defaultProfile" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentBrowserConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
ExecutablePath string `json:"executablePath"`
|
||||
Headless bool `json:"headless"`
|
||||
NoSandbox bool `json:"noSandbox"`
|
||||
DefaultProfile string `json:"defaultProfile"`
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ type IAgentService interface {
|
||||
UpdateTelegramConfig(req dto.AgentTelegramConfigUpdateReq) error
|
||||
GetDiscordConfig(req dto.AgentDiscordConfigReq) (*dto.AgentDiscordConfig, error)
|
||||
UpdateDiscordConfig(req dto.AgentDiscordConfigUpdateReq) error
|
||||
GetBrowserConfig(req dto.AgentBrowserConfigReq) (*dto.AgentBrowserConfig, error)
|
||||
UpdateBrowserConfig(req dto.AgentBrowserConfigUpdateReq) error
|
||||
ApproveChannelPairing(req dto.AgentChannelPairingApproveReq) error
|
||||
ApproveFeishuPairing(req dto.AgentFeishuPairingApproveReq) error
|
||||
}
|
||||
@@ -55,6 +57,11 @@ func NewIAgentService() IAgentService {
|
||||
return &AgentService{}
|
||||
}
|
||||
|
||||
const (
|
||||
defaultBrowserExecutablePath = "/home/node/.cache/ms-playwright/chromium-1208/chrome-linux64/chrome"
|
||||
defaultBrowserProfile = "openclaw"
|
||||
)
|
||||
|
||||
func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
|
||||
provider := strings.ToLower(strings.TrimSpace(req.Provider))
|
||||
if !isSupportedAgentProvider(provider) {
|
||||
@@ -653,6 +660,41 @@ func (a AgentService) UpdateDiscordConfig(req dto.AgentDiscordConfigUpdateReq) e
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AgentService) GetBrowserConfig(req dto.AgentBrowserConfigReq) (*dto.AgentBrowserConfig, error) {
|
||||
agent, _, err := a.loadAgentAndInstall(req.AgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conf, err := readOpenclawConfig(agent.ConfigPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := extractBrowserConfig(conf)
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (a AgentService) UpdateBrowserConfig(req dto.AgentBrowserConfigUpdateReq) error {
|
||||
agent, _, err := a.loadAgentAndInstall(req.AgentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conf, err := readOpenclawConfig(agent.ConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
setBrowserConfig(conf, dto.AgentBrowserConfig{
|
||||
Enabled: req.Enabled,
|
||||
ExecutablePath: defaultBrowserExecutablePath,
|
||||
Headless: req.Headless,
|
||||
NoSandbox: req.NoSandbox,
|
||||
DefaultProfile: strings.TrimSpace(req.DefaultProfile),
|
||||
})
|
||||
if err := writeOpenclawConfigRaw(agent.ConfigPath, conf); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AgentService) ApproveChannelPairing(req dto.AgentChannelPairingApproveReq) error {
|
||||
_, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||
if err != nil {
|
||||
@@ -887,6 +929,49 @@ func setDiscordConfig(conf map[string]interface{}, config dto.AgentDiscordConfig
|
||||
delete(discord, "dm")
|
||||
}
|
||||
|
||||
func extractBrowserConfig(conf map[string]interface{}) dto.AgentBrowserConfig {
|
||||
result := dto.AgentBrowserConfig{
|
||||
Enabled: true,
|
||||
ExecutablePath: defaultBrowserExecutablePath,
|
||||
Headless: true,
|
||||
NoSandbox: true,
|
||||
DefaultProfile: defaultBrowserProfile,
|
||||
}
|
||||
browser, ok := conf["browser"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
if enabled, ok := browser["enabled"].(bool); ok {
|
||||
result.Enabled = enabled
|
||||
}
|
||||
if executablePath, ok := browser["executablePath"].(string); ok && strings.TrimSpace(executablePath) != "" {
|
||||
result.ExecutablePath = executablePath
|
||||
}
|
||||
if headless, ok := browser["headless"].(bool); ok {
|
||||
result.Headless = headless
|
||||
}
|
||||
if noSandbox, ok := browser["noSandbox"].(bool); ok {
|
||||
result.NoSandbox = noSandbox
|
||||
}
|
||||
if defaultProfile, ok := browser["defaultProfile"].(string); ok && strings.TrimSpace(defaultProfile) != "" {
|
||||
result.DefaultProfile = defaultProfile
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func setBrowserConfig(conf map[string]interface{}, config dto.AgentBrowserConfig) {
|
||||
browser := ensureChildMap(conf, "browser")
|
||||
browser["enabled"] = config.Enabled
|
||||
browser["executablePath"] = defaultBrowserExecutablePath
|
||||
browser["headless"] = config.Headless
|
||||
browser["noSandbox"] = config.NoSandbox
|
||||
if strings.TrimSpace(config.DefaultProfile) == "" {
|
||||
browser["defaultProfile"] = defaultBrowserProfile
|
||||
} else {
|
||||
browser["defaultProfile"] = strings.TrimSpace(config.DefaultProfile)
|
||||
}
|
||||
}
|
||||
|
||||
func (a AgentService) syncAgentsByAccount(account *model.AgentAccount) error {
|
||||
agents, err := agentRepo.List(repo.WithByAccountID(account.ID))
|
||||
if err != nil {
|
||||
@@ -1112,6 +1197,7 @@ func (a AgentService) writeConfigWithRetry(appInstall *model.AppInstall, provide
|
||||
type openclawConfig struct {
|
||||
Gateway gatewayConfig `json:"gateway"`
|
||||
Agents agentsConfig `json:"agents"`
|
||||
Browser browserConfig `json:"browser"`
|
||||
Models *modelsConfig `json:"models,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1174,6 +1260,14 @@ type modelCost struct {
|
||||
CacheWrite float64 `json:"cacheWrite"`
|
||||
}
|
||||
|
||||
type browserConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
ExecutablePath string `json:"executablePath"`
|
||||
Headless bool `json:"headless"`
|
||||
NoSandbox bool `json:"noSandbox"`
|
||||
DefaultProfile string `json:"defaultProfile"`
|
||||
}
|
||||
|
||||
func writeOpenclawConfig(confDir, provider, modelName, apiType string, maxTokens, contextWindow int, baseURL, apiKey, token string) error {
|
||||
if strings.TrimSpace(confDir) == "" {
|
||||
return fmt.Errorf("config dir is required")
|
||||
@@ -1210,6 +1304,13 @@ func writeOpenclawConfig(confDir, provider, modelName, apiType string, maxTokens
|
||||
Model: modelRef{Primary: modelName},
|
||||
},
|
||||
},
|
||||
Browser: browserConfig{
|
||||
Enabled: true,
|
||||
ExecutablePath: defaultBrowserExecutablePath,
|
||||
Headless: true,
|
||||
NoSandbox: true,
|
||||
DefaultProfile: defaultBrowserProfile,
|
||||
},
|
||||
}
|
||||
|
||||
provider = strings.ToLower(strings.TrimSpace(provider))
|
||||
@@ -1431,6 +1532,13 @@ func writeOpenclawConfig(confDir, provider, modelName, apiType string, maxTokens
|
||||
}
|
||||
conf["models"] = modelsMap
|
||||
}
|
||||
if _, ok := conf["browser"]; !ok {
|
||||
browserMap, err := structToMap(cfg.Browser)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conf["browser"] = browserMap
|
||||
}
|
||||
agentsMap := ensureChildMap(conf, "agents")
|
||||
defaultsMap := ensureChildMap(agentsMap, "defaults")
|
||||
modelMap := ensureChildMap(defaultsMap, "model")
|
||||
|
||||
@@ -58,6 +58,8 @@ func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
aiToolsRouter.POST("/agents/channel/telegram/update", baseApi.UpdateAgentTelegramConfig)
|
||||
aiToolsRouter.POST("/agents/channel/discord/get", baseApi.GetAgentDiscordConfig)
|
||||
aiToolsRouter.POST("/agents/channel/discord/update", baseApi.UpdateAgentDiscordConfig)
|
||||
aiToolsRouter.POST("/agents/browser/get", baseApi.GetAgentBrowserConfig)
|
||||
aiToolsRouter.POST("/agents/browser/update", baseApi.UpdateAgentBrowserConfig)
|
||||
aiToolsRouter.POST("/agents/channel/pairing/approve", baseApi.ApproveAgentChannelPairing)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,4 +450,24 @@ export namespace AI {
|
||||
token: string;
|
||||
proxy: string;
|
||||
}
|
||||
|
||||
export interface AgentBrowserConfigReq {
|
||||
agentId: number;
|
||||
}
|
||||
|
||||
export interface AgentBrowserConfig {
|
||||
enabled: boolean;
|
||||
executablePath: string;
|
||||
headless: boolean;
|
||||
noSandbox: boolean;
|
||||
defaultProfile: string;
|
||||
}
|
||||
|
||||
export interface AgentBrowserConfigUpdateReq {
|
||||
agentId: number;
|
||||
enabled: boolean;
|
||||
headless: boolean;
|
||||
noSandbox: boolean;
|
||||
defaultProfile: string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +164,14 @@ export const updateAgentDiscordConfig = (req: AI.AgentDiscordConfigUpdateReq) =>
|
||||
return http.post(`/ai/agents/channel/discord/update`, req);
|
||||
};
|
||||
|
||||
export const getAgentBrowserConfig = (req: AI.AgentBrowserConfigReq) => {
|
||||
return http.post<AI.AgentBrowserConfig>(`/ai/agents/browser/get`, req);
|
||||
};
|
||||
|
||||
export const updateAgentBrowserConfig = (req: AI.AgentBrowserConfigUpdateReq) => {
|
||||
return http.post(`/ai/agents/browser/update`, req);
|
||||
};
|
||||
|
||||
export const approveAgentChannelPairing = (req: AI.AgentChannelPairingApproveReq) => {
|
||||
return http.post(`/ai/agents/channel/pairing/approve`, req);
|
||||
};
|
||||
|
||||
@@ -698,6 +698,13 @@ const message = {
|
||||
manualModel: 'Manual input',
|
||||
verified: 'Verified',
|
||||
configTitle: 'Configuration',
|
||||
settingsTab: 'Settings',
|
||||
browserTab: 'Browser',
|
||||
browserEnabled: 'Browser Enabled',
|
||||
headless: 'Headless',
|
||||
noSandbox: 'No Sandbox',
|
||||
defaultProfile: 'Default Profile',
|
||||
executablePath: 'Executable Path',
|
||||
switchModelSuccess: 'Model switched successfully',
|
||||
channelsTab: 'Channels',
|
||||
feishu: 'Feishu',
|
||||
|
||||
@@ -694,6 +694,13 @@ const message = {
|
||||
manualModel: 'Entrada manual de modelo',
|
||||
verified: 'Verificado',
|
||||
configTitle: 'Configuration',
|
||||
settingsTab: 'Settings',
|
||||
browserTab: 'Browser',
|
||||
browserEnabled: 'Browser Enabled',
|
||||
headless: 'Headless',
|
||||
noSandbox: 'No Sandbox',
|
||||
defaultProfile: 'Default Profile',
|
||||
executablePath: 'Executable Path',
|
||||
switchModelSuccess: 'Model switched successfully',
|
||||
channelsTab: 'Channels',
|
||||
feishu: 'Feishu',
|
||||
|
||||
@@ -683,6 +683,13 @@ const message = {
|
||||
manualModel: '手動入力',
|
||||
verified: '検証済み',
|
||||
configTitle: 'Configuration',
|
||||
settingsTab: 'Settings',
|
||||
browserTab: 'Browser',
|
||||
browserEnabled: 'Browser Enabled',
|
||||
headless: 'Headless',
|
||||
noSandbox: 'No Sandbox',
|
||||
defaultProfile: 'Default Profile',
|
||||
executablePath: 'Executable Path',
|
||||
switchModelSuccess: 'Model switched successfully',
|
||||
channelsTab: 'Channels',
|
||||
feishu: 'Feishu',
|
||||
|
||||
@@ -680,6 +680,13 @@ const message = {
|
||||
manualModel: '수동 입력',
|
||||
verified: '검증됨',
|
||||
configTitle: 'Configuration',
|
||||
settingsTab: 'Settings',
|
||||
browserTab: 'Browser',
|
||||
browserEnabled: 'Browser Enabled',
|
||||
headless: 'Headless',
|
||||
noSandbox: 'No Sandbox',
|
||||
defaultProfile: 'Default Profile',
|
||||
executablePath: 'Executable Path',
|
||||
switchModelSuccess: 'Model switched successfully',
|
||||
channelsTab: 'Channels',
|
||||
feishu: 'Feishu',
|
||||
|
||||
@@ -695,6 +695,13 @@ const message = {
|
||||
manualModel: 'Input manual',
|
||||
verified: 'Disahkan',
|
||||
configTitle: 'Configuration',
|
||||
settingsTab: 'Settings',
|
||||
browserTab: 'Browser',
|
||||
browserEnabled: 'Browser Enabled',
|
||||
headless: 'Headless',
|
||||
noSandbox: 'No Sandbox',
|
||||
defaultProfile: 'Default Profile',
|
||||
executablePath: 'Executable Path',
|
||||
switchModelSuccess: 'Model switched successfully',
|
||||
channelsTab: 'Channels',
|
||||
feishu: 'Feishu',
|
||||
|
||||
@@ -692,6 +692,13 @@ const message = {
|
||||
manualModel: 'Entrada manual',
|
||||
verified: 'Verificado',
|
||||
configTitle: 'Configuration',
|
||||
settingsTab: 'Settings',
|
||||
browserTab: 'Browser',
|
||||
browserEnabled: 'Browser Enabled',
|
||||
headless: 'Headless',
|
||||
noSandbox: 'No Sandbox',
|
||||
defaultProfile: 'Default Profile',
|
||||
executablePath: 'Executable Path',
|
||||
switchModelSuccess: 'Model switched successfully',
|
||||
channelsTab: 'Channels',
|
||||
feishu: 'Feishu',
|
||||
|
||||
@@ -688,6 +688,13 @@ const message = {
|
||||
manualModel: 'Ручной ввод',
|
||||
verified: 'Проверено',
|
||||
configTitle: 'Configuration',
|
||||
settingsTab: 'Settings',
|
||||
browserTab: 'Browser',
|
||||
browserEnabled: 'Browser Enabled',
|
||||
headless: 'Headless',
|
||||
noSandbox: 'No Sandbox',
|
||||
defaultProfile: 'Default Profile',
|
||||
executablePath: 'Executable Path',
|
||||
switchModelSuccess: 'Model switched successfully',
|
||||
channelsTab: 'Channels',
|
||||
feishu: 'Feishu',
|
||||
|
||||
@@ -702,6 +702,13 @@ const message = {
|
||||
manualModel: 'Manuel giriş',
|
||||
verified: 'Doğrulandı',
|
||||
configTitle: 'Configuration',
|
||||
settingsTab: 'Settings',
|
||||
browserTab: 'Browser',
|
||||
browserEnabled: 'Browser Enabled',
|
||||
headless: 'Headless',
|
||||
noSandbox: 'No Sandbox',
|
||||
defaultProfile: 'Default Profile',
|
||||
executablePath: 'Executable Path',
|
||||
switchModelSuccess: 'Model switched successfully',
|
||||
channelsTab: 'Channels',
|
||||
feishu: 'Feishu',
|
||||
|
||||
@@ -670,6 +670,13 @@ const message = {
|
||||
manualModel: '手動輸入模型',
|
||||
verified: '驗證狀態',
|
||||
configTitle: '配置',
|
||||
settingsTab: '設定',
|
||||
browserTab: '瀏覽器',
|
||||
browserEnabled: '瀏覽器開關',
|
||||
headless: '無頭模式',
|
||||
noSandbox: '禁用沙箱',
|
||||
defaultProfile: '預設配置檔',
|
||||
executablePath: '瀏覽器可執行路徑',
|
||||
switchModelSuccess: '模型切換成功',
|
||||
channelsTab: '聊天渠道',
|
||||
feishu: '飛書',
|
||||
|
||||
@@ -672,6 +672,13 @@ const message = {
|
||||
manualModel: '手动输入模型',
|
||||
verified: '验证状态',
|
||||
configTitle: '配置',
|
||||
settingsTab: '设置',
|
||||
browserTab: '浏览器',
|
||||
browserEnabled: '浏览器开关',
|
||||
headless: '无头模式',
|
||||
noSandbox: '禁用沙箱',
|
||||
defaultProfile: '默认配置文件',
|
||||
executablePath: '浏览器可执行路径',
|
||||
switchModelSuccess: '模型切换成功',
|
||||
channelsTab: '聊天渠道',
|
||||
feishu: '飞书',
|
||||
|
||||
@@ -39,9 +39,7 @@
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="manualModel">{{ $t('aiTools.agents.manualModel') }}</el-checkbox>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('aiTools.agents.account')" prop="accountId">
|
||||
<el-select v-model="form.accountId" @change="handleAccountChange">
|
||||
<el-option v-for="item in accountOptions" :key="item.id" :label="item.name" :value="item.id" />
|
||||
@@ -53,6 +51,9 @@
|
||||
</el-button>
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="manualModel">{{ $t('aiTools.agents.manualModel') }}</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('aiTools.model.model')" prop="model">
|
||||
<el-input v-if="manualModel" v-model="form.model" />
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
<DrawerPro v-model="open" :header="header" size="large" @close="handleClose">
|
||||
<template #content>
|
||||
<el-tabs v-model="activeTab" tab-position="left" class="config-tabs" @tab-click="handleTabClick">
|
||||
<el-tab-pane :label="t('aiTools.agents.settingsTab')" name="settings">
|
||||
<SettingsTab ref="settingsRef" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="t('aiTools.model.model')" name="model">
|
||||
<ModelTab ref="modelRef" @updated="handleModelUpdated" />
|
||||
</el-tab-pane>
|
||||
@@ -20,16 +23,26 @@ import { useI18n } from 'vue-i18n';
|
||||
import { AI } from '@/api/interface/ai';
|
||||
import ChannelsTab from './tabs/channels.vue';
|
||||
import ModelTab from './tabs/model.vue';
|
||||
import SettingsTab from './tabs/settings.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const emit = defineEmits(['updated']);
|
||||
const open = ref(false);
|
||||
const activeTab = ref('model');
|
||||
const activeTab = ref('settings');
|
||||
const header = ref('');
|
||||
const agentId = ref(0);
|
||||
const currentAgent = ref<AI.AgentItem>();
|
||||
const channelsRef = ref();
|
||||
const modelRef = ref();
|
||||
const settingsRef = ref();
|
||||
|
||||
const loadSettings = async () => {
|
||||
if (agentId.value <= 0) {
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await settingsRef.value?.load(agentId.value);
|
||||
};
|
||||
|
||||
const loadModel = async () => {
|
||||
if (!currentAgent.value) {
|
||||
@@ -48,10 +61,13 @@ const loadChannels = async () => {
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
activeTab.value = 'model';
|
||||
activeTab.value = 'settings';
|
||||
};
|
||||
|
||||
const handleTabClick = async (pane: TabsPaneContext) => {
|
||||
if (pane.paneName === 'settings' && agentId.value > 0) {
|
||||
await loadSettings();
|
||||
}
|
||||
if (pane.paneName === 'model' && currentAgent.value) {
|
||||
await loadModel();
|
||||
}
|
||||
@@ -68,9 +84,9 @@ const openDrawer = async (agent: AI.AgentItem) => {
|
||||
agentId.value = agent.id;
|
||||
currentAgent.value = agent;
|
||||
header.value = `${agent.name} - ${t('menu.config')}`;
|
||||
activeTab.value = 'model';
|
||||
activeTab.value = 'settings';
|
||||
open.value = true;
|
||||
await loadModel();
|
||||
await loadSettings();
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
|
||||
<el-tab-pane :label="t('aiTools.agents.browserTab')" name="browser">
|
||||
<BrowserTab ref="browserRef" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { nextTick, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import BrowserTab from './settings/browser.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const activeTab = ref('browser');
|
||||
const agentId = ref(0);
|
||||
const browserRef = ref();
|
||||
|
||||
const loadCurrentTab = async () => {
|
||||
if (agentId.value <= 0) {
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
if (activeTab.value === 'browser') {
|
||||
await browserRef.value?.load(agentId.value);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTabClick = async () => {
|
||||
await loadCurrentTab();
|
||||
};
|
||||
|
||||
const load = async (id: number) => {
|
||||
agentId.value = id;
|
||||
activeTab.value = 'browser';
|
||||
await loadCurrentTab();
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
load,
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-position="top" v-loading="loading">
|
||||
<el-form-item :label="t('aiTools.agents.browserEnabled')">
|
||||
<el-switch v-model="form.enabled" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.headless')">
|
||||
<el-switch v-model="form.headless" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.noSandbox')">
|
||||
<el-switch v-model="form.noSandbox" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.defaultProfile')" prop="defaultProfile">
|
||||
<el-input v-model="form.defaultProfile" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.executablePath')">
|
||||
<el-input v-model="form.executablePath" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="saving" @click="saveConfig">
|
||||
{{ t('commons.button.save') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue';
|
||||
import type { FormInstance } from 'element-plus';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import { AI } from '@/api/interface/ai';
|
||||
import { getAgentBrowserConfig, updateAgentBrowserConfig } from '@/api/modules/ai';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
|
||||
const { t } = useI18n();
|
||||
const loading = ref(false);
|
||||
const saving = ref(false);
|
||||
const agentId = ref(0);
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const form = reactive<AI.AgentBrowserConfig>({
|
||||
enabled: true,
|
||||
executablePath: '',
|
||||
headless: true,
|
||||
noSandbox: true,
|
||||
defaultProfile: 'openclaw',
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
defaultProfile: [Rules.requiredInput],
|
||||
});
|
||||
|
||||
const load = async (id: number) => {
|
||||
agentId.value = id;
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getAgentBrowserConfig({ agentId: id });
|
||||
Object.assign(form, res.data || {});
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const saveConfig = async () => {
|
||||
if (!agentId.value || !formRef.value) {
|
||||
return;
|
||||
}
|
||||
await formRef.value.validate();
|
||||
saving.value = true;
|
||||
try {
|
||||
await updateAgentBrowserConfig({
|
||||
agentId: agentId.value,
|
||||
enabled: form.enabled,
|
||||
headless: form.headless,
|
||||
noSandbox: form.noSandbox,
|
||||
defaultProfile: form.defaultProfile,
|
||||
});
|
||||
MsgSuccess(t('aiTools.agents.saveSuccess'));
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
load,
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user