@lobehub/lobehub 2.0.0-next.275 → 2.0.0-next.277
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/changelog/v1.json +14 -0
- package/locales/en-US/setting.json +11 -0
- package/locales/zh-CN/setting.json +11 -0
- package/package.json +1 -1
- package/packages/builtin-tool-group-agent-builder/src/client/Inspector/BatchCreateAgents/index.tsx +2 -2
- package/packages/builtin-tool-group-agent-builder/src/client/Inspector/UpdateGroup/index.tsx +56 -56
- package/packages/builtin-tool-group-agent-builder/src/client/Render/BatchCreateAgents.tsx +3 -2
- package/packages/builtin-tool-group-agent-builder/src/executor.ts +2 -1
- package/packages/types/src/agentCronJob/index.ts +19 -23
- package/packages/types/src/serverConfig.ts +1 -0
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Cron/Actions.tsx +31 -0
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Cron/CronTopicGroup.tsx +10 -6
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Cron/index.tsx +7 -11
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Cron/useDropdownMenu.tsx +102 -0
- package/src/app/[variants]/(main)/agent/cron/[cronId]/CronConfig.ts +179 -0
- package/src/app/[variants]/(main)/agent/cron/[cronId]/features/CronJobContentEditor.tsx +111 -0
- package/src/app/[variants]/(main)/agent/cron/[cronId]/features/CronJobHeader.tsx +45 -0
- package/src/app/[variants]/(main)/agent/cron/[cronId]/features/CronJobSaveButton.tsx +31 -0
- package/src/app/[variants]/(main)/agent/cron/[cronId]/features/CronJobScheduleConfig.tsx +213 -0
- package/src/app/[variants]/(main)/agent/cron/[cronId]/index.tsx +186 -344
- package/src/app/[variants]/(main)/agent/features/Conversation/index.tsx +8 -2
- package/src/app/[variants]/(main)/agent/features/Portal/_layout/Mobile.tsx +1 -0
- package/src/app/[variants]/(main)/agent/features/Portal/features/Portal.tsx +4 -2
- package/src/app/[variants]/(main)/agent/profile/features/AgentCronJobs/index.tsx +42 -97
- package/src/app/[variants]/(main)/agent/profile/features/ProfileEditor/index.tsx +4 -20
- package/src/app/[variants]/(main)/community/features/UserAvatar/index.tsx +15 -5
- package/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/AgentProfilePopup.tsx +1 -6
- package/src/app/[variants]/(main)/group/features/Portal/_layout/Mobile.tsx +1 -0
- package/src/app/[variants]/(main)/group/features/Portal/features/Portal.tsx +4 -2
- package/src/hooks/useYamlArguments.ts +11 -8
- package/src/server/globalConfig/index.ts +1 -0
- package/src/services/chatGroup/index.ts +1 -4
- package/src/store/serverConfig/selectors.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,49 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.277](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.276...v2.0.0-next.277)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2026-01-13**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **misc**: Update the community user layout action button, update the cron job visiual way.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's improved
|
|
19
|
+
|
|
20
|
+
- **misc**: Update the community user layout action button, closes [#11472](https://github.com/lobehub/lobe-chat/issues/11472) ([2dd6d42](https://github.com/lobehub/lobe-chat/commit/2dd6d42))
|
|
21
|
+
- **misc**: Update the cron job visiual way, closes [#11466](https://github.com/lobehub/lobe-chat/issues/11466) ([63d81de](https://github.com/lobehub/lobe-chat/commit/63d81de))
|
|
22
|
+
|
|
23
|
+
</details>
|
|
24
|
+
|
|
25
|
+
<div align="right">
|
|
26
|
+
|
|
27
|
+
[](#readme-top)
|
|
28
|
+
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
## [Version 2.0.0-next.276](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.275...v2.0.0-next.276)
|
|
32
|
+
|
|
33
|
+
<sup>Released on **2026-01-13**</sup>
|
|
34
|
+
|
|
35
|
+
<br/>
|
|
36
|
+
|
|
37
|
+
<details>
|
|
38
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
39
|
+
|
|
40
|
+
</details>
|
|
41
|
+
|
|
42
|
+
<div align="right">
|
|
43
|
+
|
|
44
|
+
[](#readme-top)
|
|
45
|
+
|
|
46
|
+
</div>
|
|
47
|
+
|
|
5
48
|
## [Version 2.0.0-next.275](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.274...v2.0.0-next.275)
|
|
6
49
|
|
|
7
50
|
<sup>Released on **2026-01-13**</sup>
|
package/changelog/v1.json
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"children": {
|
|
4
|
+
"features": [
|
|
5
|
+
"Update the community user layout action button, update the cron job visiual way."
|
|
6
|
+
]
|
|
7
|
+
},
|
|
8
|
+
"date": "2026-01-13",
|
|
9
|
+
"version": "2.0.0-next.277"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"children": {},
|
|
13
|
+
"date": "2026-01-13",
|
|
14
|
+
"version": "2.0.0-next.276"
|
|
15
|
+
},
|
|
2
16
|
{
|
|
3
17
|
"children": {
|
|
4
18
|
"features": [
|
|
@@ -3,10 +3,16 @@
|
|
|
3
3
|
"about.title": "About",
|
|
4
4
|
"advancedSettings": "Advanced Settings",
|
|
5
5
|
"agentCronJobs.addJob": "Add Scheduled Task",
|
|
6
|
+
"agentCronJobs.clearTopics": "Clear Execution Results",
|
|
7
|
+
"agentCronJobs.clearTopicsFailed": "Failed to clear execution results",
|
|
8
|
+
"agentCronJobs.confirmClearTopics": "Are you sure you want to clear {{count}} execution result(s)? This action cannot be undone.",
|
|
6
9
|
"agentCronJobs.confirmDelete": "Are you sure you want to delete this scheduled task?",
|
|
10
|
+
"agentCronJobs.confirmDeleteCronJob": "Are you sure you want to delete this scheduled task and all its execution results? This action cannot be undone.",
|
|
7
11
|
"agentCronJobs.content": "Task Content",
|
|
8
12
|
"agentCronJobs.create": "Create",
|
|
9
13
|
"agentCronJobs.createSuccess": "Scheduled task created successfully",
|
|
14
|
+
"agentCronJobs.deleteCronJob": "Delete Scheduled Task",
|
|
15
|
+
"agentCronJobs.deleteFailed": "Failed to delete scheduled task",
|
|
10
16
|
"agentCronJobs.deleteJob": "Delete Task",
|
|
11
17
|
"agentCronJobs.deleteSuccess": "Scheduled task deleted successfully",
|
|
12
18
|
"agentCronJobs.description": "Automate your agent with scheduled executions",
|
|
@@ -25,6 +31,7 @@
|
|
|
25
31
|
"agentCronJobs.form.validation.nameRequired": "Task name is required",
|
|
26
32
|
"agentCronJobs.interval.12hours": "Every 12 hours",
|
|
27
33
|
"agentCronJobs.interval.1hour": "Every hour",
|
|
34
|
+
"agentCronJobs.interval.2hours": "Every 2 hours",
|
|
28
35
|
"agentCronJobs.interval.30min": "Every 30 minutes",
|
|
29
36
|
"agentCronJobs.interval.6hours": "Every 6 hours",
|
|
30
37
|
"agentCronJobs.interval.daily": "Daily",
|
|
@@ -36,7 +43,11 @@
|
|
|
36
43
|
"agentCronJobs.noExecutionResults": "No execution results",
|
|
37
44
|
"agentCronJobs.remainingExecutions": "Remaining: {{count}}",
|
|
38
45
|
"agentCronJobs.save": "Save",
|
|
46
|
+
"agentCronJobs.saveAsNew": "Save as New Scheduled Task",
|
|
39
47
|
"agentCronJobs.schedule": "Schedule",
|
|
48
|
+
"agentCronJobs.scheduleType.daily": "Daily",
|
|
49
|
+
"agentCronJobs.scheduleType.hourly": "Hourly",
|
|
50
|
+
"agentCronJobs.scheduleType.weekly": "Weekly",
|
|
40
51
|
"agentCronJobs.status.depleted": "Depleted",
|
|
41
52
|
"agentCronJobs.status.disabled": "Disabled",
|
|
42
53
|
"agentCronJobs.status.enabled": "Enabled",
|
|
@@ -3,10 +3,16 @@
|
|
|
3
3
|
"about.title": "关于",
|
|
4
4
|
"advancedSettings": "进阶配置",
|
|
5
5
|
"agentCronJobs.addJob": "添加定时任务",
|
|
6
|
+
"agentCronJobs.clearTopics": "清空执行结果",
|
|
7
|
+
"agentCronJobs.clearTopicsFailed": "清空执行结果失败",
|
|
8
|
+
"agentCronJobs.confirmClearTopics": "确定要清空 {{count}} 条执行结果吗?此操作不可撤销。",
|
|
6
9
|
"agentCronJobs.confirmDelete": "确定要删除此定时任务吗?",
|
|
10
|
+
"agentCronJobs.confirmDeleteCronJob": "确定要删除此定时任务及其所有执行结果吗?此操作不可撤销。",
|
|
7
11
|
"agentCronJobs.content": "任务内容",
|
|
8
12
|
"agentCronJobs.create": "创建",
|
|
9
13
|
"agentCronJobs.createSuccess": "定时任务创建成功",
|
|
14
|
+
"agentCronJobs.deleteCronJob": "删除定时任务",
|
|
15
|
+
"agentCronJobs.deleteFailed": "删除定时任务失败",
|
|
10
16
|
"agentCronJobs.deleteJob": "删除任务",
|
|
11
17
|
"agentCronJobs.deleteSuccess": "定时任务删除成功",
|
|
12
18
|
"agentCronJobs.description": "通过定时执行自动化您的智能体",
|
|
@@ -25,6 +31,7 @@
|
|
|
25
31
|
"agentCronJobs.form.validation.nameRequired": "任务名称不能为空",
|
|
26
32
|
"agentCronJobs.interval.12hours": "每12小时",
|
|
27
33
|
"agentCronJobs.interval.1hour": "每小时",
|
|
34
|
+
"agentCronJobs.interval.2hours": "每2小时",
|
|
28
35
|
"agentCronJobs.interval.30min": "每30分钟",
|
|
29
36
|
"agentCronJobs.interval.6hours": "每6小时",
|
|
30
37
|
"agentCronJobs.interval.daily": "每日",
|
|
@@ -36,7 +43,11 @@
|
|
|
36
43
|
"agentCronJobs.noExecutionResults": "无执行结果",
|
|
37
44
|
"agentCronJobs.remainingExecutions": "剩余:{{count}}",
|
|
38
45
|
"agentCronJobs.save": "保存",
|
|
46
|
+
"agentCronJobs.saveAsNew": "保存为新定时任务",
|
|
39
47
|
"agentCronJobs.schedule": "计划",
|
|
48
|
+
"agentCronJobs.scheduleType.daily": "每日",
|
|
49
|
+
"agentCronJobs.scheduleType.hourly": "每小时",
|
|
50
|
+
"agentCronJobs.scheduleType.weekly": "每周",
|
|
40
51
|
"agentCronJobs.status.depleted": "已耗尽",
|
|
41
52
|
"agentCronJobs.status.disabled": "已禁用",
|
|
42
53
|
"agentCronJobs.status.enabled": "已启用",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.277",
|
|
4
4
|
"description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
package/packages/builtin-tool-group-agent-builder/src/client/Inspector/BatchCreateAgents/index.tsx
CHANGED
|
@@ -18,8 +18,8 @@ const styles = createStaticStyles(({ css, cssVar: cv }) => ({
|
|
|
18
18
|
align-items: center;
|
|
19
19
|
`,
|
|
20
20
|
count: css`
|
|
21
|
-
color: ${cv.colorTextSecondary};
|
|
22
21
|
font-size: 12px;
|
|
22
|
+
color: ${cv.colorTextSecondary};
|
|
23
23
|
`,
|
|
24
24
|
root: css`
|
|
25
25
|
overflow: hidden;
|
|
@@ -83,8 +83,8 @@ export const BatchCreateAgentsInspector = memo<
|
|
|
83
83
|
<div className={styles.avatarGroup}>
|
|
84
84
|
{displayInfo.displayAgents.map((agent, index) => (
|
|
85
85
|
<Avatar
|
|
86
|
-
key={index}
|
|
87
86
|
avatar={agent.avatar}
|
|
87
|
+
key={index}
|
|
88
88
|
shape={'square'}
|
|
89
89
|
size={20}
|
|
90
90
|
title={agent.title}
|
package/packages/builtin-tool-group-agent-builder/src/client/Inspector/UpdateGroup/index.tsx
CHANGED
|
@@ -17,70 +17,70 @@ const styles = createStaticStyles(({ css }) => ({
|
|
|
17
17
|
`,
|
|
18
18
|
}));
|
|
19
19
|
|
|
20
|
-
export const UpdateGroupInspector = memo<
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
export const UpdateGroupInspector = memo<
|
|
21
|
+
BuiltinInspectorProps<UpdateGroupParams, UpdateGroupState>
|
|
22
|
+
>(({ args, partialArgs, isArgumentsStreaming, isLoading, pluginState }) => {
|
|
23
|
+
const { t } = useTranslation('plugin');
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
const config = args?.config || partialArgs?.config;
|
|
26
|
+
const meta = args?.meta || partialArgs?.meta;
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
if (config?.openingQuestions !== undefined) {
|
|
35
|
-
fields.push(t('builtins.lobe-group-agent-builder.inspector.openingQuestions'));
|
|
36
|
-
}
|
|
37
|
-
// Meta fields
|
|
38
|
-
if (meta?.title !== undefined) {
|
|
39
|
-
fields.push(t('builtins.lobe-group-agent-builder.inspector.title'));
|
|
40
|
-
}
|
|
41
|
-
if (meta?.description !== undefined) {
|
|
42
|
-
fields.push(t('builtins.lobe-group-agent-builder.inspector.description'));
|
|
43
|
-
}
|
|
44
|
-
if (meta?.avatar !== undefined) {
|
|
45
|
-
fields.push(t('builtins.lobe-group-agent-builder.inspector.avatar'));
|
|
46
|
-
}
|
|
47
|
-
if (meta?.backgroundColor !== undefined) {
|
|
48
|
-
fields.push(t('builtins.lobe-group-agent-builder.inspector.backgroundColor'));
|
|
49
|
-
}
|
|
50
|
-
return fields.length > 0 ? fields.join(', ') : '';
|
|
51
|
-
}, [config, meta, t]);
|
|
52
|
-
|
|
53
|
-
// Initial streaming state
|
|
54
|
-
if (isArgumentsStreaming && !displayText) {
|
|
55
|
-
return (
|
|
56
|
-
<div className={cx(inspectorTextStyles.root, shinyTextStyles.shinyText)}>
|
|
57
|
-
<span>{t('builtins.lobe-group-agent-builder.apiName.updateGroup')}</span>
|
|
58
|
-
</div>
|
|
59
|
-
);
|
|
28
|
+
// Build display text from updated fields
|
|
29
|
+
const displayText = useMemo(() => {
|
|
30
|
+
const fields: string[] = [];
|
|
31
|
+
// Config fields
|
|
32
|
+
if (config?.openingMessage !== undefined) {
|
|
33
|
+
fields.push(t('builtins.lobe-group-agent-builder.inspector.openingMessage'));
|
|
60
34
|
}
|
|
35
|
+
if (config?.openingQuestions !== undefined) {
|
|
36
|
+
fields.push(t('builtins.lobe-group-agent-builder.inspector.openingQuestions'));
|
|
37
|
+
}
|
|
38
|
+
// Meta fields
|
|
39
|
+
if (meta?.title !== undefined) {
|
|
40
|
+
fields.push(t('builtins.lobe-group-agent-builder.inspector.title'));
|
|
41
|
+
}
|
|
42
|
+
if (meta?.description !== undefined) {
|
|
43
|
+
fields.push(t('builtins.lobe-group-agent-builder.inspector.description'));
|
|
44
|
+
}
|
|
45
|
+
if (meta?.avatar !== undefined) {
|
|
46
|
+
fields.push(t('builtins.lobe-group-agent-builder.inspector.avatar'));
|
|
47
|
+
}
|
|
48
|
+
if (meta?.backgroundColor !== undefined) {
|
|
49
|
+
fields.push(t('builtins.lobe-group-agent-builder.inspector.backgroundColor'));
|
|
50
|
+
}
|
|
51
|
+
return fields.length > 0 ? fields.join(', ') : '';
|
|
52
|
+
}, [config, meta, t]);
|
|
61
53
|
|
|
62
|
-
|
|
63
|
-
|
|
54
|
+
// Initial streaming state
|
|
55
|
+
if (isArgumentsStreaming && !displayText) {
|
|
64
56
|
return (
|
|
65
|
-
<div
|
|
66
|
-
className={cx(
|
|
67
|
-
inspectorTextStyles.root,
|
|
68
|
-
(isArgumentsStreaming || isLoading) && shinyTextStyles.shinyText,
|
|
69
|
-
)}
|
|
70
|
-
>
|
|
57
|
+
<div className={cx(inspectorTextStyles.root, shinyTextStyles.shinyText)}>
|
|
71
58
|
<span>{t('builtins.lobe-group-agent-builder.apiName.updateGroup')}</span>
|
|
72
|
-
{displayText && (
|
|
73
|
-
<>
|
|
74
|
-
: <span className={highlightTextStyles.primary}>{displayText}</span>
|
|
75
|
-
</>
|
|
76
|
-
)}
|
|
77
|
-
{!isLoading && isSuccess && (
|
|
78
|
-
<Check className={styles.statusIcon} color={cssVar.colorSuccess} size={14} />
|
|
79
|
-
)}
|
|
80
59
|
</div>
|
|
81
60
|
);
|
|
82
|
-
}
|
|
83
|
-
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const isSuccess = pluginState?.success;
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<div
|
|
67
|
+
className={cx(
|
|
68
|
+
inspectorTextStyles.root,
|
|
69
|
+
(isArgumentsStreaming || isLoading) && shinyTextStyles.shinyText,
|
|
70
|
+
)}
|
|
71
|
+
>
|
|
72
|
+
<span>{t('builtins.lobe-group-agent-builder.apiName.updateGroup')}</span>
|
|
73
|
+
{displayText && (
|
|
74
|
+
<>
|
|
75
|
+
: <span className={highlightTextStyles.primary}>{displayText}</span>
|
|
76
|
+
</>
|
|
77
|
+
)}
|
|
78
|
+
{!isLoading && isSuccess && (
|
|
79
|
+
<Check className={styles.statusIcon} color={cssVar.colorSuccess} size={14} />
|
|
80
|
+
)}
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
84
|
|
|
85
85
|
UpdateGroupInspector.displayName = 'UpdateGroupInspector';
|
|
86
86
|
|
|
@@ -11,9 +11,10 @@ import type { BatchCreateAgentsParams, BatchCreateAgentsState } from '../../type
|
|
|
11
11
|
|
|
12
12
|
const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
13
13
|
container: css`
|
|
14
|
-
padding: 4px
|
|
15
|
-
|
|
14
|
+
padding-block: 4px;
|
|
15
|
+
padding-inline: 16px;
|
|
16
16
|
border-radius: 8px;
|
|
17
|
+
background: ${cssVar.colorFillQuaternary};
|
|
17
18
|
`,
|
|
18
19
|
description: css`
|
|
19
20
|
overflow: hidden;
|
|
@@ -238,7 +238,8 @@ class GroupAgentBuilderExecutor extends BaseExecutor<typeof GroupAgentBuilderApi
|
|
|
238
238
|
|
|
239
239
|
if (!agentId) {
|
|
240
240
|
return {
|
|
241
|
-
content:
|
|
241
|
+
content:
|
|
242
|
+
'No agent found. Please provide an agentId or ensure supervisor context is available.',
|
|
242
243
|
error: { message: 'No agent found', type: 'NoAgentContext' },
|
|
243
244
|
success: false,
|
|
244
245
|
};
|
|
@@ -21,25 +21,6 @@ export const cronPatternSchema = z
|
|
|
21
21
|
// Minimum 30 minutes validation (using standard cron format)
|
|
22
22
|
export const minimumIntervalSchema = z.string().refine((pattern) => {
|
|
23
23
|
// Standard cron format: minute hour day month weekday
|
|
24
|
-
const allowedPatterns = [
|
|
25
|
-
'*/30 * * * *', // Every 30 minutes
|
|
26
|
-
'0 * * * *', // Every hour
|
|
27
|
-
'0 */2 * * *', // Every 2 hours
|
|
28
|
-
'0 */3 * * *', // Every 3 hours
|
|
29
|
-
'0 */4 * * *', // Every 4 hours
|
|
30
|
-
'0 */6 * * *', // Every 6 hours
|
|
31
|
-
'0 */8 * * *', // Every 8 hours
|
|
32
|
-
'0 */12 * * *', // Every 12 hours
|
|
33
|
-
'0 0 * * *', // Daily at midnight
|
|
34
|
-
'0 0 * * 0', // Weekly on Sunday
|
|
35
|
-
'0 0 1 * *', // Monthly on 1st
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
// Check if it matches allowed patterns
|
|
39
|
-
if (allowedPatterns.includes(pattern)) {
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
24
|
// Parse pattern to validate minimum 30-minute interval
|
|
44
25
|
const parts = pattern.split(' ');
|
|
45
26
|
if (parts.length !== 5) {
|
|
@@ -48,24 +29,39 @@ export const minimumIntervalSchema = z.string().refine((pattern) => {
|
|
|
48
29
|
|
|
49
30
|
const [minute, hour] = parts;
|
|
50
31
|
|
|
32
|
+
// Validate minute is 0 or 30 (we only allow 30-minute intervals)
|
|
33
|
+
const isValidMinute = minute === '0' || minute === '30' || minute === '*/30';
|
|
34
|
+
|
|
35
|
+
if (!isValidMinute) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
51
39
|
// Allow minute intervals >= 30 (e.g., */30, */45, */60)
|
|
52
40
|
if (minute.startsWith('*/')) {
|
|
53
41
|
const interval = parseInt(minute.slice(2));
|
|
54
42
|
if (!isNaN(interval) && interval >= 30) {
|
|
55
43
|
return true;
|
|
56
44
|
}
|
|
45
|
+
return false;
|
|
57
46
|
}
|
|
58
47
|
|
|
59
|
-
// Allow hourly patterns: 0 */N * * * where N >= 1
|
|
60
|
-
if (minute === '0' && hour.startsWith('*/')) {
|
|
48
|
+
// Allow hourly patterns: {0|30} */N * * * where N >= 1
|
|
49
|
+
if ((minute === '0' || minute === '30') && hour.startsWith('*/')) {
|
|
61
50
|
const interval = parseInt(hour.slice(2));
|
|
62
51
|
if (!isNaN(interval) && interval >= 1) {
|
|
63
52
|
return true;
|
|
64
53
|
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Allow hourly patterns: {0|30} * * * * (every hour at :00 or :30)
|
|
58
|
+
if ((minute === '0' || minute === '30') && hour === '*') {
|
|
59
|
+
return true;
|
|
65
60
|
}
|
|
66
61
|
|
|
67
|
-
// Allow specific hour patterns: 0 N * * * (runs once per day)
|
|
68
|
-
|
|
62
|
+
// Allow specific hour patterns: {0|30} N * * * (runs once per day)
|
|
63
|
+
// or {0|30} N * * {weekdays} (runs on specific weekdays)
|
|
64
|
+
if ((minute === '0' || minute === '30') && /^\d+$/.test(hour)) {
|
|
69
65
|
const h = parseInt(hour);
|
|
70
66
|
if (!isNaN(h) && h >= 0 && h <= 23) {
|
|
71
67
|
return true;
|
|
@@ -49,6 +49,7 @@ export type ServerLanguageModel = Partial<Record<GlobalLLMProviderKey, ServerMod
|
|
|
49
49
|
export interface GlobalServerConfig {
|
|
50
50
|
aiProvider: ServerLanguageModel;
|
|
51
51
|
defaultAgent?: PartialDeep<UserDefaultAgent>;
|
|
52
|
+
enableBusinessFeatures?: boolean;
|
|
52
53
|
enableEmailVerification?: boolean;
|
|
53
54
|
enableKlavis?: boolean;
|
|
54
55
|
enableLobehubSkill?: boolean;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ActionIcon, Dropdown } from '@lobehub/ui';
|
|
2
|
+
import { MoreHorizontal } from 'lucide-react';
|
|
3
|
+
import { memo } from 'react';
|
|
4
|
+
|
|
5
|
+
import { useCronJobDropdownMenu } from './useDropdownMenu';
|
|
6
|
+
|
|
7
|
+
interface ActionsProps {
|
|
8
|
+
cronJobId: string;
|
|
9
|
+
topics: Array<{ id: string }>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const Actions = memo<ActionsProps>(({ cronJobId, topics }) => {
|
|
13
|
+
const dropdownMenu = useCronJobDropdownMenu(cronJobId, topics);
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<Dropdown
|
|
17
|
+
arrow={false}
|
|
18
|
+
menu={{
|
|
19
|
+
items: dropdownMenu,
|
|
20
|
+
onClick: ({ domEvent }) => {
|
|
21
|
+
domEvent.stopPropagation();
|
|
22
|
+
},
|
|
23
|
+
}}
|
|
24
|
+
trigger={['click']}
|
|
25
|
+
>
|
|
26
|
+
<ActionIcon icon={MoreHorizontal} size={'small'} />
|
|
27
|
+
</Dropdown>
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export default Actions;
|
|
@@ -9,6 +9,7 @@ import { useParams } from 'react-router-dom';
|
|
|
9
9
|
import { useRouter } from '@/app/[variants]/(main)/hooks/useRouter';
|
|
10
10
|
import type { AgentCronJob } from '@/database/schemas/agentCronJob';
|
|
11
11
|
|
|
12
|
+
import Actions from './Actions';
|
|
12
13
|
import CronTopicItem from './CronTopicItem';
|
|
13
14
|
|
|
14
15
|
interface CronTopicGroupProps {
|
|
@@ -43,12 +44,15 @@ const CronTopicGroup = memo<CronTopicGroupProps>(({ cronJob, cronJobId, topics }
|
|
|
43
44
|
return (
|
|
44
45
|
<AccordionItem
|
|
45
46
|
action={
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
<Flexbox align="center" gap={4} horizontal>
|
|
48
|
+
<ActionIcon
|
|
49
|
+
icon={Settings2Icon}
|
|
50
|
+
onClick={handleOpenCronJob}
|
|
51
|
+
size="small"
|
|
52
|
+
title={t('agentCronJobs.editJob')}
|
|
53
|
+
/>
|
|
54
|
+
<Actions cronJobId={cronJobId} topics={topics} />
|
|
55
|
+
</Flexbox>
|
|
52
56
|
}
|
|
53
57
|
itemKey={cronJobId}
|
|
54
58
|
paddingBlock={4}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { ENABLE_BUSINESS_FEATURES } from '@lobechat/business-const';
|
|
4
3
|
import { Accordion, AccordionItem, ActionIcon, Flexbox, Text } from '@lobehub/ui';
|
|
5
4
|
import { Plus } from 'lucide-react';
|
|
6
5
|
import { memo, useCallback } from 'react';
|
|
@@ -12,6 +11,7 @@ import EmptyNavItem from '@/features/NavPanel/components/EmptyNavItem';
|
|
|
12
11
|
import SkeletonList from '@/features/NavPanel/components/SkeletonList';
|
|
13
12
|
import { useQueryRoute } from '@/hooks/useQueryRoute';
|
|
14
13
|
import { useAgentStore } from '@/store/agent';
|
|
14
|
+
import { serverConfigSelectors, useServerConfigStore } from '@/store/serverConfig';
|
|
15
15
|
|
|
16
16
|
import CronTopicGroup from './CronTopicGroup';
|
|
17
17
|
|
|
@@ -22,24 +22,20 @@ interface CronTopicListProps {
|
|
|
22
22
|
const CronTopicList = memo<CronTopicListProps>(({ itemKey }) => {
|
|
23
23
|
const { t } = useTranslation('setting');
|
|
24
24
|
const router = useQueryRoute();
|
|
25
|
-
const [agentId,
|
|
25
|
+
const [agentId, useFetchCronTopicsWithJobInfo] = useAgentStore((s) => [
|
|
26
26
|
s.activeAgentId,
|
|
27
|
-
s.createAgentCronJob,
|
|
28
27
|
s.useFetchCronTopicsWithJobInfo,
|
|
29
28
|
]);
|
|
30
29
|
const { data: cronTopicsGroupsWithJobInfo = [], isLoading } =
|
|
31
30
|
useFetchCronTopicsWithJobInfo(agentId);
|
|
31
|
+
const enableBusinessFeatures = useServerConfigStore(serverConfigSelectors.enableBusinessFeatures);
|
|
32
32
|
|
|
33
|
-
const handleCreateCronJob = useCallback(
|
|
33
|
+
const handleCreateCronJob = useCallback(() => {
|
|
34
34
|
if (!agentId) return;
|
|
35
|
+
router.push(urlJoin('/agent', agentId, 'cron', 'new'));
|
|
36
|
+
}, [agentId, router]);
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
if (cronJobId) {
|
|
38
|
-
router.push(urlJoin('/agent', agentId, 'cron', cronJobId));
|
|
39
|
-
}
|
|
40
|
-
}, [agentId, createAgentCronJob, router]);
|
|
41
|
-
|
|
42
|
-
if (!ENABLE_BUSINESS_FEATURES) return null;
|
|
38
|
+
if (!enableBusinessFeatures) return null;
|
|
43
39
|
|
|
44
40
|
const addAction = (
|
|
45
41
|
<ActionIcon
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Icon, type MenuProps } from '@lobehub/ui';
|
|
2
|
+
import { App } from 'antd';
|
|
3
|
+
import { Trash } from 'lucide-react';
|
|
4
|
+
import { useCallback, useMemo } from 'react';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
|
|
7
|
+
import { agentCronJobService } from '@/services/agentCronJob';
|
|
8
|
+
import { topicService } from '@/services/topic';
|
|
9
|
+
import { useAgentStore } from '@/store/agent';
|
|
10
|
+
|
|
11
|
+
export const useCronJobDropdownMenu = (
|
|
12
|
+
cronJobId: string,
|
|
13
|
+
topics: Array<{ id: string }>,
|
|
14
|
+
): MenuProps['items'] => {
|
|
15
|
+
const { t } = useTranslation(['setting', 'common']);
|
|
16
|
+
const { modal } = App.useApp();
|
|
17
|
+
|
|
18
|
+
const refreshCronTopics = useAgentStore((s) => s.internal_refreshCronTopics);
|
|
19
|
+
|
|
20
|
+
const handleDeleteCronJob = useCallback(async () => {
|
|
21
|
+
try {
|
|
22
|
+
// Delete all topics associated with this cron job
|
|
23
|
+
if (topics.length > 0) {
|
|
24
|
+
const topicIds = topics.map((t) => t.id);
|
|
25
|
+
await topicService.batchRemoveTopics(topicIds);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Delete the cron job
|
|
29
|
+
await agentCronJobService.delete(cronJobId);
|
|
30
|
+
|
|
31
|
+
// Refresh the cron topics list
|
|
32
|
+
await refreshCronTopics();
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error('Failed to delete cron job:', error);
|
|
35
|
+
modal.error({
|
|
36
|
+
content: t('agentCronJobs.deleteFailed' as any),
|
|
37
|
+
title: t('error' as any, { ns: 'common' }),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}, [cronJobId, topics, refreshCronTopics, modal, t]);
|
|
41
|
+
|
|
42
|
+
const handleClearTopics = useCallback(async () => {
|
|
43
|
+
if (topics.length === 0) return;
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const topicIds = topics.map((t) => t.id);
|
|
47
|
+
await topicService.batchRemoveTopics(topicIds);
|
|
48
|
+
|
|
49
|
+
// Refresh the cron topics list
|
|
50
|
+
await refreshCronTopics();
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error('Failed to clear topics:', error);
|
|
53
|
+
modal.error({
|
|
54
|
+
content: t('agentCronJobs.clearTopicsFailed' as any),
|
|
55
|
+
title: t('error' as any, { ns: 'common' }),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}, [topics, refreshCronTopics, modal, t]);
|
|
59
|
+
|
|
60
|
+
return useMemo(
|
|
61
|
+
() =>
|
|
62
|
+
[
|
|
63
|
+
{
|
|
64
|
+
icon: <Icon icon={Trash} />,
|
|
65
|
+
key: 'clearTopics',
|
|
66
|
+
label: t('agentCronJobs.clearTopics' as any),
|
|
67
|
+
onClick: () => {
|
|
68
|
+
modal.confirm({
|
|
69
|
+
cancelText: t('cancel', { ns: 'common' }),
|
|
70
|
+
centered: true,
|
|
71
|
+
content: t('agentCronJobs.confirmClearTopics' as any, { count: topics.length }),
|
|
72
|
+
okButtonProps: { danger: true },
|
|
73
|
+
okText: t('ok', { ns: 'common' }),
|
|
74
|
+
onOk: handleClearTopics,
|
|
75
|
+
title: t('agentCronJobs.clearTopics' as any),
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
type: 'divider' as const,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
danger: true,
|
|
84
|
+
icon: <Icon icon={Trash} />,
|
|
85
|
+
key: 'deleteCronJob',
|
|
86
|
+
label: t('agentCronJobs.deleteCronJob' as any),
|
|
87
|
+
onClick: () => {
|
|
88
|
+
modal.confirm({
|
|
89
|
+
cancelText: t('cancel', { ns: 'common' }),
|
|
90
|
+
centered: true,
|
|
91
|
+
content: t('agentCronJobs.confirmDeleteCronJob' as any),
|
|
92
|
+
okButtonProps: { danger: true },
|
|
93
|
+
okText: t('ok', { ns: 'common' }),
|
|
94
|
+
onOk: handleDeleteCronJob,
|
|
95
|
+
title: t('agentCronJobs.deleteCronJob' as any),
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
].filter(Boolean) as MenuProps['items'],
|
|
100
|
+
[topics.length, handleClearTopics, handleDeleteCronJob, t, modal],
|
|
101
|
+
);
|
|
102
|
+
};
|