diff --git a/agent/app/api/v2/agents.go b/agent/app/api/v2/agents.go index 96b814154..bff2e9010 100644 --- a/agent/app/api/v2/agents.go +++ b/agent/app/api/v2/agents.go @@ -394,6 +394,47 @@ func (b *BaseApi) UpdateAgentBrowserConfig(c *gin.Context) { helper.Success(c) } +// @Tags AI +// @Summary Get Agent Other config +// @Accept json +// @Param request body dto.AgentOtherConfigReq true "request" +// @Success 200 {object} dto.AgentOtherConfig +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /ai/agents/other/get [post] +func (b *BaseApi) GetAgentOtherConfig(c *gin.Context) { + var req dto.AgentOtherConfigReq + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + data, err := agentService.GetOtherConfig(req) + if err != nil { + helper.BadRequest(c, err) + return + } + helper.SuccessWithData(c, data) +} + +// @Tags AI +// @Summary Update Agent Other config +// @Accept json +// @Param request body dto.AgentOtherConfigUpdateReq true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /ai/agents/other/update [post] +func (b *BaseApi) UpdateAgentOtherConfig(c *gin.Context) { + var req dto.AgentOtherConfigUpdateReq + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + if err := agentService.UpdateOtherConfig(req); err != nil { + helper.BadRequest(c, err) + return + } + helper.Success(c) +} + // @Tags AI // @Summary Approve Agent Feishu pairing code // @Accept json diff --git a/agent/app/dto/agents.go b/agent/app/dto/agents.go index a5d472d49..8c66624a0 100644 --- a/agent/app/dto/agents.go +++ b/agent/app/dto/agents.go @@ -238,3 +238,16 @@ type AgentBrowserConfig struct { NoSandbox bool `json:"noSandbox"` DefaultProfile string `json:"defaultProfile"` } + +type AgentOtherConfigReq struct { + AgentID uint `json:"agentId" validate:"required"` +} + +type AgentOtherConfigUpdateReq struct { + AgentID uint `json:"agentId" validate:"required"` + UserTimezone string `json:"userTimezone" validate:"required"` +} + +type AgentOtherConfig struct { + UserTimezone string `json:"userTimezone"` +} diff --git a/agent/app/service/agents.go b/agent/app/service/agents.go index 5d1955e1c..e1adcb31a 100644 --- a/agent/app/service/agents.go +++ b/agent/app/service/agents.go @@ -49,6 +49,8 @@ type IAgentService interface { UpdateDiscordConfig(req dto.AgentDiscordConfigUpdateReq) error GetBrowserConfig(req dto.AgentBrowserConfigReq) (*dto.AgentBrowserConfig, error) UpdateBrowserConfig(req dto.AgentBrowserConfigUpdateReq) error + GetOtherConfig(req dto.AgentOtherConfigReq) (*dto.AgentOtherConfig, error) + UpdateOtherConfig(req dto.AgentOtherConfigUpdateReq) error ApproveChannelPairing(req dto.AgentChannelPairingApproveReq) error ApproveFeishuPairing(req dto.AgentFeishuPairingApproveReq) error } @@ -60,6 +62,7 @@ func NewIAgentService() IAgentService { const ( defaultBrowserExecutablePath = "/home/node/.cache/ms-playwright/chromium-1208/chrome-linux64/chrome" defaultBrowserProfile = "openclaw" + defaultUserTimezone = "Asia/Shanghai" ) func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) { @@ -767,6 +770,35 @@ func (a AgentService) UpdateBrowserConfig(req dto.AgentBrowserConfigUpdateReq) e return nil } +func (a AgentService) GetOtherConfig(req dto.AgentOtherConfigReq) (*dto.AgentOtherConfig, 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 := extractOtherConfig(conf) + return &result, nil +} + +func (a AgentService) UpdateOtherConfig(req dto.AgentOtherConfigUpdateReq) error { + agent, _, err := a.loadAgentAndInstall(req.AgentID) + if err != nil { + return err + } + conf, err := readOpenclawConfig(agent.ConfigPath) + if err != nil { + return err + } + setOtherConfig(conf, dto.AgentOtherConfig{UserTimezone: strings.TrimSpace(req.UserTimezone)}) + 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 { @@ -1038,6 +1070,32 @@ func setBrowserConfig(conf map[string]interface{}, config dto.AgentBrowserConfig } } +func extractOtherConfig(conf map[string]interface{}) dto.AgentOtherConfig { + result := dto.AgentOtherConfig{UserTimezone: resolveServerTimezone()} + agents, ok := conf["agents"].(map[string]interface{}) + if !ok { + return result + } + defaults, ok := agents["defaults"].(map[string]interface{}) + if !ok { + return result + } + if timezone, ok := defaults["userTimezone"].(string); ok && strings.TrimSpace(timezone) != "" { + result.UserTimezone = strings.TrimSpace(timezone) + } + return result +} + +func setOtherConfig(conf map[string]interface{}, config dto.AgentOtherConfig) { + agents := ensureChildMap(conf, "agents") + defaults := ensureChildMap(agents, "defaults") + timezone := strings.TrimSpace(config.UserTimezone) + if timezone == "" { + timezone = resolveServerTimezone() + } + defaults["userTimezone"] = timezone +} + func (a AgentService) syncAgentsByAccount(account *model.AgentAccount) error { agents, err := agentRepo.List(repo.WithByAccountID(account.ID)) if err != nil { @@ -1333,7 +1391,8 @@ type agentsConfig struct { } type agentDefaults struct { - Model modelRef `json:"model"` + UserTimezone string `json:"userTimezone,omitempty"` + Model modelRef `json:"model"` } type modelRef struct { @@ -1410,7 +1469,8 @@ func writeOpenclawConfig(confDir, provider, modelName, apiType string, maxTokens }, Agents: agentsConfig{ Defaults: agentDefaults{ - Model: modelRef{Primary: modelName}, + UserTimezone: resolveServerTimezone(), + Model: modelRef{Primary: modelName}, }, }, Browser: browserConfig{ @@ -1677,6 +1737,9 @@ func writeOpenclawConfig(confDir, provider, modelName, apiType string, maxTokens } agentsMap := ensureChildMap(conf, "agents") defaultsMap := ensureChildMap(agentsMap, "defaults") + if tz, ok := defaultsMap["userTimezone"]; !ok || strings.TrimSpace(fmt.Sprintf("%v", tz)) == "" { + defaultsMap["userTimezone"] = resolveServerTimezone() + } modelMap := ensureChildMap(defaultsMap, "model") modelMap["primary"] = cfg.Agents.Defaults.Model.Primary @@ -1700,6 +1763,17 @@ func writeOpenclawConfig(confDir, provider, modelName, apiType string, maxTokens return fileOp.SaveFile(envPath, content, 0600) } +func resolveServerTimezone() string { + timezone := strings.TrimSpace(common.LoadTimeZoneByCmd()) + if timezone == "" { + return defaultUserTimezone + } + if _, err := time.LoadLocation(timezone); err != nil { + return defaultUserTimezone + } + return timezone +} + func ensureChildMap(parent map[string]interface{}, key string) map[string]interface{} { if child, ok := parent[key].(map[string]interface{}); ok { return child diff --git a/agent/router/ro_ai.go b/agent/router/ro_ai.go index e29c8def8..e46583135 100644 --- a/agent/router/ro_ai.go +++ b/agent/router/ro_ai.go @@ -60,6 +60,8 @@ func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) { 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/other/get", baseApi.GetAgentOtherConfig) + aiToolsRouter.POST("/agents/other/update", baseApi.UpdateAgentOtherConfig) aiToolsRouter.POST("/agents/channel/pairing/approve", baseApi.ApproveAgentChannelPairing) } } diff --git a/frontend/src/api/interface/ai.ts b/frontend/src/api/interface/ai.ts index a91e70d65..5d988968f 100644 --- a/frontend/src/api/interface/ai.ts +++ b/frontend/src/api/interface/ai.ts @@ -472,4 +472,17 @@ export namespace AI { noSandbox: boolean; defaultProfile: string; } + + export interface AgentOtherConfigReq { + agentId: number; + } + + export interface AgentOtherConfig { + userTimezone: string; + } + + export interface AgentOtherConfigUpdateReq { + agentId: number; + userTimezone: string; + } } diff --git a/frontend/src/api/modules/ai.ts b/frontend/src/api/modules/ai.ts index 0d8b4b2df..cf48574ad 100644 --- a/frontend/src/api/modules/ai.ts +++ b/frontend/src/api/modules/ai.ts @@ -173,6 +173,14 @@ export const updateAgentBrowserConfig = (req: AI.AgentBrowserConfigUpdateReq) => return http.post(`/ai/agents/browser/update`, req); }; +export const getAgentOtherConfig = (req: AI.AgentOtherConfigReq) => { + return http.post(`/ai/agents/other/get`, req); +}; + +export const updateAgentOtherConfig = (req: AI.AgentOtherConfigUpdateReq) => { + return http.post(`/ai/agents/other/update`, req); +}; + export const approveAgentChannelPairing = (req: AI.AgentChannelPairingApproveReq) => { return http.post(`/ai/agents/channel/pairing/approve`, req); }; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index a78aada63..283a28a13 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -698,6 +698,8 @@ const message = { configTitle: 'Configuration', settingsTab: 'Settings', browserTab: 'Browser', + otherTab: 'Other', + timeZone: 'Time Zone', browserEnabled: 'Browser Enabled', headless: 'Headless', noSandbox: 'No Sandbox', diff --git a/frontend/src/lang/modules/es-es.ts b/frontend/src/lang/modules/es-es.ts index a18a5e3a4..fea5ec276 100644 --- a/frontend/src/lang/modules/es-es.ts +++ b/frontend/src/lang/modules/es-es.ts @@ -694,6 +694,8 @@ const message = { configTitle: 'Configuration', settingsTab: 'Settings', browserTab: 'Browser', + otherTab: 'Other', + timeZone: 'Zona horaria', browserEnabled: 'Browser Enabled', headless: 'Headless', noSandbox: 'No Sandbox', diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index bce7f9149..703384568 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -686,6 +686,8 @@ const message = { configTitle: 'Configuration', settingsTab: 'Settings', browserTab: 'Browser', + otherTab: 'Other', + timeZone: 'タイムゾーン', browserEnabled: 'Browser Enabled', headless: 'Headless', noSandbox: 'No Sandbox', diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index d63ab6398..c106a7254 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -681,6 +681,8 @@ const message = { configTitle: 'Configuration', settingsTab: 'Settings', browserTab: 'Browser', + otherTab: 'Other', + timeZone: '시간대', browserEnabled: 'Browser Enabled', headless: 'Headless', noSandbox: 'No Sandbox', diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index 2ae0afe6d..3008391bc 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -697,6 +697,8 @@ const message = { configTitle: 'Configuration', settingsTab: 'Settings', browserTab: 'Browser', + otherTab: 'Other', + timeZone: 'Zon Waktu', browserEnabled: 'Browser Enabled', headless: 'Headless', noSandbox: 'No Sandbox', diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index a6bbd91fe..7907ad982 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -694,6 +694,8 @@ const message = { configTitle: 'Configuration', settingsTab: 'Settings', browserTab: 'Browser', + otherTab: 'Other', + timeZone: 'Fuso horário', browserEnabled: 'Browser Enabled', headless: 'Headless', noSandbox: 'No Sandbox', diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index a71424c08..689a31a46 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -689,6 +689,8 @@ const message = { configTitle: 'Configuration', settingsTab: 'Settings', browserTab: 'Browser', + otherTab: 'Other', + timeZone: 'Часовой пояс', browserEnabled: 'Browser Enabled', headless: 'Headless', noSandbox: 'No Sandbox', diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index 74d7bb642..85e999dae 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -703,6 +703,8 @@ const message = { configTitle: 'Configuration', settingsTab: 'Settings', browserTab: 'Browser', + otherTab: 'Other', + timeZone: 'Saat Dilimi', browserEnabled: 'Browser Enabled', headless: 'Headless', noSandbox: 'No Sandbox', diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index 75b25602e..ca0db7507 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -672,6 +672,8 @@ const message = { configTitle: '設定', settingsTab: '設定', browserTab: '瀏覽器', + otherTab: '其他', + timeZone: '時區', browserEnabled: '瀏覽器開關', headless: '無頭模式', noSandbox: '禁用沙箱', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 556a7a8de..febbf38fc 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -656,6 +656,8 @@ const message = { configTitle: '配置', settingsTab: '设置', browserTab: '浏览器', + otherTab: '其他', + timeZone: '时区', browserEnabled: '浏览器开关', headless: '无头模式', noSandbox: '禁用沙箱', diff --git a/frontend/src/views/ai/agents/agent/config/tabs/settings.vue b/frontend/src/views/ai/agents/agent/config/tabs/settings.vue index 1aa1e60da..f4b488e22 100644 --- a/frontend/src/views/ai/agents/agent/config/tabs/settings.vue +++ b/frontend/src/views/ai/agents/agent/config/tabs/settings.vue @@ -3,6 +3,9 @@ + + + @@ -10,11 +13,13 @@ import { nextTick, ref } from 'vue'; import { useI18n } from 'vue-i18n'; import BrowserTab from './settings/browser.vue'; +import OtherTab from './settings/other.vue'; const { t } = useI18n(); const activeTab = ref('browser'); const agentId = ref(0); const browserRef = ref(); +const otherRef = ref(); const loadCurrentTab = async () => { if (agentId.value <= 0) { @@ -23,6 +28,10 @@ const loadCurrentTab = async () => { await nextTick(); if (activeTab.value === 'browser') { await browserRef.value?.load(agentId.value); + return; + } + if (activeTab.value === 'other') { + await otherRef.value?.load(agentId.value); } }; diff --git a/frontend/src/views/ai/agents/agent/config/tabs/settings/other.vue b/frontend/src/views/ai/agents/agent/config/tabs/settings/other.vue new file mode 100644 index 000000000..77ef11183 --- /dev/null +++ b/frontend/src/views/ai/agents/agent/config/tabs/settings/other.vue @@ -0,0 +1,68 @@ + + +