@goodandready/dsh-cron 0.1.5 → 0.1.6
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/lib/chat-start.js +62 -0
- package/lib/client.js +253 -101
- package/lib/index.js +35 -13
- package/lib/models-handler.js +32 -0
- package/lib/prompt.js +17 -0
- package/lib/runner.js +46 -2
- package/package.json +1 -1
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export async function chatStartHandler(ctx, req, res, parseJsonBody, sendJson, randomUUID) {
|
|
2
|
+
try {
|
|
3
|
+
if (req.method !== 'POST') {
|
|
4
|
+
sendJson(res, 405, { ok: false, error: 'Method not allowed' });
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const body = await parseJsonBody(req);
|
|
9
|
+
const userPrompt = (body.prompt || '').trim();
|
|
10
|
+
if (!userPrompt) {
|
|
11
|
+
sendJson(res, 400, { ok: false, error: 'Текст задачи не может быть пустым' });
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const agents = ctx.agents;
|
|
16
|
+
if (!agents || typeof agents.create !== 'function') {
|
|
17
|
+
sendJson(res, 500, { ok: false, error: 'Служба agents недоступна в DSH' });
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let createUserMessage = (m) => m;
|
|
22
|
+
try {
|
|
23
|
+
const llmModule = await import('@deepseek-ai/dsh-llm');
|
|
24
|
+
if (llmModule && typeof llmModule.createUserMessage === 'function') {
|
|
25
|
+
createUserMessage = llmModule.createUserMessage;
|
|
26
|
+
}
|
|
27
|
+
} catch {}
|
|
28
|
+
|
|
29
|
+
let mintSessionId = () => `cron-setup-${randomUUID()}`;
|
|
30
|
+
try {
|
|
31
|
+
const sessionModule = await import('@deepseek-ai/dsh-session');
|
|
32
|
+
if (sessionModule && typeof sessionModule.SessionId === 'function') {
|
|
33
|
+
const sid = mintSessionId();
|
|
34
|
+
mintSessionId = () => sessionModule.SessionId(sid);
|
|
35
|
+
}
|
|
36
|
+
} catch {}
|
|
37
|
+
|
|
38
|
+
const defaultSel = ctx.get('agentDefaultModel')?.currentSelection?.();
|
|
39
|
+
const provider = body.provider || defaultSel?.provider;
|
|
40
|
+
const model = body.model || defaultSel?.model;
|
|
41
|
+
|
|
42
|
+
const handle = await agents.create({
|
|
43
|
+
sessionId: mintSessionId(),
|
|
44
|
+
meta: {},
|
|
45
|
+
agentOptions: provider && model ? { provider, model } : undefined,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const { buildAgentCronPrompt } = await import('./prompt.js');
|
|
49
|
+
const fullPrompt = buildAgentCronPrompt(userPrompt);
|
|
50
|
+
|
|
51
|
+
await handle.agent.whenIdle();
|
|
52
|
+
handle.agent.followup(createUserMessage({
|
|
53
|
+
content: [{ type: 'text', text: fullPrompt }],
|
|
54
|
+
source: { kind: 'plugin', plugin: 'dsh-cron', form: 'cron-setup' },
|
|
55
|
+
}));
|
|
56
|
+
|
|
57
|
+
sendJson(res, 200, { ok: true, sessionId: handle.agent.session.id });
|
|
58
|
+
} catch (err) {
|
|
59
|
+
ctx.logger?.warn?.(`[dsh-cron] chat/start error: ${err.message}`);
|
|
60
|
+
sendJson(res, 500, { ok: false, error: err.message });
|
|
61
|
+
}
|
|
62
|
+
}
|
package/lib/client.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
window.__ModuleLoader__.load({
|
|
2
2
|
id: '@goodandready/dsh-cron',
|
|
3
3
|
factory: (require) => {
|
|
4
|
-
var module = { exports: {} }
|
|
5
|
-
var exports = module.exports
|
|
6
|
-
|
|
7
|
-
const ReactDOM = require('react-dom/client');
|
|
4
|
+
var module = { exports: {} }
|
|
5
|
+
var exports = module.exports
|
|
6
|
+
const React = window.React || require('react');
|
|
7
|
+
const ReactDOM = window.ReactDOM || require('react-dom/client');
|
|
8
8
|
|
|
9
|
-
const NS = '
|
|
9
|
+
const NS = 'cron';
|
|
10
10
|
const PANEL = 'cron';
|
|
11
11
|
const ACTIVATE_EVENT = 'dsh-panel-activate';
|
|
12
12
|
const ACTIVE_ATTR = 'data-dsh-cron-active';
|
|
@@ -19,22 +19,26 @@ window.__ModuleLoader__.load({
|
|
|
19
19
|
const ICON_TIMER = '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>';
|
|
20
20
|
const ICON_PLAY = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 21"></polygon></svg>';
|
|
21
21
|
const ICON_PAUSE = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="4" width="4" height="16"></rect><rect x="14" y="4" width="4" height="16"></rect></svg>';
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
22
|
+
const ICON_TRASH = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>';
|
|
23
|
+
const ICON_SEARCH = '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>';
|
|
24
|
+
const ICON_CHEVRON_DOWN = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>';
|
|
25
|
+
const ICON_BELL = '<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path><path d="M13.73 21a2 2 0 0 1-3.46 0"></path></svg>';
|
|
26
|
+
const ICON_CLIPBOARD = '<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect></svg>';
|
|
27
|
+
const ICON_ACTIVITY = '<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline></svg>';
|
|
28
|
+
const ICON_SEND = '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>';
|
|
27
29
|
|
|
28
30
|
const STYLES = `
|
|
29
|
-
[data-pane=conversation],
|
|
30
|
-
[
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
html[${ACTIVE_ATTR}]
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
html[${ACTIVE_ATTR}] [data-pane=conversation],
|
|
32
|
+
html[${ACTIVE_ATTR}] [class*=centerCol] {
|
|
33
|
+
position: relative !important;
|
|
34
|
+
}
|
|
35
|
+
html[${ACTIVE_ATTR}] [data-pane=conversation] > *:not([${VIEW_ATTR}]),
|
|
36
|
+
html[${ACTIVE_ATTR}] [class*=centerCol] > *:not([${VIEW_ATTR}]) {
|
|
37
|
+
visibility: hidden !important;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.dsh-cron-entry-clone { display: flex; align-items: center; gap: 8px; }
|
|
41
|
+
.dsh-cron-entry-icon { display: flex; align-items: center; justify-content: center; flex: none; width: 16px; height: 16px; }
|
|
38
42
|
.dsh-cron-entry-label { text-overflow: ellipsis; overflow: hidden; }
|
|
39
43
|
[data-dsh-frame][data-sidebar-collapsed] .dsh-cron-entry-label { display: none; }
|
|
40
44
|
|
|
@@ -67,6 +71,7 @@ window.__ModuleLoader__.load({
|
|
|
67
71
|
.dsh-cron-task-info { flex: 1; min-width: 0; }
|
|
68
72
|
.dsh-cron-task-title { font-size: 15px; font-weight: 500; margin-bottom: 3px; }
|
|
69
73
|
.dsh-cron-task-sched { font-size: 13px; color: #9ca3af; }
|
|
74
|
+
.dsh-cron-task-model-tag { display: inline-block; font-size: 11px; padding: 2px 7px; border-radius: 4px; background: rgba(255,255,255,0.06); color: #aaa; margin-left: 8px; vertical-align: middle; }
|
|
70
75
|
.dsh-cron-task-actions { display: flex; gap: 8px; }
|
|
71
76
|
.dsh-cron-icon-btn { background: transparent; border: 1px solid #333; border-radius: 8px; padding: 6px 12px; font-size: 12.5px; color: #aaa; cursor: pointer; }
|
|
72
77
|
.dsh-cron-icon-btn:hover { background: #2a2a2a; color: #fff; }
|
|
@@ -81,15 +86,25 @@ window.__ModuleLoader__.load({
|
|
|
81
86
|
.dsh-cron-rec-desc { font-size: 13px; color: #71717a; }
|
|
82
87
|
|
|
83
88
|
.dsh-cron-modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.7); backdrop-filter: blur(4px); display: flex; align-items: center; justify-content: center; z-index: 1000; }
|
|
84
|
-
.dsh-cron-modal { background: var(--dsw-alias-bg-layer-2, #1f1f1f); border: 1px solid var(--dsw-alias-border-l2, #333); border-radius: 16px; width: 100%; max-width:
|
|
89
|
+
.dsh-cron-modal { background: var(--dsw-alias-bg-layer-2, #1f1f1f); border: 1px solid var(--dsw-alias-border-l2, #333); border-radius: 16px; width: 100%; max-width: 540px; padding: 24px; box-shadow: 0 20px 40px rgba(0,0,0,0.6); box-sizing: border-box; }
|
|
85
90
|
.dsh-cron-modal-title { font-size: 18px; font-weight: 600; margin-bottom: 18px; }
|
|
86
91
|
.dsh-cron-form-group { margin-bottom: 16px; }
|
|
87
92
|
.dsh-cron-form-group label { display: block; font-size: 13px; color: #aaa; margin-bottom: 6px; }
|
|
88
|
-
.dsh-cron-form-group input, .dsh-cron-form-group textarea { width: 100%; background: #141414; border: 1px solid #333; border-radius: 8px; padding: 9px 12px; color: #fff; font-size: 13.5px; outline: none; box-sizing: border-box; }
|
|
93
|
+
.dsh-cron-form-group input, .dsh-cron-form-group textarea, .dsh-cron-form-group select { width: 100%; background: #141414; border: 1px solid #333; border-radius: 8px; padding: 9px 12px; color: #fff; font-size: 13.5px; outline: none; box-sizing: border-box; }
|
|
94
|
+
.dsh-cron-form-group select { cursor: pointer; }
|
|
89
95
|
.dsh-cron-form-group textarea { min-height: 80px; resize: vertical; }
|
|
90
|
-
.dsh-cron-
|
|
91
|
-
.dsh-cron-
|
|
96
|
+
.dsh-cron-form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
|
|
97
|
+
.dsh-cron-modal-foot { display: flex; justify-content: flex-end; align-items: center; gap: 10px; margin-top: 20px; }
|
|
98
|
+
.dsh-cron-btn-primary { background: #ededed; color: #111; border: none; border-radius: 8px; padding: 7px 16px; font-size: 13.5px; font-weight: 500; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; }
|
|
99
|
+
.dsh-cron-btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
92
100
|
.dsh-cron-btn-secondary { background: transparent; color: #999; border: 1px solid #383838; border-radius: 8px; padding: 7px 16px; font-size: 13.5px; cursor: pointer; }
|
|
101
|
+
.dsh-cron-chat-box { position: relative; background: #141414; border: 1px solid #333; border-radius: 12px; padding: 12px; }
|
|
102
|
+
.dsh-cron-chat-input { width: 100%; background: transparent; border: none; color: #fff; font-size: 14px; min-height: 100px; resize: vertical; outline: none; box-sizing: border-box; line-height: 1.5; }
|
|
103
|
+
.dsh-cron-chat-foot { display: flex; justify-content: space-between; align-items: center; margin-top: 8px; padding-top: 8px; border-top: 1px solid #222; }
|
|
104
|
+
.dsh-cron-chat-hint { font-size: 12px; color: #666; }
|
|
105
|
+
.dsh-cron-send-btn { width: 34px; height: 34px; border-radius: 8px; background: #ededed; color: #111; border: none; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: transform 0.1s, background 0.15s; }
|
|
106
|
+
.dsh-cron-send-btn:hover:not(:disabled) { background: #fff; transform: translateY(-1px); }
|
|
107
|
+
.dsh-cron-send-btn:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
93
108
|
|
|
94
109
|
.dsh-cron-card { background: var(--dsw-alias-bg-layer-2, #1f1f1f); border: 1px solid var(--dsw-alias-border-l1, #333); border-radius: 12px; padding: 18px 20px; margin-bottom: 16px; }
|
|
95
110
|
.dsh-cron-card-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
|
|
@@ -129,23 +144,16 @@ window.__ModuleLoader__.load({
|
|
|
129
144
|
};
|
|
130
145
|
}
|
|
131
146
|
|
|
132
|
-
function
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
sessions.create({}).then((sessionId) => {
|
|
142
|
-
if (sessions.open) sessions.open(sessionId);
|
|
143
|
-
}).catch(() => {});
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
147
|
+
function openSession(ctx, sessionId) {
|
|
148
|
+
if (!sessionId) return false;
|
|
149
|
+
const sessions = ctx && typeof ctx.get === 'function' ? ctx.get('sessions') : ctx?.sessions;
|
|
150
|
+
if (!sessions || typeof sessions.open !== 'function') return false;
|
|
151
|
+
try {
|
|
152
|
+
sessions.open(sessionId);
|
|
153
|
+
return true;
|
|
154
|
+
} catch {
|
|
155
|
+
return false;
|
|
146
156
|
}
|
|
147
|
-
const btn = nativeButton();
|
|
148
|
-
if (btn) btn.click();
|
|
149
157
|
}
|
|
150
158
|
|
|
151
159
|
function CronScreen(props) {
|
|
@@ -155,21 +163,36 @@ window.__ModuleLoader__.load({
|
|
|
155
163
|
const [tasks, setTasks] = React.useState([]);
|
|
156
164
|
const [recs, setRecs] = React.useState([]);
|
|
157
165
|
const [dropdownOpen, setDropdownOpen] = React.useState(false);
|
|
158
|
-
|
|
166
|
+
|
|
167
|
+
// Manual modal state
|
|
168
|
+
const [manualModalOpen, setManualModalOpen] = React.useState(false);
|
|
159
169
|
const [formTitle, setFormTitle] = React.useState('');
|
|
160
170
|
const [formSchedule, setFormSchedule] = React.useState('');
|
|
161
171
|
const [formPrompt, setFormPrompt] = React.useState('');
|
|
172
|
+
const [formProvider, setFormProvider] = React.useState('');
|
|
173
|
+
const [formModel, setFormModel] = React.useState('');
|
|
174
|
+
|
|
175
|
+
// DSH dialog state
|
|
176
|
+
const [dshModalOpen, setDshModalOpen] = React.useState(false);
|
|
177
|
+
const [dshPrompt, setDshPrompt] = React.useState('');
|
|
178
|
+
const [dshLoading, setDshLoading] = React.useState(false);
|
|
179
|
+
|
|
180
|
+
// Models list state
|
|
181
|
+
const [providers, setProviders] = React.useState([]);
|
|
182
|
+
const [models, setModels] = React.useState([]);
|
|
183
|
+
const [defaultModelInfo, setDefaultModelInfo] = React.useState(null);
|
|
162
184
|
|
|
163
185
|
React.useEffect(() => {
|
|
164
186
|
const onKey = (e) => {
|
|
165
187
|
if (e.key !== 'Escape') return;
|
|
166
|
-
if (
|
|
188
|
+
if (manualModalOpen) { setManualModalOpen(false); return; }
|
|
189
|
+
if (dshModalOpen) { setDshModalOpen(false); return; }
|
|
167
190
|
if (dropdownOpen) { setDropdownOpen(false); return; }
|
|
168
191
|
if (onClose) onClose();
|
|
169
192
|
};
|
|
170
193
|
document.addEventListener('keydown', onKey);
|
|
171
194
|
return () => document.removeEventListener('keydown', onKey);
|
|
172
|
-
}, [
|
|
195
|
+
}, [manualModalOpen, dshModalOpen, dropdownOpen, onClose]);
|
|
173
196
|
|
|
174
197
|
const fetchTasks = async () => {
|
|
175
198
|
try {
|
|
@@ -182,7 +205,21 @@ window.__ModuleLoader__.load({
|
|
|
182
205
|
} catch (err) {}
|
|
183
206
|
};
|
|
184
207
|
|
|
208
|
+
const loadModels = async (providerToFetch) => {
|
|
209
|
+
try {
|
|
210
|
+
const url = '/dsh-cron/models' + (providerToFetch ? '?provider=' + encodeURIComponent(providerToFetch) : '');
|
|
211
|
+
const res = await fetch(url);
|
|
212
|
+
const data = await res.json();
|
|
213
|
+
if (data) {
|
|
214
|
+
if (data.providers) setProviders(data.providers);
|
|
215
|
+
if (data.models) setModels(data.models);
|
|
216
|
+
if (data.current) setDefaultModelInfo(data.current);
|
|
217
|
+
}
|
|
218
|
+
} catch (err) {}
|
|
219
|
+
};
|
|
220
|
+
|
|
185
221
|
React.useEffect(() => { fetchTasks(); }, [tab, query]);
|
|
222
|
+
React.useEffect(() => { loadModels(); }, []);
|
|
186
223
|
|
|
187
224
|
const handleToggleTask = async (id) => {
|
|
188
225
|
try {
|
|
@@ -207,21 +244,34 @@ window.__ModuleLoader__.load({
|
|
|
207
244
|
} catch (err) {}
|
|
208
245
|
};
|
|
209
246
|
|
|
210
|
-
const
|
|
247
|
+
const openManualModal = (preset) => {
|
|
211
248
|
setDropdownOpen(false);
|
|
212
249
|
if (preset) {
|
|
213
250
|
setFormTitle(preset.title || '');
|
|
214
251
|
setFormSchedule(preset.schedule || '');
|
|
215
252
|
setFormPrompt(preset.prompt || '');
|
|
253
|
+
setFormProvider(defaultModelInfo?.provider || '');
|
|
254
|
+
setFormModel(defaultModelInfo?.model || '');
|
|
255
|
+
if (defaultModelInfo?.provider) loadModels(defaultModelInfo.provider);
|
|
216
256
|
} else {
|
|
217
257
|
setFormTitle('');
|
|
218
258
|
setFormSchedule('0 9 * * 1-5');
|
|
219
259
|
setFormPrompt('');
|
|
260
|
+
setFormProvider(defaultModelInfo?.provider || '');
|
|
261
|
+
setFormModel(defaultModelInfo?.model || '');
|
|
262
|
+
if (defaultModelInfo?.provider) loadModels(defaultModelInfo.provider);
|
|
220
263
|
}
|
|
221
|
-
|
|
264
|
+
setManualModalOpen(true);
|
|
222
265
|
};
|
|
223
266
|
|
|
224
|
-
const
|
|
267
|
+
const handleProviderChange = (e) => {
|
|
268
|
+
const prov = e.target.value;
|
|
269
|
+
setFormProvider(prov);
|
|
270
|
+
setFormModel('');
|
|
271
|
+
loadModels(prov);
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const handleSaveManual = async (e) => {
|
|
225
275
|
e.preventDefault();
|
|
226
276
|
try {
|
|
227
277
|
const res = await fetch('/dsh-cron/tasks', {
|
|
@@ -230,38 +280,54 @@ window.__ModuleLoader__.load({
|
|
|
230
280
|
body: JSON.stringify({
|
|
231
281
|
title: formTitle,
|
|
232
282
|
schedule: formSchedule,
|
|
233
|
-
prompt: formPrompt
|
|
283
|
+
prompt: formPrompt,
|
|
284
|
+
provider: formProvider || undefined,
|
|
285
|
+
model: formModel || undefined,
|
|
234
286
|
})
|
|
235
287
|
});
|
|
236
288
|
const data = await res.json();
|
|
237
289
|
if (!data.ok) { alert(data.error || 'Ошибка'); return; }
|
|
238
|
-
|
|
290
|
+
setManualModalOpen(false);
|
|
239
291
|
fetchTasks();
|
|
240
292
|
} catch (err) { alert(err.message); }
|
|
241
293
|
};
|
|
242
294
|
|
|
243
|
-
const
|
|
295
|
+
const openDshModal = () => {
|
|
244
296
|
setDropdownOpen(false);
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
|
|
297
|
+
setDshPrompt('');
|
|
298
|
+
setDshModalOpen(true);
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
const handleStartWithDSH = async () => {
|
|
302
|
+
const text = dshPrompt.trim();
|
|
303
|
+
if (!text) return;
|
|
304
|
+
|
|
305
|
+
setDshLoading(true);
|
|
306
|
+
try {
|
|
307
|
+
const res = await fetch('/dsh-cron/chat/start', {
|
|
308
|
+
method: 'POST',
|
|
309
|
+
headers: { 'Content-Type': 'application/json' },
|
|
310
|
+
body: JSON.stringify({ prompt: text })
|
|
311
|
+
});
|
|
312
|
+
const data = await res.json();
|
|
313
|
+
if (!data.ok || !data.sessionId) {
|
|
314
|
+
alert(data.error || 'Не удалось запустить диалог с агентом');
|
|
315
|
+
setDshLoading(false);
|
|
316
|
+
return;
|
|
262
317
|
}
|
|
263
|
-
|
|
264
|
-
|
|
318
|
+
|
|
319
|
+
setDshModalOpen(false);
|
|
320
|
+
if (onClose) onClose();
|
|
321
|
+
else toggle.set(false);
|
|
322
|
+
|
|
323
|
+
if (!openSession(ctx, data.sessionId)) {
|
|
324
|
+
alert('Сессия создана (' + data.sessionId + '), перейдите к ней в списке чатов');
|
|
325
|
+
}
|
|
326
|
+
} catch (err) {
|
|
327
|
+
alert('Ошибка запуска: ' + err.message);
|
|
328
|
+
} finally {
|
|
329
|
+
setDshLoading(false);
|
|
330
|
+
}
|
|
265
331
|
};
|
|
266
332
|
|
|
267
333
|
return React.createElement('div', { className: 'dsh-cron-overlay' },
|
|
@@ -291,8 +357,8 @@ window.__ModuleLoader__.load({
|
|
|
291
357
|
React.createElement('span', { dangerouslySetInnerHTML: { __html: ICON_CHEVRON_DOWN } })
|
|
292
358
|
),
|
|
293
359
|
dropdownOpen && React.createElement('div', { className: 'dsh-cron-dropdown' },
|
|
294
|
-
React.createElement('button', { className: 'dsh-cron-dropdown-item', onClick:
|
|
295
|
-
React.createElement('button', { className: 'dsh-cron-dropdown-item', onClick: () =>
|
|
360
|
+
React.createElement('button', { className: 'dsh-cron-dropdown-item', onClick: openDshModal }, '💬 Создать с DSH'),
|
|
361
|
+
React.createElement('button', { className: 'dsh-cron-dropdown-item', onClick: () => openManualModal(null) }, '✏️ Настроить вручную')
|
|
296
362
|
)
|
|
297
363
|
)
|
|
298
364
|
),
|
|
@@ -312,46 +378,58 @@ window.__ModuleLoader__.load({
|
|
|
312
378
|
className: 'dsh-cron-tab',
|
|
313
379
|
'data-active': tab === t ? 'true' : undefined,
|
|
314
380
|
onClick: () => setTab(t)
|
|
315
|
-
},
|
|
316
|
-
t === 'all' ? 'Все' : t === 'active' ? 'Активные' : t === 'paused' ? 'Приостановленные' : 'Завершено'
|
|
317
|
-
)
|
|
381
|
+
}, t === 'all' ? 'Все' : t === 'active' ? 'Активные' : t === 'paused' ? 'На паузе' : 'Завершенные')
|
|
318
382
|
)
|
|
319
383
|
),
|
|
320
384
|
React.createElement('div', { className: 'dsh-cron-task-list' },
|
|
321
|
-
tasks.length === 0
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
385
|
+
tasks.length === 0
|
|
386
|
+
? React.createElement('div', { style: { color: '#777', padding: '16px 0', fontSize: '14px' } }, 'Нет запланированных задач')
|
|
387
|
+
: tasks.map((task) =>
|
|
388
|
+
React.createElement('div', { key: task.id, className: 'dsh-cron-task-item' },
|
|
389
|
+
React.createElement('button', {
|
|
390
|
+
className: 'dsh-cron-task-status-btn',
|
|
391
|
+
'data-active': task.status === 'active' ? 'true' : undefined,
|
|
392
|
+
title: task.status === 'active' ? 'Приостановить' : 'Возобновить',
|
|
393
|
+
onClick: () => handleToggleTask(task.id)
|
|
394
|
+
}, React.createElement('span', { dangerouslySetInnerHTML: { __html: task.status === 'active' ? ICON_PAUSE : ICON_PLAY } })),
|
|
395
|
+
React.createElement('div', { className: 'dsh-cron-task-info' },
|
|
396
|
+
React.createElement('div', { className: 'dsh-cron-task-title' },
|
|
397
|
+
task.title,
|
|
398
|
+
task.model ? React.createElement('span', { className: 'dsh-cron-task-model-tag' }, task.model.split('/').pop()) : null
|
|
399
|
+
),
|
|
400
|
+
React.createElement('div', { className: 'dsh-cron-task-sched' },
|
|
401
|
+
task.scheduleText || task.schedule,
|
|
402
|
+
task.nextRunAt ? ` · след: ${new Date(task.nextRunAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}` : ''
|
|
403
|
+
)
|
|
404
|
+
),
|
|
405
|
+
React.createElement('div', { className: 'dsh-cron-task-actions' },
|
|
406
|
+
React.createElement('button', {
|
|
407
|
+
className: 'dsh-cron-icon-btn',
|
|
408
|
+
title: 'Запустить немедленно',
|
|
409
|
+
onClick: () => handleRunNow(task.id)
|
|
410
|
+
}, 'Запустить'),
|
|
411
|
+
React.createElement('button', {
|
|
412
|
+
className: 'dsh-cron-icon-btn',
|
|
413
|
+
title: 'Удалить задачу',
|
|
414
|
+
onClick: () => handleDelete(task.id)
|
|
415
|
+
}, React.createElement('span', { dangerouslySetInnerHTML: { __html: ICON_TRASH } }))
|
|
416
|
+
)
|
|
417
|
+
)
|
|
337
418
|
)
|
|
338
|
-
)
|
|
339
|
-
)
|
|
340
419
|
),
|
|
341
|
-
React.createElement('div', { className: 'dsh-cron-recs-title' }, '
|
|
420
|
+
React.createElement('div', { className: 'dsh-cron-recs-title' }, 'Рекомендуемые задачи'),
|
|
342
421
|
React.createElement('div', { className: 'dsh-cron-recs-list' },
|
|
343
422
|
recs.map((rec) =>
|
|
344
423
|
React.createElement('div', {
|
|
345
424
|
key: rec.id,
|
|
346
425
|
className: 'dsh-cron-rec-card',
|
|
347
|
-
onClick: () =>
|
|
426
|
+
onClick: () => openManualModal(rec)
|
|
348
427
|
},
|
|
349
|
-
React.createElement('div', {
|
|
350
|
-
|
|
351
|
-
dangerouslySetInnerHTML: {
|
|
428
|
+
React.createElement('div', { className: 'dsh-cron-rec-icon' },
|
|
429
|
+
React.createElement('span', { dangerouslySetInnerHTML: {
|
|
352
430
|
__html: rec.icon === 'bell' ? ICON_BELL : rec.icon === 'clipboard' ? ICON_CLIPBOARD : ICON_ACTIVITY
|
|
353
|
-
}
|
|
354
|
-
|
|
431
|
+
} })
|
|
432
|
+
),
|
|
355
433
|
React.createElement('div', { className: 'dsh-cron-rec-content' },
|
|
356
434
|
React.createElement('div', { className: 'dsh-cron-rec-title' },
|
|
357
435
|
rec.title,
|
|
@@ -363,10 +441,12 @@ window.__ModuleLoader__.load({
|
|
|
363
441
|
)
|
|
364
442
|
)
|
|
365
443
|
),
|
|
366
|
-
|
|
444
|
+
|
|
445
|
+
// 1. Manual creation modal
|
|
446
|
+
manualModalOpen && React.createElement('div', { className: 'dsh-cron-modal-overlay', onClick: () => setManualModalOpen(false) },
|
|
367
447
|
React.createElement('div', { className: 'dsh-cron-modal', onClick: (e) => e.stopPropagation() },
|
|
368
448
|
React.createElement('div', { className: 'dsh-cron-modal-title' }, 'Настройка запланированной задачи'),
|
|
369
|
-
React.createElement('form', { onSubmit:
|
|
449
|
+
React.createElement('form', { onSubmit: handleSaveManual },
|
|
370
450
|
React.createElement('div', { className: 'dsh-cron-form-group' },
|
|
371
451
|
React.createElement('label', null, 'Название задачи'),
|
|
372
452
|
React.createElement('input', {
|
|
@@ -385,6 +465,28 @@ window.__ModuleLoader__.load({
|
|
|
385
465
|
onChange: (e) => setFormSchedule(e.target.value)
|
|
386
466
|
})
|
|
387
467
|
),
|
|
468
|
+
React.createElement('div', { className: 'dsh-cron-form-row' },
|
|
469
|
+
React.createElement('div', { className: 'dsh-cron-form-group' },
|
|
470
|
+
React.createElement('label', null, 'Провайдер LLM'),
|
|
471
|
+
React.createElement('select', {
|
|
472
|
+
value: formProvider,
|
|
473
|
+
onChange: handleProviderChange
|
|
474
|
+
},
|
|
475
|
+
React.createElement('option', { value: '' }, 'По умолчанию (DSH)'),
|
|
476
|
+
providers.map((p) => React.createElement('option', { key: p.id, value: p.id }, p.name || p.id))
|
|
477
|
+
)
|
|
478
|
+
),
|
|
479
|
+
React.createElement('div', { className: 'dsh-cron-form-group' },
|
|
480
|
+
React.createElement('label', null, 'Модель LLM'),
|
|
481
|
+
React.createElement('select', {
|
|
482
|
+
value: formModel,
|
|
483
|
+
onChange: (e) => setFormModel(e.target.value)
|
|
484
|
+
},
|
|
485
|
+
React.createElement('option', { value: '' }, 'По умолчанию'),
|
|
486
|
+
models.map((m) => React.createElement('option', { key: m.id, value: m.id }, m.name || m.id))
|
|
487
|
+
)
|
|
488
|
+
)
|
|
489
|
+
),
|
|
388
490
|
React.createElement('div', { className: 'dsh-cron-form-group' },
|
|
389
491
|
React.createElement('label', null, 'Промпт / Инструкция для агента'),
|
|
390
492
|
React.createElement('textarea', {
|
|
@@ -398,7 +500,7 @@ window.__ModuleLoader__.load({
|
|
|
398
500
|
React.createElement('button', {
|
|
399
501
|
type: 'button',
|
|
400
502
|
className: 'dsh-cron-btn-secondary',
|
|
401
|
-
onClick: () =>
|
|
503
|
+
onClick: () => setManualModalOpen(false)
|
|
402
504
|
}, 'Отмена'),
|
|
403
505
|
React.createElement('button', {
|
|
404
506
|
type: 'submit',
|
|
@@ -407,6 +509,56 @@ window.__ModuleLoader__.load({
|
|
|
407
509
|
)
|
|
408
510
|
)
|
|
409
511
|
)
|
|
512
|
+
),
|
|
513
|
+
|
|
514
|
+
// 2. "Создать с DSH" modal
|
|
515
|
+
dshModalOpen && React.createElement('div', { className: 'dsh-cron-modal-overlay', onClick: () => !dshLoading && setDshModalOpen(false) },
|
|
516
|
+
React.createElement('div', { className: 'dsh-cron-modal', onClick: (e) => e.stopPropagation() },
|
|
517
|
+
React.createElement('div', { className: 'dsh-cron-modal-title' }, 'Запланировать задачу с DSH Агентом'),
|
|
518
|
+
React.createElement('div', { className: 'dsh-cron-form-group' },
|
|
519
|
+
React.createElement('label', null, 'Опишите задачу и желаемую периодичность:'),
|
|
520
|
+
React.createElement('div', { className: 'dsh-cron-chat-box' },
|
|
521
|
+
React.createElement('textarea', {
|
|
522
|
+
className: 'dsh-cron-chat-input',
|
|
523
|
+
rows: 4,
|
|
524
|
+
autoFocus: true,
|
|
525
|
+
disabled: dshLoading,
|
|
526
|
+
placeholder: 'Например: проверяй каждые 2 часа свободное место на сервере и наличие упавших systemd-сервисов, присылай алерт только если что-то не так...',
|
|
527
|
+
value: dshPrompt,
|
|
528
|
+
onKeyDown: (e) => {
|
|
529
|
+
if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
|
|
530
|
+
e.preventDefault();
|
|
531
|
+
handleStartWithDSH();
|
|
532
|
+
}
|
|
533
|
+
},
|
|
534
|
+
onChange: (e) => setDshPrompt(e.target.value)
|
|
535
|
+
}),
|
|
536
|
+
React.createElement('div', { className: 'dsh-cron-chat-foot' },
|
|
537
|
+
React.createElement('span', { className: 'dsh-cron-chat-hint' }, 'Ctrl+Enter для отправки'),
|
|
538
|
+
React.createElement('button', {
|
|
539
|
+
type: 'button',
|
|
540
|
+
className: 'dsh-cron-send-btn',
|
|
541
|
+
disabled: dshLoading || !dshPrompt.trim(),
|
|
542
|
+
title: 'Отправить агенту',
|
|
543
|
+
onClick: handleStartWithDSH
|
|
544
|
+
},
|
|
545
|
+
React.createElement('span', {
|
|
546
|
+
style: { display: 'flex', alignItems: 'center' },
|
|
547
|
+
dangerouslySetInnerHTML: { __html: ICON_SEND }
|
|
548
|
+
})
|
|
549
|
+
)
|
|
550
|
+
)
|
|
551
|
+
)
|
|
552
|
+
),
|
|
553
|
+
React.createElement('div', { className: 'dsh-cron-modal-foot' },
|
|
554
|
+
React.createElement('button', {
|
|
555
|
+
type: 'button',
|
|
556
|
+
className: 'dsh-cron-btn-secondary',
|
|
557
|
+
disabled: dshLoading,
|
|
558
|
+
onClick: () => setDshModalOpen(false)
|
|
559
|
+
}, 'Отмена')
|
|
560
|
+
)
|
|
561
|
+
)
|
|
410
562
|
)
|
|
411
563
|
);
|
|
412
564
|
}
|
|
@@ -611,4 +763,4 @@ window.__ModuleLoader__.load({
|
|
|
611
763
|
module.exports = { apply, inject: ['slots', 'locale', 'settingsScope'] };
|
|
612
764
|
return module.exports;
|
|
613
765
|
}
|
|
614
|
-
})
|
|
766
|
+
})
|
package/lib/index.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { TaskStore } from './store.js';
|
|
2
2
|
import { TaskScheduler, parseScheduleExpression } from './scheduler.js';
|
|
3
3
|
import { SessionRunner } from './runner.js';
|
|
4
|
+
import { getModelsHandler } from './models-handler.js';
|
|
5
|
+
import { chatStartHandler } from './chat-start.js';
|
|
4
6
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
7
|
+
import { randomUUID } from 'node:crypto';
|
|
5
8
|
|
|
6
9
|
export const name = '@goodandready/dsh-cron';
|
|
7
|
-
export const inject = ['tools', 'webServer', 'settings'];
|
|
10
|
+
export const inject = ['tools', 'webServer', 'settings', 'llm', 'agents', 'agentDefaultModel'];
|
|
8
11
|
|
|
9
12
|
function parseJsonBody(req) {
|
|
10
13
|
return new Promise((resolve, reject) => {
|
|
@@ -101,6 +104,8 @@ export function apply(ctx, config) {
|
|
|
101
104
|
type: body.type || 'prompt',
|
|
102
105
|
delivery: body.delivery || 'current',
|
|
103
106
|
status: body.status || 'active',
|
|
107
|
+
provider: body.provider || undefined,
|
|
108
|
+
model: body.model || undefined,
|
|
104
109
|
});
|
|
105
110
|
scheduler.scheduleTask(task);
|
|
106
111
|
sendJson(res, 200, { ok: true, task });
|
|
@@ -114,6 +119,24 @@ export function apply(ctx, config) {
|
|
|
114
119
|
}
|
|
115
120
|
}), 'dsh-cron: /tasks');
|
|
116
121
|
|
|
122
|
+
// GET /dsh-cron/models
|
|
123
|
+
ctx.effect(() => ctx.webServer.register({
|
|
124
|
+
kind: 'exact',
|
|
125
|
+
path: '/dsh-cron/models',
|
|
126
|
+
handler: async (req, res) => {
|
|
127
|
+
await getModelsHandler(ctx, req, res, sendJson);
|
|
128
|
+
}
|
|
129
|
+
}), 'dsh-cron: /models');
|
|
130
|
+
|
|
131
|
+
// POST /dsh-cron/chat/start
|
|
132
|
+
ctx.effect(() => ctx.webServer.register({
|
|
133
|
+
kind: 'exact',
|
|
134
|
+
path: '/dsh-cron/chat/start',
|
|
135
|
+
handler: async (req, res) => {
|
|
136
|
+
await chatStartHandler(ctx, req, res, parseJsonBody, sendJson, randomUUID);
|
|
137
|
+
}
|
|
138
|
+
}), 'dsh-cron: /chat/start');
|
|
139
|
+
|
|
117
140
|
// Prefix routes for actions (/dsh-cron/action/*)
|
|
118
141
|
ctx.effect(() => ctx.webServer.register({
|
|
119
142
|
kind: 'prefix',
|
|
@@ -128,11 +151,11 @@ export function apply(ctx, config) {
|
|
|
128
151
|
const taskId = toggleMatch[1];
|
|
129
152
|
const task = store.get(taskId);
|
|
130
153
|
if (!task) {
|
|
131
|
-
sendJson(res, 404, { ok: false, error: '
|
|
154
|
+
sendJson(res, 404, { ok: false, error: 'Task not found' });
|
|
132
155
|
return;
|
|
133
156
|
}
|
|
134
|
-
const updated = task.status === 'active'
|
|
135
|
-
? scheduler.pauseTask(taskId)
|
|
157
|
+
const updated = task.status === 'active'
|
|
158
|
+
? scheduler.pauseTask(taskId)
|
|
136
159
|
: scheduler.resumeTask(taskId);
|
|
137
160
|
sendJson(res, 200, { ok: true, task: updated });
|
|
138
161
|
return;
|
|
@@ -141,13 +164,8 @@ export function apply(ctx, config) {
|
|
|
141
164
|
const runMatch = pathname.match(/^\/dsh-cron\/action\/([^/]+)\/run$/);
|
|
142
165
|
if (req.method === 'POST' && runMatch) {
|
|
143
166
|
const taskId = runMatch[1];
|
|
144
|
-
const task = store.get(taskId);
|
|
145
|
-
if (!task) {
|
|
146
|
-
sendJson(res, 404, { ok: false, error: 'Задача не найдена' });
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
167
|
scheduler.runTask(taskId);
|
|
150
|
-
sendJson(res, 200, { ok: true
|
|
168
|
+
sendJson(res, 200, { ok: true });
|
|
151
169
|
return;
|
|
152
170
|
}
|
|
153
171
|
|
|
@@ -175,7 +193,9 @@ export function apply(ctx, config) {
|
|
|
175
193
|
title: { type: 'string', required: true, description: 'Краткое название задачи' },
|
|
176
194
|
schedule: { type: 'string', required: true, description: 'Выражение cron или интервал (every 30m, 0 9 * * 1-5, daily)' },
|
|
177
195
|
prompt: { type: 'string', required: true, description: 'Инструкция для агента при запуске' },
|
|
178
|
-
delivery: { type: 'string', description: 'Режим запуска: current или isolated' }
|
|
196
|
+
delivery: { type: 'string', description: 'Режим запуска: current или isolated' },
|
|
197
|
+
provider: { type: 'string', description: 'ID провайдера LLM для выполнения задачи' },
|
|
198
|
+
model: { type: 'string', description: 'ID модели LLM для выполнения задачи' }
|
|
179
199
|
},
|
|
180
200
|
output: {
|
|
181
201
|
schema: {
|
|
@@ -190,7 +210,7 @@ export function apply(ctx, config) {
|
|
|
190
210
|
return [{ type: 'text', text: value.message }];
|
|
191
211
|
}
|
|
192
212
|
},
|
|
193
|
-
execute: async ({ title, schedule, prompt, delivery }) => {
|
|
213
|
+
execute: async ({ title, schedule, prompt, delivery, provider, model }) => {
|
|
194
214
|
try {
|
|
195
215
|
const parsed = parseScheduleExpression(schedule);
|
|
196
216
|
const task = store.set({
|
|
@@ -199,6 +219,8 @@ export function apply(ctx, config) {
|
|
|
199
219
|
scheduleText: parsed.humanText,
|
|
200
220
|
prompt,
|
|
201
221
|
delivery: delivery || 'isolated',
|
|
222
|
+
provider: provider || undefined,
|
|
223
|
+
model: model || undefined,
|
|
202
224
|
status: 'active'
|
|
203
225
|
});
|
|
204
226
|
scheduler.scheduleTask(task);
|
|
@@ -346,4 +368,4 @@ export function apply(ctx, config) {
|
|
|
346
368
|
return;
|
|
347
369
|
}
|
|
348
370
|
});
|
|
349
|
-
}
|
|
371
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export async function getModelsHandler(ctx, req, res, sendJson) {
|
|
2
|
+
const current = ctx.get('agentDefaultModel')?.currentSelection?.() ?? null;
|
|
3
|
+
let providers = [];
|
|
4
|
+
try {
|
|
5
|
+
if (ctx.llm && typeof ctx.llm.listProviders === 'function') {
|
|
6
|
+
providers = (ctx.llm.listProviders() ?? []).map((p) => ({ id: p.id, name: p.name ?? p.id }));
|
|
7
|
+
}
|
|
8
|
+
} catch {
|
|
9
|
+
providers = [];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const url = new URL(req.url, 'http://127.0.0.1');
|
|
13
|
+
const wanted = url.searchParams.get('provider') || '';
|
|
14
|
+
if (wanted === '') {
|
|
15
|
+
sendJson(res, 200, { current, providers, models: [] });
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
let rows = [];
|
|
21
|
+
if (ctx.llm && typeof ctx.llm.listModels === 'function') {
|
|
22
|
+
rows = await ctx.llm.listModels(wanted);
|
|
23
|
+
}
|
|
24
|
+
sendJson(res, 200, {
|
|
25
|
+
current,
|
|
26
|
+
providers,
|
|
27
|
+
models: (rows ?? []).map((m) => ({ id: m.id, name: m.name ?? m.id })),
|
|
28
|
+
});
|
|
29
|
+
} catch {
|
|
30
|
+
sendJson(res, 200, { current, providers, models: [] });
|
|
31
|
+
}
|
|
32
|
+
}
|
package/lib/prompt.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the initial prompt for starting a session with DSH agent to configure a cron task.
|
|
3
|
+
* @param {string} userText
|
|
4
|
+
* @returns {string}
|
|
5
|
+
*/
|
|
6
|
+
export function buildAgentCronPrompt(userText) {
|
|
7
|
+
const text = (userText || '').trim();
|
|
8
|
+
return `Пользователь хочет запланировать cron-задачу через DSH Cron:
|
|
9
|
+
"${text}"
|
|
10
|
+
|
|
11
|
+
ИНСТРУКЦИЯ ДЛЯ АГЕНТА:
|
|
12
|
+
1. Твоя цель — помочь пользователю настроить и зарегистрировать запланированную задачу с помощью инструмента \`cron_schedule_task\`.
|
|
13
|
+
2. Предложи подходящую и экономичную LLM-модель для выполнения этого крона (например, \`Qwen/Qwen3.8-Flash\` через Command Code или \`deepseek/deepseek-v4-flash\`), уточни у пользователя, устраивает ли она его или он предпочитает другую подключенную модель.
|
|
14
|
+
3. Уточни и сформулируй точное расписание cron (например, "0 * * * *" или интервал вроде "every 1h").
|
|
15
|
+
4. Примени Silent Rule (правило тишины): если задача мониторинга/проверки не обнаружила проблем, критических изменений или ошибок, агент должен возвращать ПУСТОЙ ответ или короткое уведомление без лишнего шума.
|
|
16
|
+
5. После согласования параметров ОБЯЗАТЕЛЬНО вызови инструмент \`cron_schedule_task\`, передав title, schedule, prompt и delivery ("isolated" или "current").`;
|
|
17
|
+
}
|
package/lib/runner.js
CHANGED
|
@@ -9,7 +9,51 @@ export class SessionRunner {
|
|
|
9
9
|
async execute(task) {
|
|
10
10
|
console.log(`[dsh-cron] executing task "${task.title}" (${task.id})`);
|
|
11
11
|
|
|
12
|
-
//
|
|
12
|
+
// 1. If ctx.agents is available, run through agent session
|
|
13
|
+
if (this.ctx && this.ctx.agents && typeof this.ctx.agents.create === 'function') {
|
|
14
|
+
try {
|
|
15
|
+
const { randomUUID } = await import('node:crypto');
|
|
16
|
+
let createUserMessage = (m) => m;
|
|
17
|
+
try {
|
|
18
|
+
const llmModule = await import('@deepseek-ai/dsh-llm');
|
|
19
|
+
if (llmModule && typeof llmModule.createUserMessage === 'function') {
|
|
20
|
+
createUserMessage = llmModule.createUserMessage;
|
|
21
|
+
}
|
|
22
|
+
} catch {}
|
|
23
|
+
|
|
24
|
+
let mintSessionId = () => `cron-exec-${task.id}-${randomUUID()}`;
|
|
25
|
+
try {
|
|
26
|
+
const sessionModule = await import('@deepseek-ai/dsh-session');
|
|
27
|
+
if (sessionModule && typeof sessionModule.SessionId === 'function') {
|
|
28
|
+
const sid = mintSessionId();
|
|
29
|
+
mintSessionId = () => sessionModule.SessionId(sid);
|
|
30
|
+
}
|
|
31
|
+
} catch {}
|
|
32
|
+
|
|
33
|
+
const defaultSel = this.ctx.get?.('agentDefaultModel')?.currentSelection?.();
|
|
34
|
+
const provider = task.provider || defaultSel?.provider;
|
|
35
|
+
const model = task.model || defaultSel?.model;
|
|
36
|
+
|
|
37
|
+
const handle = await this.ctx.agents.create({
|
|
38
|
+
sessionId: mintSessionId(),
|
|
39
|
+
meta: {},
|
|
40
|
+
agentOptions: provider && model ? { provider, model } : undefined,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
await handle.agent.whenIdle();
|
|
44
|
+
handle.agent.followup(createUserMessage({
|
|
45
|
+
content: [{ type: 'text', text: task.prompt }],
|
|
46
|
+
source: { kind: 'plugin', plugin: 'dsh-cron', form: 'cron-execute' },
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
return `[dsh-cron] Agent started session ${handle.agent.session?.id || 'cron'}`;
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.error(`[dsh-cron] agent execution failed:`, err.message);
|
|
52
|
+
throw err;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 2. Check if legacy session service is present
|
|
13
57
|
if (this.ctx && this.ctx.session && typeof this.ctx.session.create === 'function') {
|
|
14
58
|
try {
|
|
15
59
|
const session = await this.ctx.session.create({
|
|
@@ -27,7 +71,7 @@ export class SessionRunner {
|
|
|
27
71
|
}
|
|
28
72
|
}
|
|
29
73
|
|
|
30
|
-
// Direct shell / script task fallback
|
|
74
|
+
// 3. Direct shell / script task fallback
|
|
31
75
|
if (task.type === 'script' && task.prompt) {
|
|
32
76
|
const { exec } = await import('node:child_process');
|
|
33
77
|
const { promisify } = await import('node:util');
|