@lobehub/lobehub 2.0.13 → 2.1.0
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 +25 -0
- package/changelog/v2.json +9 -0
- package/locales/en-US/setting.json +16 -0
- package/locales/zh-CN/setting.json +16 -0
- package/package.json +1 -1
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Cron/index.tsx +4 -2
- package/src/app/[variants]/(main)/agent/cron/[cronId]/features/CronJobContentEditor.tsx +39 -44
- package/src/app/[variants]/(main)/agent/cron/[cronId]/features/CronJobHeader.tsx +27 -20
- package/src/app/[variants]/(main)/agent/cron/[cronId]/features/CronJobSaveButton.tsx +4 -2
- package/src/app/[variants]/(main)/agent/cron/[cronId]/features/CronJobScheduleConfig.tsx +183 -145
- package/src/app/[variants]/(main)/agent/profile/features/AgentCronJobs/hooks/useAgentCronJobs.ts +3 -4
- package/src/app/[variants]/(main)/agent/profile/features/AgentCronJobs/index.tsx +4 -3
- package/src/app/[variants]/(main)/agent/profile/features/ProfileEditor/index.tsx +4 -3
- package/src/locales/default/setting.ts +16 -0
- package/src/store/agent/slices/cron/action.ts +6 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.1.0](https://github.com/lobehub/lobe-chat/compare/v2.0.13...v2.1.0)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2026-01-30**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **misc**: Refactor cron job UI and use runtime enableBusinessFeatures flag.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's improved
|
|
19
|
+
|
|
20
|
+
- **misc**: Refactor cron job UI and use runtime enableBusinessFeatures flag, closes [#11975](https://github.com/lobehub/lobe-chat/issues/11975) ([104a19a](https://github.com/lobehub/lobe-chat/commit/104a19a))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
5
30
|
### [Version 2.0.13](https://github.com/lobehub/lobe-chat/compare/v2.0.12...v2.0.13)
|
|
6
31
|
|
|
7
32
|
<sup>Released on **2026-01-29**</sup>
|
package/changelog/v2.json
CHANGED
|
@@ -34,11 +34,20 @@
|
|
|
34
34
|
"agentCronJobs.empty.description": "Create your first scheduled task to automate your agent",
|
|
35
35
|
"agentCronJobs.empty.title": "No scheduled tasks yet",
|
|
36
36
|
"agentCronJobs.enable": "Enable",
|
|
37
|
+
"agentCronJobs.form.at": "at",
|
|
37
38
|
"agentCronJobs.form.content.placeholder": "Enter the prompt or instruction for the agent",
|
|
39
|
+
"agentCronJobs.form.every": "Every",
|
|
40
|
+
"agentCronJobs.form.frequency": "Frequency",
|
|
41
|
+
"agentCronJobs.form.hours": "hour(s)",
|
|
42
|
+
"agentCronJobs.form.maxExecutions": "Stop after",
|
|
38
43
|
"agentCronJobs.form.maxExecutions.placeholder": "Leave empty for unlimited",
|
|
39
44
|
"agentCronJobs.form.name.placeholder": "Enter task name",
|
|
45
|
+
"agentCronJobs.form.time": "Time",
|
|
40
46
|
"agentCronJobs.form.timeRange.end": "End Time",
|
|
41
47
|
"agentCronJobs.form.timeRange.start": "Start Time",
|
|
48
|
+
"agentCronJobs.form.times": "times",
|
|
49
|
+
"agentCronJobs.form.timezone": "Timezone",
|
|
50
|
+
"agentCronJobs.form.unlimited": "Run continuously",
|
|
42
51
|
"agentCronJobs.form.validation.contentRequired": "Task content is required",
|
|
43
52
|
"agentCronJobs.form.validation.invalidTimeRange": "Start time must be before end time",
|
|
44
53
|
"agentCronJobs.form.validation.nameRequired": "Task name is required",
|
|
@@ -83,6 +92,13 @@
|
|
|
83
92
|
"agentCronJobs.weekday.tuesday": "Tuesday",
|
|
84
93
|
"agentCronJobs.weekday.wednesday": "Wednesday",
|
|
85
94
|
"agentCronJobs.weekdays": "Weekdays",
|
|
95
|
+
"agentCronJobs.weekdays.fri": "Fri",
|
|
96
|
+
"agentCronJobs.weekdays.mon": "Mon",
|
|
97
|
+
"agentCronJobs.weekdays.sat": "Sat",
|
|
98
|
+
"agentCronJobs.weekdays.sun": "Sun",
|
|
99
|
+
"agentCronJobs.weekdays.thu": "Thu",
|
|
100
|
+
"agentCronJobs.weekdays.tue": "Tue",
|
|
101
|
+
"agentCronJobs.weekdays.wed": "Wed",
|
|
86
102
|
"agentInfoDescription.basic.avatar": "Avatar",
|
|
87
103
|
"agentInfoDescription.basic.description": "Description",
|
|
88
104
|
"agentInfoDescription.basic.name": "Name",
|
|
@@ -34,11 +34,20 @@
|
|
|
34
34
|
"agentCronJobs.empty.description": "创建您的第一个定时任务以实现智能体自动化",
|
|
35
35
|
"agentCronJobs.empty.title": "暂无定时任务",
|
|
36
36
|
"agentCronJobs.enable": "启用",
|
|
37
|
+
"agentCronJobs.form.at": "于",
|
|
37
38
|
"agentCronJobs.form.content.placeholder": "输入智能体的提示词或指令",
|
|
39
|
+
"agentCronJobs.form.every": "每",
|
|
40
|
+
"agentCronJobs.form.frequency": "执行频率",
|
|
41
|
+
"agentCronJobs.form.hours": "小时",
|
|
42
|
+
"agentCronJobs.form.maxExecutions": "执行",
|
|
38
43
|
"agentCronJobs.form.maxExecutions.placeholder": "留空表示无限次",
|
|
39
44
|
"agentCronJobs.form.name.placeholder": "输入任务名称",
|
|
45
|
+
"agentCronJobs.form.time": "时间",
|
|
40
46
|
"agentCronJobs.form.timeRange.end": "结束时间",
|
|
41
47
|
"agentCronJobs.form.timeRange.start": "开始时间",
|
|
48
|
+
"agentCronJobs.form.times": "次后停止",
|
|
49
|
+
"agentCronJobs.form.timezone": "时区",
|
|
50
|
+
"agentCronJobs.form.unlimited": "持续执行",
|
|
42
51
|
"agentCronJobs.form.validation.contentRequired": "任务内容不能为空",
|
|
43
52
|
"agentCronJobs.form.validation.invalidTimeRange": "开始时间必须早于结束时间",
|
|
44
53
|
"agentCronJobs.form.validation.nameRequired": "任务名称不能为空",
|
|
@@ -83,6 +92,13 @@
|
|
|
83
92
|
"agentCronJobs.weekday.tuesday": "星期二",
|
|
84
93
|
"agentCronJobs.weekday.wednesday": "星期三",
|
|
85
94
|
"agentCronJobs.weekdays": "工作日",
|
|
95
|
+
"agentCronJobs.weekdays.fri": "周五",
|
|
96
|
+
"agentCronJobs.weekdays.mon": "周一",
|
|
97
|
+
"agentCronJobs.weekdays.sat": "周六",
|
|
98
|
+
"agentCronJobs.weekdays.sun": "周日",
|
|
99
|
+
"agentCronJobs.weekdays.thu": "周四",
|
|
100
|
+
"agentCronJobs.weekdays.tue": "周二",
|
|
101
|
+
"agentCronJobs.weekdays.wed": "周三",
|
|
86
102
|
"agentInfoDescription.basic.avatar": "头像",
|
|
87
103
|
"agentInfoDescription.basic.description": "描述",
|
|
88
104
|
"agentInfoDescription.basic.name": "名称",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
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",
|
|
@@ -26,9 +26,11 @@ const CronTopicList = memo<CronTopicListProps>(({ itemKey }) => {
|
|
|
26
26
|
s.activeAgentId,
|
|
27
27
|
s.useFetchCronTopicsWithJobInfo,
|
|
28
28
|
]);
|
|
29
|
-
const { data: cronTopicsGroupsWithJobInfo = [], isLoading } =
|
|
30
|
-
useFetchCronTopicsWithJobInfo(agentId);
|
|
31
29
|
const enableBusinessFeatures = useServerConfigStore(serverConfigSelectors.enableBusinessFeatures);
|
|
30
|
+
const { data: cronTopicsGroupsWithJobInfo = [], isLoading } = useFetchCronTopicsWithJobInfo(
|
|
31
|
+
agentId,
|
|
32
|
+
enableBusinessFeatures,
|
|
33
|
+
);
|
|
32
34
|
|
|
33
35
|
const handleCreateCronJob = useCallback(() => {
|
|
34
36
|
if (!agentId) return;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
ReactCodePlugin,
|
|
3
5
|
ReactCodemirrorPlugin,
|
|
@@ -8,12 +10,19 @@ import {
|
|
|
8
10
|
ReactTablePlugin,
|
|
9
11
|
} from '@lobehub/editor';
|
|
10
12
|
import { Editor, useEditor } from '@lobehub/editor/react';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { Clock } from 'lucide-react';
|
|
13
|
+
import { FormGroup } from '@lobehub/ui';
|
|
14
|
+
import { createStaticStyles } from 'antd-style';
|
|
14
15
|
import { memo, useCallback, useEffect, useRef } from 'react';
|
|
15
16
|
import { useTranslation } from 'react-i18next';
|
|
16
17
|
|
|
18
|
+
const styles = createStaticStyles(({ css }) => ({
|
|
19
|
+
editorWrapper: css`
|
|
20
|
+
min-height: 200px;
|
|
21
|
+
padding-block: 8px;
|
|
22
|
+
padding-inline: 0;
|
|
23
|
+
`,
|
|
24
|
+
}));
|
|
25
|
+
|
|
17
26
|
interface CronJobContentEditorProps {
|
|
18
27
|
enableRichRender: boolean;
|
|
19
28
|
initialValue: string;
|
|
@@ -26,12 +35,10 @@ const CronJobContentEditor = memo<CronJobContentEditorProps>(
|
|
|
26
35
|
const editor = useEditor();
|
|
27
36
|
const currentValueRef = useRef(initialValue);
|
|
28
37
|
|
|
29
|
-
// Update currentValueRef when initialValue changes
|
|
30
38
|
useEffect(() => {
|
|
31
39
|
currentValueRef.current = initialValue;
|
|
32
40
|
}, [initialValue]);
|
|
33
41
|
|
|
34
|
-
// Initialize editor content when editor is ready
|
|
35
42
|
useEffect(() => {
|
|
36
43
|
if (!editor) return;
|
|
37
44
|
try {
|
|
@@ -48,7 +55,6 @@ const CronJobContentEditor = memo<CronJobContentEditorProps>(
|
|
|
48
55
|
}
|
|
49
56
|
}, [editor, enableRichRender, initialValue]);
|
|
50
57
|
|
|
51
|
-
// Handle content changes
|
|
52
58
|
const handleContentChange = useCallback(
|
|
53
59
|
(e: any) => {
|
|
54
60
|
const nextContent = enableRichRender
|
|
@@ -57,7 +63,6 @@ const CronJobContentEditor = memo<CronJobContentEditorProps>(
|
|
|
57
63
|
|
|
58
64
|
const finalContent = nextContent || '';
|
|
59
65
|
|
|
60
|
-
// Only call onChange if content actually changed
|
|
61
66
|
if (finalContent !== currentValueRef.current) {
|
|
62
67
|
currentValueRef.current = finalContent;
|
|
63
68
|
onChange(finalContent);
|
|
@@ -67,43 +72,33 @@ const CronJobContentEditor = memo<CronJobContentEditorProps>(
|
|
|
67
72
|
);
|
|
68
73
|
|
|
69
74
|
return (
|
|
70
|
-
<
|
|
71
|
-
<
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
]
|
|
98
|
-
: undefined
|
|
99
|
-
}
|
|
100
|
-
style={{ paddingBottom: 48 }}
|
|
101
|
-
type={'text'}
|
|
102
|
-
variant={'chat'}
|
|
103
|
-
/>
|
|
104
|
-
</Flexbox>
|
|
105
|
-
</Card>
|
|
106
|
-
</Flexbox>
|
|
75
|
+
<FormGroup title={t('agentCronJobs.content')} variant="filled">
|
|
76
|
+
<div className={styles.editorWrapper}>
|
|
77
|
+
<Editor
|
|
78
|
+
content={''}
|
|
79
|
+
editor={editor}
|
|
80
|
+
lineEmptyPlaceholder={t('agentCronJobs.form.content.placeholder')}
|
|
81
|
+
onTextChange={handleContentChange}
|
|
82
|
+
placeholder={t('agentCronJobs.form.content.placeholder')}
|
|
83
|
+
plugins={
|
|
84
|
+
enableRichRender
|
|
85
|
+
? [
|
|
86
|
+
ReactListPlugin,
|
|
87
|
+
ReactCodePlugin,
|
|
88
|
+
ReactCodemirrorPlugin,
|
|
89
|
+
ReactHRPlugin,
|
|
90
|
+
ReactLinkPlugin,
|
|
91
|
+
ReactTablePlugin,
|
|
92
|
+
ReactMathPlugin,
|
|
93
|
+
]
|
|
94
|
+
: undefined
|
|
95
|
+
}
|
|
96
|
+
style={{ paddingBottom: 48 }}
|
|
97
|
+
type={'text'}
|
|
98
|
+
variant={'chat'}
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
101
|
+
</FormGroup>
|
|
107
102
|
);
|
|
108
103
|
},
|
|
109
104
|
);
|
|
@@ -1,8 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Flexbox, Input, LobeSwitch as Switch } from '@lobehub/ui';
|
|
4
|
+
import { createStaticStyles } from 'antd-style';
|
|
3
5
|
import { memo } from 'react';
|
|
4
6
|
import { useTranslation } from 'react-i18next';
|
|
5
7
|
|
|
8
|
+
const styles = createStaticStyles(({ css }) => ({
|
|
9
|
+
titleInput: css`
|
|
10
|
+
flex: 1;
|
|
11
|
+
font-size: 28px;
|
|
12
|
+
font-weight: 500;
|
|
13
|
+
line-height: 1.4;
|
|
14
|
+
`,
|
|
15
|
+
}));
|
|
16
|
+
|
|
6
17
|
interface CronJobHeaderProps {
|
|
7
18
|
enabled?: boolean;
|
|
8
19
|
isNewJob?: boolean;
|
|
@@ -17,30 +28,26 @@ const CronJobHeader = memo<CronJobHeaderProps>(
|
|
|
17
28
|
const { t } = useTranslation(['setting', 'common']);
|
|
18
29
|
|
|
19
30
|
return (
|
|
20
|
-
<Flexbox
|
|
21
|
-
|
|
31
|
+
<Flexbox
|
|
32
|
+
align="center"
|
|
33
|
+
gap={16}
|
|
34
|
+
horizontal
|
|
35
|
+
justify="space-between"
|
|
36
|
+
style={{ marginBottom: 8 }}
|
|
37
|
+
>
|
|
22
38
|
<Input
|
|
39
|
+
className={styles.titleInput}
|
|
23
40
|
onChange={(e) => onNameChange(e.target.value)}
|
|
24
41
|
placeholder={t('agentCronJobs.form.name.placeholder')}
|
|
25
|
-
style={{
|
|
26
|
-
fontSize: 28,
|
|
27
|
-
fontWeight: 600,
|
|
28
|
-
padding: 0,
|
|
29
|
-
}}
|
|
30
42
|
value={name}
|
|
31
|
-
variant=
|
|
43
|
+
variant="borderless"
|
|
32
44
|
/>
|
|
33
|
-
|
|
34
|
-
{/* Controls Row */}
|
|
35
45
|
{!isNewJob && (
|
|
36
|
-
<
|
|
37
|
-
{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
onChange={onToggleEnabled}
|
|
42
|
-
/>
|
|
43
|
-
</Flexbox>
|
|
46
|
+
<Switch
|
|
47
|
+
checked={enabled ?? false}
|
|
48
|
+
loading={isTogglingEnabled}
|
|
49
|
+
onChange={onToggleEnabled}
|
|
50
|
+
/>
|
|
44
51
|
)}
|
|
45
52
|
</Flexbox>
|
|
46
53
|
);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
1
3
|
import { Button, Flexbox } from '@lobehub/ui';
|
|
2
4
|
import { Save } from 'lucide-react';
|
|
3
5
|
import { memo } from 'react';
|
|
@@ -13,13 +15,13 @@ const CronJobSaveButton = memo<CronJobSaveButtonProps>(({ disabled, loading, onS
|
|
|
13
15
|
const { t } = useTranslation('setting');
|
|
14
16
|
|
|
15
17
|
return (
|
|
16
|
-
<Flexbox paddingBlock={
|
|
18
|
+
<Flexbox paddingBlock={8}>
|
|
17
19
|
<Button
|
|
18
20
|
disabled={disabled}
|
|
19
21
|
icon={Save}
|
|
20
22
|
loading={loading}
|
|
21
23
|
onClick={onSave}
|
|
22
|
-
style={{
|
|
24
|
+
style={{ maxWidth: 200, width: '100%' }}
|
|
23
25
|
type="primary"
|
|
24
26
|
>
|
|
25
27
|
{t('agentCronJobs.saveAsNew')}
|
|
@@ -1,17 +1,62 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Checkbox, Flexbox, FormGroup, LobeSelect as Select, Text } from '@lobehub/ui';
|
|
4
|
+
import { Divider, InputNumber, TimePicker } from 'antd';
|
|
5
|
+
import { createStaticStyles, cx } from 'antd-style';
|
|
3
6
|
import type { Dayjs } from 'dayjs';
|
|
4
7
|
import dayjs from 'dayjs';
|
|
5
|
-
import { memo
|
|
8
|
+
import { memo } from 'react';
|
|
6
9
|
import { useTranslation } from 'react-i18next';
|
|
7
10
|
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
import { SCHEDULE_TYPE_OPTIONS, type ScheduleType, TIMEZONE_OPTIONS } from '../CronConfig';
|
|
12
|
+
|
|
13
|
+
const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
14
|
+
label: css`
|
|
15
|
+
flex-shrink: 0;
|
|
16
|
+
width: 120px;
|
|
17
|
+
`,
|
|
18
|
+
row: css`
|
|
19
|
+
min-height: 48px;
|
|
20
|
+
padding-block: 12px;
|
|
21
|
+
padding-inline: 0;
|
|
22
|
+
`,
|
|
23
|
+
weekdayButton: css`
|
|
24
|
+
cursor: pointer;
|
|
25
|
+
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
justify-content: center;
|
|
29
|
+
|
|
30
|
+
width: 40px;
|
|
31
|
+
height: 32px;
|
|
32
|
+
border: 1px solid ${cssVar.colorBorder};
|
|
33
|
+
border-radius: 6px;
|
|
34
|
+
|
|
35
|
+
font-size: 12px;
|
|
36
|
+
font-weight: 500;
|
|
37
|
+
color: ${cssVar.colorTextSecondary};
|
|
38
|
+
|
|
39
|
+
background: transparent;
|
|
40
|
+
|
|
41
|
+
transition: all 0.15s ease;
|
|
42
|
+
|
|
43
|
+
&:hover {
|
|
44
|
+
border-color: ${cssVar.colorPrimary};
|
|
45
|
+
color: ${cssVar.colorPrimary};
|
|
46
|
+
}
|
|
47
|
+
`,
|
|
48
|
+
weekdayButtonActive: css`
|
|
49
|
+
border-color: ${cssVar.colorPrimary};
|
|
50
|
+
color: ${cssVar.colorTextLightSolid};
|
|
51
|
+
background: ${cssVar.colorPrimary};
|
|
52
|
+
|
|
53
|
+
&:hover {
|
|
54
|
+
border-color: ${cssVar.colorPrimaryHover};
|
|
55
|
+
color: ${cssVar.colorTextLightSolid};
|
|
56
|
+
background: ${cssVar.colorPrimaryHover};
|
|
57
|
+
}
|
|
58
|
+
`,
|
|
59
|
+
}));
|
|
15
60
|
|
|
16
61
|
interface CronJobScheduleConfigProps {
|
|
17
62
|
hourlyInterval?: number;
|
|
@@ -30,6 +75,16 @@ interface CronJobScheduleConfigProps {
|
|
|
30
75
|
weekdays: number[];
|
|
31
76
|
}
|
|
32
77
|
|
|
78
|
+
const WEEKDAYS = [
|
|
79
|
+
{ key: 1, label: 'agentCronJobs.weekdays.mon' },
|
|
80
|
+
{ key: 2, label: 'agentCronJobs.weekdays.tue' },
|
|
81
|
+
{ key: 3, label: 'agentCronJobs.weekdays.wed' },
|
|
82
|
+
{ key: 4, label: 'agentCronJobs.weekdays.thu' },
|
|
83
|
+
{ key: 5, label: 'agentCronJobs.weekdays.fri' },
|
|
84
|
+
{ key: 6, label: 'agentCronJobs.weekdays.sat' },
|
|
85
|
+
{ key: 0, label: 'agentCronJobs.weekdays.sun' },
|
|
86
|
+
];
|
|
87
|
+
|
|
33
88
|
const CronJobScheduleConfig = memo<CronJobScheduleConfigProps>(
|
|
34
89
|
({
|
|
35
90
|
hourlyInterval,
|
|
@@ -42,172 +97,155 @@ const CronJobScheduleConfig = memo<CronJobScheduleConfigProps>(
|
|
|
42
97
|
}) => {
|
|
43
98
|
const { t } = useTranslation('setting');
|
|
44
99
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (scheduleTypeLabel) {
|
|
54
|
-
result.push({
|
|
55
|
-
key: 'scheduleType',
|
|
56
|
-
label: t(scheduleTypeLabel as any),
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Trigger time
|
|
61
|
-
if (scheduleType === 'hourly') {
|
|
62
|
-
const minute = triggerTime.minute();
|
|
63
|
-
result.push({
|
|
64
|
-
key: 'interval',
|
|
65
|
-
label: `Every ${hourlyInterval || 1} hour(s) at :${minute.toString().padStart(2, '0')}`,
|
|
66
|
-
});
|
|
67
|
-
} else {
|
|
68
|
-
result.push({
|
|
69
|
-
key: 'triggerTime',
|
|
70
|
-
label: triggerTime.format('HH:mm'),
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Timezone
|
|
75
|
-
result.push({
|
|
76
|
-
key: 'timezone',
|
|
77
|
-
label: timezone,
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// Weekdays for weekly schedule
|
|
81
|
-
if (scheduleType === 'weekly' && weekdays.length > 0) {
|
|
82
|
-
result.push({
|
|
83
|
-
key: 'weekdays',
|
|
84
|
-
label: weekdays.map((day) => t(WEEKDAY_LABELS[day] as any)).join(', '),
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return result;
|
|
89
|
-
}, [scheduleType, triggerTime, timezone, weekdays, hourlyInterval, t]);
|
|
100
|
+
const toggleWeekday = (day: number) => {
|
|
101
|
+
const newWeekdays = weekdays.includes(day)
|
|
102
|
+
? weekdays.filter((d) => d !== day)
|
|
103
|
+
: [...weekdays, day];
|
|
104
|
+
onScheduleChange({ weekdays: newWeekdays });
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const isUnlimited = maxExecutions === null || maxExecutions === undefined;
|
|
90
108
|
|
|
91
109
|
return (
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
{
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
})
|
|
113
|
-
}
|
|
114
|
-
options={SCHEDULE_TYPE_OPTIONS.map((opt) => ({
|
|
115
|
-
label: t(opt.label as any),
|
|
116
|
-
value: opt.value,
|
|
117
|
-
}))}
|
|
118
|
-
size="small"
|
|
119
|
-
style={{ minWidth: 120 }}
|
|
120
|
-
value={scheduleType}
|
|
121
|
-
/>
|
|
110
|
+
<FormGroup title={t('agentCronJobs.schedule')} variant="filled">
|
|
111
|
+
{/* Frequency Row */}
|
|
112
|
+
<Flexbox align="center" className={styles.row} gap={24} horizontal>
|
|
113
|
+
<Text className={styles.label}>{t('agentCronJobs.form.frequency')}</Text>
|
|
114
|
+
<Select
|
|
115
|
+
onChange={(value: ScheduleType) =>
|
|
116
|
+
onScheduleChange({
|
|
117
|
+
scheduleType: value,
|
|
118
|
+
weekdays: value === 'weekly' ? [1, 2, 3, 4, 5] : [],
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
options={SCHEDULE_TYPE_OPTIONS.map((opt) => ({
|
|
122
|
+
label: t(opt.label as any),
|
|
123
|
+
value: opt.value,
|
|
124
|
+
}))}
|
|
125
|
+
style={{ width: 140 }}
|
|
126
|
+
value={scheduleType}
|
|
127
|
+
variant="outlined"
|
|
128
|
+
/>
|
|
129
|
+
</Flexbox>
|
|
122
130
|
|
|
123
|
-
|
|
124
|
-
{scheduleType === 'weekly' && (
|
|
125
|
-
<Select
|
|
126
|
-
maxTagCount="responsive"
|
|
127
|
-
mode="multiple"
|
|
128
|
-
onChange={(values: number[]) => onScheduleChange({ weekdays: values })}
|
|
129
|
-
options={WEEKDAY_OPTIONS.map((opt) => ({
|
|
130
|
-
label: t(opt.label as any),
|
|
131
|
-
value: opt.value,
|
|
132
|
-
}))}
|
|
133
|
-
placeholder="Select days"
|
|
134
|
-
size="small"
|
|
135
|
-
style={{ minWidth: 150 }}
|
|
136
|
-
value={weekdays}
|
|
137
|
-
/>
|
|
138
|
-
)}
|
|
131
|
+
<Divider style={{ margin: 0 }} />
|
|
139
132
|
|
|
140
|
-
|
|
141
|
-
|
|
133
|
+
{/* Time Row (for daily/weekly) */}
|
|
134
|
+
{scheduleType !== 'hourly' && (
|
|
135
|
+
<>
|
|
136
|
+
<Flexbox align="center" className={styles.row} gap={24} horizontal>
|
|
137
|
+
<Text className={styles.label}>{t('agentCronJobs.form.time')}</Text>
|
|
142
138
|
<TimePicker
|
|
143
139
|
format="HH:mm"
|
|
144
|
-
minuteStep={
|
|
140
|
+
minuteStep={15}
|
|
145
141
|
onChange={(value) => {
|
|
146
142
|
if (value) onScheduleChange({ triggerTime: value });
|
|
147
143
|
}}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
value={triggerTime ?? dayjs().hour(0).minute(0)}
|
|
144
|
+
style={{ width: 120 }}
|
|
145
|
+
value={triggerTime ?? dayjs().hour(9).minute(0)}
|
|
151
146
|
/>
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
147
|
+
</Flexbox>
|
|
148
|
+
<Divider style={{ margin: 0 }} />
|
|
149
|
+
</>
|
|
150
|
+
)}
|
|
151
|
+
|
|
152
|
+
{/* Hourly Interval Row */}
|
|
153
|
+
{scheduleType === 'hourly' && (
|
|
154
|
+
<>
|
|
155
|
+
<Flexbox align="center" className={styles.row} gap={24} horizontal>
|
|
156
|
+
<Text className={styles.label}>{t('agentCronJobs.form.every')}</Text>
|
|
157
|
+
<Flexbox align="center" gap={8} horizontal>
|
|
158
158
|
<InputNumber
|
|
159
159
|
max={24}
|
|
160
160
|
min={1}
|
|
161
|
-
onChange={(value:
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
size="small"
|
|
165
|
-
style={{ width: 80 }}
|
|
161
|
+
onChange={(value) => onScheduleChange({ hourlyInterval: value ?? 1 })}
|
|
162
|
+
style={{ width: 70 }}
|
|
166
163
|
value={hourlyInterval ?? 1}
|
|
167
164
|
/>
|
|
168
|
-
<Text type="secondary">
|
|
165
|
+
<Text type="secondary">{t('agentCronJobs.form.hours')}</Text>
|
|
166
|
+
<Text type="secondary">{t('agentCronJobs.form.at')}</Text>
|
|
169
167
|
<Select
|
|
170
168
|
onChange={(value: number) =>
|
|
171
169
|
onScheduleChange({ triggerTime: dayjs().hour(0).minute(value) })
|
|
172
170
|
}
|
|
173
171
|
options={[
|
|
174
172
|
{ label: ':00', value: 0 },
|
|
173
|
+
{ label: ':15', value: 15 },
|
|
175
174
|
{ label: ':30', value: 30 },
|
|
175
|
+
{ label: ':45', value: 45 },
|
|
176
176
|
]}
|
|
177
|
-
|
|
178
|
-
style={{ width: 80 }}
|
|
177
|
+
style={{ width: '80px' }}
|
|
179
178
|
value={triggerTime?.minute() ?? 0}
|
|
179
|
+
variant="outlined"
|
|
180
180
|
/>
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
181
|
+
</Flexbox>
|
|
182
|
+
</Flexbox>
|
|
183
|
+
<Divider style={{ margin: 0 }} />
|
|
184
|
+
</>
|
|
185
|
+
)}
|
|
186
|
+
|
|
187
|
+
{/* Weekday Selector (only for weekly) */}
|
|
188
|
+
{scheduleType === 'weekly' && (
|
|
189
|
+
<>
|
|
190
|
+
<Flexbox align="center" className={styles.row} gap={24} horizontal>
|
|
191
|
+
<Text className={styles.label}>{t('agentCronJobs.weekdays')}</Text>
|
|
192
|
+
<Flexbox gap={6} horizontal>
|
|
193
|
+
{WEEKDAYS.map(({ key, label }) => (
|
|
194
|
+
<div
|
|
195
|
+
className={cx(
|
|
196
|
+
styles.weekdayButton,
|
|
197
|
+
weekdays.includes(key) && styles.weekdayButtonActive,
|
|
198
|
+
)}
|
|
199
|
+
key={key}
|
|
200
|
+
onClick={() => toggleWeekday(key)}
|
|
201
|
+
>
|
|
202
|
+
{t(label as any)}
|
|
203
|
+
</div>
|
|
204
|
+
))}
|
|
205
|
+
</Flexbox>
|
|
206
|
+
</Flexbox>
|
|
207
|
+
<Divider style={{ margin: 0 }} />
|
|
208
|
+
</>
|
|
209
|
+
)}
|
|
210
|
+
|
|
211
|
+
{/* Timezone Row */}
|
|
212
|
+
<Flexbox align="center" className={styles.row} gap={24} horizontal>
|
|
213
|
+
<Text className={styles.label}>{t('agentCronJobs.form.timezone')}</Text>
|
|
214
|
+
<Select
|
|
215
|
+
onChange={(value: string) => onScheduleChange({ timezone: value })}
|
|
216
|
+
options={TIMEZONE_OPTIONS}
|
|
217
|
+
popupMatchSelectWidth={false}
|
|
218
|
+
showSearch
|
|
219
|
+
style={{ minWidth: '200px', width: 'fit-content' }}
|
|
220
|
+
value={timezone}
|
|
221
|
+
variant="outlined"
|
|
222
|
+
/>
|
|
223
|
+
</Flexbox>
|
|
224
|
+
|
|
225
|
+
<Divider style={{ margin: 0 }} />
|
|
194
226
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
227
|
+
{/* Execution Limit Row */}
|
|
228
|
+
<Flexbox align="center" className={styles.row} gap={24} horizontal>
|
|
229
|
+
<Text className={styles.label}>{t('agentCronJobs.maxExecutions')}</Text>
|
|
230
|
+
<Flexbox align="center" gap={12} horizontal>
|
|
198
231
|
<InputNumber
|
|
232
|
+
disabled={isUnlimited}
|
|
199
233
|
min={1}
|
|
200
|
-
onChange={(value:
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
size="small"
|
|
205
|
-
style={{ width: 160 }}
|
|
206
|
-
value={maxExecutions ?? null}
|
|
234
|
+
onChange={(value) => onScheduleChange({ maxExecutions: value })}
|
|
235
|
+
placeholder="100"
|
|
236
|
+
style={{ width: 100 }}
|
|
237
|
+
value={maxExecutions ?? undefined}
|
|
207
238
|
/>
|
|
239
|
+
<Text type="secondary">{t('agentCronJobs.form.times')}</Text>
|
|
240
|
+
<Checkbox
|
|
241
|
+
checked={isUnlimited}
|
|
242
|
+
onChange={(checked) => onScheduleChange({ maxExecutions: checked ? null : 100 })}
|
|
243
|
+
>
|
|
244
|
+
{t('agentCronJobs.form.unlimited')}
|
|
245
|
+
</Checkbox>
|
|
208
246
|
</Flexbox>
|
|
209
247
|
</Flexbox>
|
|
210
|
-
</
|
|
248
|
+
</FormGroup>
|
|
211
249
|
);
|
|
212
250
|
},
|
|
213
251
|
);
|
package/src/app/[variants]/(main)/agent/profile/features/AgentCronJobs/hooks/useAgentCronJobs.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ENABLE_BUSINESS_FEATURES } from '@lobechat/business-const';
|
|
2
1
|
import { message } from 'antd';
|
|
3
2
|
import { useCallback } from 'react';
|
|
4
3
|
import { useTranslation } from 'react-i18next';
|
|
@@ -10,7 +9,7 @@ import type {
|
|
|
10
9
|
} from '@/database/schemas/agentCronJob';
|
|
11
10
|
import { agentCronJobService } from '@/services/agentCronJob';
|
|
12
11
|
|
|
13
|
-
export const useAgentCronJobs = (agentId?: string) => {
|
|
12
|
+
export const useAgentCronJobs = (agentId?: string, enabled: boolean = true) => {
|
|
14
13
|
const { t } = useTranslation('setting');
|
|
15
14
|
|
|
16
15
|
// Fetch cron jobs for the agent
|
|
@@ -20,8 +19,8 @@ export const useAgentCronJobs = (agentId?: string) => {
|
|
|
20
19
|
isLoading: loading,
|
|
21
20
|
mutate,
|
|
22
21
|
} = useSWR(
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
enabled && agentId ? `/api/agent-cron-jobs/${agentId}` : null,
|
|
23
|
+
enabled && agentId ? () => agentCronJobService.getByAgentId(agentId) : null,
|
|
25
24
|
{
|
|
26
25
|
onError: (error) => {
|
|
27
26
|
console.error('Failed to fetch cron jobs:', error);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { ENABLE_BUSINESS_FEATURES } from '@lobechat/business-const';
|
|
4
3
|
import { Flexbox } from '@lobehub/ui';
|
|
5
4
|
import { Typography } from 'antd';
|
|
6
5
|
import { Clock } from 'lucide-react';
|
|
@@ -10,6 +9,7 @@ import urlJoin from 'url-join';
|
|
|
10
9
|
|
|
11
10
|
import { useQueryRoute } from '@/hooks/useQueryRoute';
|
|
12
11
|
import { useAgentStore } from '@/store/agent';
|
|
12
|
+
import { serverConfigSelectors, useServerConfigStore } from '@/store/serverConfig';
|
|
13
13
|
|
|
14
14
|
import CronJobCards from './CronJobCards';
|
|
15
15
|
import { useAgentCronJobs } from './hooks/useAgentCronJobs';
|
|
@@ -20,8 +20,9 @@ const AgentCronJobs = memo(() => {
|
|
|
20
20
|
const { t } = useTranslation('setting');
|
|
21
21
|
const agentId = useAgentStore((s) => s.activeAgentId);
|
|
22
22
|
const router = useQueryRoute();
|
|
23
|
+
const enableBusinessFeatures = useServerConfigStore(serverConfigSelectors.enableBusinessFeatures);
|
|
23
24
|
|
|
24
|
-
const { cronJobs, loading, deleteCronJob } = useAgentCronJobs(agentId);
|
|
25
|
+
const { cronJobs, loading, deleteCronJob } = useAgentCronJobs(agentId, enableBusinessFeatures);
|
|
25
26
|
|
|
26
27
|
// Edit: Navigate to cron job detail page
|
|
27
28
|
const handleEdit = useCallback(
|
|
@@ -40,7 +41,7 @@ const AgentCronJobs = memo(() => {
|
|
|
40
41
|
[deleteCronJob],
|
|
41
42
|
);
|
|
42
43
|
|
|
43
|
-
if (!
|
|
44
|
+
if (!enableBusinessFeatures) return null;
|
|
44
45
|
|
|
45
46
|
if (!agentId) {
|
|
46
47
|
return null;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { ENABLE_BUSINESS_FEATURES } from '@lobechat/business-const';
|
|
4
3
|
import { Button, Flexbox } from '@lobehub/ui';
|
|
5
4
|
import { Divider } from 'antd';
|
|
6
5
|
import { useTheme } from 'antd-style';
|
|
@@ -15,6 +14,7 @@ import { useQueryRoute } from '@/hooks/useQueryRoute';
|
|
|
15
14
|
import { useAgentStore } from '@/store/agent';
|
|
16
15
|
import { agentSelectors } from '@/store/agent/selectors';
|
|
17
16
|
import { useChatStore } from '@/store/chat';
|
|
17
|
+
import { serverConfigSelectors, useServerConfigStore } from '@/store/serverConfig';
|
|
18
18
|
|
|
19
19
|
import AgentCronJobs from '../AgentCronJobs';
|
|
20
20
|
import AgentSettings from '../AgentSettings';
|
|
@@ -31,6 +31,7 @@ const ProfileEditor = memo(() => {
|
|
|
31
31
|
const agentId = useAgentStore((s) => s.activeAgentId);
|
|
32
32
|
const switchTopic = useChatStore((s) => s.switchTopic);
|
|
33
33
|
const router = useQueryRoute();
|
|
34
|
+
const enableBusinessFeatures = useServerConfigStore(serverConfigSelectors.enableBusinessFeatures);
|
|
34
35
|
|
|
35
36
|
const handleCreateCronJob = useCallback(() => {
|
|
36
37
|
if (!agentId) return;
|
|
@@ -95,7 +96,7 @@ const ProfileEditor = memo(() => {
|
|
|
95
96
|
{t('startConversation')}
|
|
96
97
|
</Button>
|
|
97
98
|
<AgentPublishButton />
|
|
98
|
-
{
|
|
99
|
+
{enableBusinessFeatures && (
|
|
99
100
|
<Button icon={Clock} onClick={handleCreateCronJob}>
|
|
100
101
|
{t('agentCronJobs.addJob')}
|
|
101
102
|
</Button>
|
|
@@ -106,7 +107,7 @@ const ProfileEditor = memo(() => {
|
|
|
106
107
|
{/* Main Content: Prompt Editor */}
|
|
107
108
|
<EditorCanvas />
|
|
108
109
|
{/* Agent Cron Jobs Display (only show if jobs exist) */}
|
|
109
|
-
{
|
|
110
|
+
{enableBusinessFeatures && <AgentCronJobs />}
|
|
110
111
|
{/* Advanced Settings Modal */}
|
|
111
112
|
<AgentSettings />
|
|
112
113
|
</>
|
|
@@ -37,11 +37,20 @@ export default {
|
|
|
37
37
|
'agentCronJobs.empty.description': 'Create your first scheduled task to automate your agent',
|
|
38
38
|
'agentCronJobs.empty.title': 'No scheduled tasks yet',
|
|
39
39
|
'agentCronJobs.enable': 'Enable',
|
|
40
|
+
'agentCronJobs.form.at': 'at',
|
|
40
41
|
'agentCronJobs.form.content.placeholder': 'Enter the prompt or instruction for the agent',
|
|
42
|
+
'agentCronJobs.form.every': 'Every',
|
|
43
|
+
'agentCronJobs.form.frequency': 'Frequency',
|
|
44
|
+
'agentCronJobs.form.hours': 'hour(s)',
|
|
45
|
+
'agentCronJobs.form.maxExecutions': 'Stop after',
|
|
41
46
|
'agentCronJobs.form.maxExecutions.placeholder': 'Leave empty for unlimited',
|
|
42
47
|
'agentCronJobs.form.name.placeholder': 'Enter task name',
|
|
48
|
+
'agentCronJobs.form.time': 'Time',
|
|
43
49
|
'agentCronJobs.form.timeRange.end': 'End Time',
|
|
44
50
|
'agentCronJobs.form.timeRange.start': 'Start Time',
|
|
51
|
+
'agentCronJobs.form.times': 'times',
|
|
52
|
+
'agentCronJobs.form.timezone': 'Timezone',
|
|
53
|
+
'agentCronJobs.form.unlimited': 'Run continuously',
|
|
45
54
|
'agentCronJobs.form.validation.contentRequired': 'Task content is required',
|
|
46
55
|
'agentCronJobs.form.validation.invalidTimeRange': 'Start time must be before end time',
|
|
47
56
|
'agentCronJobs.form.validation.nameRequired': 'Task name is required',
|
|
@@ -86,6 +95,13 @@ export default {
|
|
|
86
95
|
'agentCronJobs.weekday.tuesday': 'Tuesday',
|
|
87
96
|
'agentCronJobs.weekday.wednesday': 'Wednesday',
|
|
88
97
|
'agentCronJobs.weekdays': 'Weekdays',
|
|
98
|
+
'agentCronJobs.weekdays.fri': 'Fri',
|
|
99
|
+
'agentCronJobs.weekdays.mon': 'Mon',
|
|
100
|
+
'agentCronJobs.weekdays.sat': 'Sat',
|
|
101
|
+
'agentCronJobs.weekdays.sun': 'Sun',
|
|
102
|
+
'agentCronJobs.weekdays.thu': 'Thu',
|
|
103
|
+
'agentCronJobs.weekdays.tue': 'Tue',
|
|
104
|
+
'agentCronJobs.weekdays.wed': 'Wed',
|
|
89
105
|
'agentInfoDescription.basic.avatar': 'Avatar',
|
|
90
106
|
'agentInfoDescription.basic.description': 'Description',
|
|
91
107
|
'agentInfoDescription.basic.name': 'Name',
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ENABLE_BUSINESS_FEATURES } from '@lobechat/business-const';
|
|
2
1
|
import type { SWRResponse } from 'swr';
|
|
3
2
|
import { type StateCreator } from 'zustand/vanilla';
|
|
4
3
|
|
|
@@ -33,7 +32,10 @@ export interface CronTopicGroupWithJobInfo {
|
|
|
33
32
|
export interface CronSliceAction {
|
|
34
33
|
createAgentCronJob: () => Promise<string | null>;
|
|
35
34
|
internal_refreshCronTopics: () => Promise<void>;
|
|
36
|
-
useFetchCronTopicsWithJobInfo: (
|
|
35
|
+
useFetchCronTopicsWithJobInfo: (
|
|
36
|
+
agentId?: string,
|
|
37
|
+
enabled?: boolean,
|
|
38
|
+
) => SWRResponse<CronTopicGroupWithJobInfo[]>;
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
export const createCronSlice: StateCreator<
|
|
@@ -69,9 +71,9 @@ export const createCronSlice: StateCreator<
|
|
|
69
71
|
await mutate([FETCH_CRON_TOPICS_WITH_JOB_INFO_KEY, get().activeAgentId]);
|
|
70
72
|
},
|
|
71
73
|
|
|
72
|
-
useFetchCronTopicsWithJobInfo: (agentId) =>
|
|
74
|
+
useFetchCronTopicsWithJobInfo: (agentId, enabled = true) =>
|
|
73
75
|
useClientDataSWR<CronTopicGroupWithJobInfo[]>(
|
|
74
|
-
|
|
76
|
+
enabled && agentId ? [FETCH_CRON_TOPICS_WITH_JOB_INFO_KEY, agentId] : null,
|
|
75
77
|
async ([, id]: [string, string]) => {
|
|
76
78
|
const [cronJobsResult, cronTopicsGroups] = await Promise.all([
|
|
77
79
|
lambdaClient.agentCronJob.findByAgent.query({ agentId: id }),
|