diff --git a/apps/web/package.json b/apps/web/package.json index 94ef1575..2e192a58 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -23,6 +23,8 @@ "@xterm/addon-serialize": "^0.14.0", "@xterm/xterm": "^6.0.0", "animate.css": "^4.1.1", + "cron-parser": "^5.5.0", + "cronstrue": "^3.14.0", "dotenv": "^17.2.3", "echarts": "^6.0.0", "katex": "^0.16.28", diff --git a/apps/web/src/i18n/locales/en.json b/apps/web/src/i18n/locales/en.json index a332e783..3e8f07cf 100644 --- a/apps/web/src/i18n/locales/en.json +++ b/apps/web/src/i18n/locales/en.json @@ -4,6 +4,7 @@ "cancel": "Cancel", "back": "Back", "save": "Save", + "create": "Create", "add": "Add", "edit": "Edit", "delete": "Delete", @@ -972,6 +973,8 @@ "compactionModel": "Compaction Model", "compactionModelDescription": "Select a model for summarization. Defaults to the bot's chat model if not set.", "compactionModelPlaceholder": "Use chat model (default)", + "showToolCallsInIM": "Show Tool Calls in IM", + "showToolCallsInIMDescription": "Surface tool execution status (running / completed / failed) as a single message in IM channels. Off by default.", "browserContext": "Browser Context", "browserContextPlaceholder": "Select browser context (disabled if empty)", "allowGuest": "Default ACL Effect", @@ -1240,7 +1243,86 @@ "updatedAt": "Updated", "statusEnabled": "Enabled", "statusDisabled": "Disabled", - "unlimited": "∞" + "unlimited": "∞", + "actions": "Actions", + "create": "New Schedule", + "edit": "Edit", + "delete": "Delete", + "deleteConfirm": "Delete schedule \"{name}\"? This cannot be undone.", + "deleteSuccess": "Schedule deleted", + "deleteFailed": "Failed to delete schedule", + "saveSuccess": "Schedule saved", + "saveFailed": "Failed to save schedule", + "form": { + "name": "Name", + "namePlaceholder": "e.g. Morning brief", + "description": "Description", + "descriptionPlaceholder": "What this schedule does", + "command": "Instruction", + "commandPlaceholder": "Tell the bot what to do when this fires", + "commandHint": "This will be delivered to the bot as a message each time the schedule triggers.", + "pattern": "Schedule", + "mode": "Mode", + "everyMinutes": "Every N minutes (1-59)", + "atMinute": "At minute of each hour (0-59)", + "hour": "Hour", + "hours": "Hours", + "hoursHint": "Click to select one or more hours of the day.", + "minute": "Minute", + "weekdays": "Days of week", + "monthDays": "Days of month", + "month": "Month", + "monthDay": "Day", + "maxCalls": "Run limit", + "maxCallsUnlimited": "Unlimited", + "enabled": "Enabled", + "patternPreview": "Cron pattern", + "nextRuns": "Next runs ({tz})", + "invalidPattern": "Pattern is invalid", + "advancedPattern": "Cron expression", + "advancedHint": "Standard cron: minute hour day-of-month month day-of-week. Descriptors like @daily are also accepted." + }, + "mode": { + "minutes": "Every N minutes", + "hourly": "Hourly", + "daily": "Daily", + "weekly": "Weekly", + "monthly": "Monthly", + "yearly": "Yearly", + "advanced": "Advanced (cron)" + }, + "modeHint": { + "minutes": "Trigger every N minutes.", + "hourly": "Trigger once every hour, at a given minute.", + "daily": "Trigger every day at the selected hours.", + "weekly": "Trigger on the selected days of the week.", + "monthly": "Trigger on the selected days of the month.", + "yearly": "Trigger once a year on a specific month and day.", + "advanced": "Write a raw cron expression for full control." + }, + "weekday": { + "sun": "Sun", + "mon": "Mon", + "tue": "Tue", + "wed": "Wed", + "thu": "Thu", + "fri": "Fri", + "sat": "Sat" + }, + "month": { + "jan": "January", + "feb": "February", + "mar": "March", + "apr": "April", + "may": "May", + "jun": "June", + "jul": "July", + "aug": "August", + "sep": "September", + "oct": "October", + "nov": "November", + "dec": "December" + } }, "history": { "title": "History", diff --git a/apps/web/src/i18n/locales/zh.json b/apps/web/src/i18n/locales/zh.json index c2b657bc..fddeba03 100644 --- a/apps/web/src/i18n/locales/zh.json +++ b/apps/web/src/i18n/locales/zh.json @@ -4,6 +4,7 @@ "cancel": "取消", "back": "返回", "save": "保存", + "create": "创建", "add": "添加", "edit": "编辑", "delete": "删除", @@ -968,6 +969,8 @@ "compactionModel": "压缩模型", "compactionModelDescription": "选择用于摘要的模型,未设置时默认使用聊天模型。", "compactionModelPlaceholder": "使用聊天模型(默认)", + "showToolCallsInIM": "在 IM 中显示工具调用", + "showToolCallsInIMDescription": "在 IM 频道中以单条消息持续展示工具执行的状态(进行中 / 已完成 / 失败)。默认关闭。", "browserContext": "浏览器上下文", "browserContextPlaceholder": "选择浏览器上下文(未配置时不启用)", "allowGuest": "ACL 默认行为", @@ -1236,7 +1239,86 @@ "updatedAt": "更新时间", "statusEnabled": "已启用", "statusDisabled": "已禁用", - "unlimited": "无限制" + "unlimited": "无限制", + "actions": "操作", + "create": "新建任务", + "edit": "编辑", + "delete": "删除", + "deleteConfirm": "确定删除任务 \"{name}\" 吗?此操作无法撤销。", + "deleteSuccess": "已删除", + "deleteFailed": "删除失败", + "saveSuccess": "已保存", + "saveFailed": "保存失败", + "form": { + "name": "名称", + "namePlaceholder": "例如:每日早报", + "description": "描述", + "descriptionPlaceholder": "这个任务做什么", + "command": "指令", + "commandPlaceholder": "每次触发时让 Bot 做什么", + "commandHint": "任务触发时,这段内容会作为消息发送给 Bot。", + "pattern": "调度规则", + "mode": "模式", + "everyMinutes": "每 N 分钟 (1-59)", + "atMinute": "每小时的第 M 分钟 (0-59)", + "hour": "小时", + "hours": "小时", + "hoursHint": "点击选择一个或多个小时。", + "minute": "分钟", + "weekdays": "星期", + "monthDays": "每月日期", + "month": "月份", + "monthDay": "日期", + "maxCalls": "运行次数限制", + "maxCallsUnlimited": "不限制", + "enabled": "启用", + "patternPreview": "Cron 表达式", + "nextRuns": "接下来的触发时间({tz})", + "invalidPattern": "表达式无效", + "advancedPattern": "Cron 表达式", + "advancedHint": "标准 cron 格式:分 时 日 月 星期。也支持 @daily 等描述符。" + }, + "mode": { + "minutes": "每 N 分钟", + "hourly": "每小时", + "daily": "每天", + "weekly": "每周", + "monthly": "每月", + "yearly": "每年", + "advanced": "高级(Cron)" + }, + "modeHint": { + "minutes": "每 N 分钟触发一次。", + "hourly": "每小时在指定分钟触发。", + "daily": "每天在选定的小时触发。", + "weekly": "在选定的星期几触发。", + "monthly": "在每月选定的日期触发。", + "yearly": "每年在指定月份和日期触发。", + "advanced": "直接编写 cron 表达式以获得完全控制。" + }, + "weekday": { + "sun": "日", + "mon": "一", + "tue": "二", + "wed": "三", + "thu": "四", + "fri": "五", + "sat": "六" + }, + "month": { + "jan": "一月", + "feb": "二月", + "mar": "三月", + "apr": "四月", + "may": "五月", + "jun": "六月", + "jul": "七月", + "aug": "八月", + "sep": "九月", + "oct": "十月", + "nov": "十一月", + "dec": "十二月" + } }, "history": { "title": "对话历史", diff --git a/apps/web/src/pages/bots/components/bot-schedule.vue b/apps/web/src/pages/bots/components/bot-schedule.vue index 0b29a926..95585c14 100644 --- a/apps/web/src/pages/bots/components/bot-schedule.vue +++ b/apps/web/src/pages/bots/components/bot-schedule.vue @@ -27,6 +27,13 @@ /> {{ $t('common.refresh') }} + @@ -49,9 +56,17 @@ class="size-6 text-muted-foreground" /> -

+

{{ $t('bots.schedule.empty') }}

+ @@ -72,12 +87,12 @@ {{ $t('bots.schedule.calls') }} - - {{ $t('bots.schedule.createdAt') }} - {{ $t('bots.schedule.updatedAt') }} + + {{ $t('bots.schedule.actions') }} + @@ -86,31 +101,71 @@ :key="item.id" class="border-b last:border-0 hover:bg-muted/30" > - +
{{ item.name }}
{{ item.description }}
- - + + {{ item.pattern }} +
+ {{ describeItem(item.pattern) }} +
- - - {{ item.enabled ? $t('bots.schedule.statusEnabled') : $t('bots.schedule.statusDisabled') }} - + +
+ + + {{ item.enabled ? $t('bots.schedule.statusEnabled') : $t('bots.schedule.statusDisabled') }} + +
- - {{ item.current_calls ?? 0 }} / {{ item.max_calls || $t('bots.schedule.unlimited') }} + + {{ item.current_calls ?? 0 }} / {{ formatMaxCalls(item) }} - - {{ formatDateTime(item.created_at) }} - - + {{ formatDateTime(item.updated_at) }} + +
+ + + + +
+ @@ -154,35 +209,58 @@ + + diff --git a/apps/web/src/pages/bots/components/schedule-form-dialog.vue b/apps/web/src/pages/bots/components/schedule-form-dialog.vue new file mode 100644 index 00000000..b87df2bc --- /dev/null +++ b/apps/web/src/pages/bots/components/schedule-form-dialog.vue @@ -0,0 +1,300 @@ +