@goodandready/dsh-cron 0.2.10 → 0.2.12
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/README.md +27 -2
- package/README.ru.md +488 -0
- package/README.zh.md +488 -0
- package/docs/README.ru.md +41 -1
- package/docs/README.zh.md +41 -1
- package/lib/api.js +93 -2
- package/lib/chat-start.js +21 -1
- package/lib/client.js +595 -3
- package/lib/index.js +18 -0
- package/lib/runner.js +29 -1
- package/lib/scheduler.js +239 -4
- package/lib/store.js +34 -0
- package/lib/task-transfer.js +9 -0
- package/package.json +1 -1
package/lib/api.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { randomUUID, createHash } from 'node:crypto';
|
|
9
|
-
import { parseScheduleExpression } from './scheduler.js';
|
|
9
|
+
import { parseScheduleExpression, previewSchedule } from './scheduler.js';
|
|
10
10
|
import { normalizeTaskType, CODE_EXECUTING_TYPES } from './runtimes.js';
|
|
11
11
|
import { CHANNEL_IDS, unknownChannelIds } from './channels.js';
|
|
12
12
|
import { applyTaskPatch } from './task-patch.js';
|
|
@@ -60,6 +60,16 @@ export function createCronApiHandler(store, scheduler, collection) {
|
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
if (scope === 'heartbeat' && id) {
|
|
64
|
+
handleHeartbeatPing({ store, req, res, taskId: id });
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (scope === 'schedule' && id === 'preview') {
|
|
69
|
+
await handleSchedulePreview({ req, res, url });
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
63
73
|
if (!id) {
|
|
64
74
|
await handleTaskCollection({ store, scheduler, collection, req, res, url, scope });
|
|
65
75
|
return;
|
|
@@ -352,6 +362,16 @@ function applyImportPlan({ store, scheduler, plan }) {
|
|
|
352
362
|
// ------------------------------------------------------------ single task
|
|
353
363
|
|
|
354
364
|
async function handleTaskItem({ store, scheduler, req, res, url, id, action }) {
|
|
365
|
+
if ((req.method === 'GET' || req.method === 'POST') && action === 'heartbeat') {
|
|
366
|
+
handleHeartbeatPing({ store, req, res, taskId: id });
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (req.method === 'POST' && action === 'dry-run') {
|
|
371
|
+
await handleDryRunTask({ store, scheduler, req, res, id });
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
|
|
355
375
|
if (req.method === 'GET' && action === 'archive') {
|
|
356
376
|
const limit = parseInt(url.searchParams.get('limit') || '50', 10);
|
|
357
377
|
const offset = parseInt(url.searchParams.get('offset') || '0', 10);
|
|
@@ -532,7 +552,15 @@ function mergeExecutionFields(current, body, parsed) {
|
|
|
532
552
|
* tasks and the config-owned refusals are the same code, not a second copy.
|
|
533
553
|
*/
|
|
534
554
|
export async function handleExternalTaskRequest({ store, scheduler, req, res, url }) {
|
|
535
|
-
const parts = url.pathname.split('/').filter(Boolean); // dsh-cron, api, tasks, :id?, :action?
|
|
555
|
+
const parts = url.pathname.split('/').filter(Boolean); // dsh-cron, api, [tasks|heartbeat|schedule], :id?, :action?
|
|
556
|
+
if (parts[2] === 'heartbeat' && parts[3]) {
|
|
557
|
+
handleHeartbeatPing({ store, req, res, taskId: parts[3] });
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
if (parts[2] === 'schedule' && parts[3] === 'preview') {
|
|
561
|
+
await handleSchedulePreview({ req, res, url });
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
536
564
|
const id = parts[3];
|
|
537
565
|
const action = parts[4];
|
|
538
566
|
|
|
@@ -681,3 +709,66 @@ export async function handleTelegramWebhook({ store, scheduler, req, res }) {
|
|
|
681
709
|
|
|
682
710
|
sendJson(res, 200, { ok: true, verb, taskId });
|
|
683
711
|
}
|
|
712
|
+
|
|
713
|
+
export function handleHeartbeatPing({ store, req, res, taskId }) {
|
|
714
|
+
const task = store.get(taskId);
|
|
715
|
+
if (!task) {
|
|
716
|
+
sendJson(res, 404, { ok: false, error: 'Task not found' });
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
const result = typeof store.recordHeartbeat === 'function' ? store.recordHeartbeat(taskId) : null;
|
|
720
|
+
if (!result) {
|
|
721
|
+
sendJson(res, 500, { ok: false, error: 'Could not record heartbeat' });
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
sendJson(res, 200, result);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
export async function handleSchedulePreview({ req, res, url }) {
|
|
728
|
+
let schedule = '';
|
|
729
|
+
let timezone = '';
|
|
730
|
+
let count = 5;
|
|
731
|
+
|
|
732
|
+
if (req.method === 'POST') {
|
|
733
|
+
const { body, error } = await readBody(req, res);
|
|
734
|
+
if (error) return;
|
|
735
|
+
schedule = body.schedule || '';
|
|
736
|
+
timezone = body.timezone || '';
|
|
737
|
+
count = body.count || 5;
|
|
738
|
+
} else if (req.method === 'GET') {
|
|
739
|
+
schedule = url.searchParams.get('schedule') || '';
|
|
740
|
+
timezone = url.searchParams.get('timezone') || '';
|
|
741
|
+
count = parseInt(url.searchParams.get('count') || '5', 10);
|
|
742
|
+
} else {
|
|
743
|
+
sendJson(res, 405, NOT_ALLOWED);
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
if (!schedule) {
|
|
748
|
+
sendJson(res, 400, { ok: false, error: 'Field "schedule" is required' });
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
try {
|
|
753
|
+
const preview = previewSchedule(schedule, { timezone, count });
|
|
754
|
+
sendJson(res, 200, { ok: true, ...preview });
|
|
755
|
+
} catch (err) {
|
|
756
|
+
sendJson(res, 400, { ok: false, error: err.message });
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
export async function handleDryRunTask({ store, scheduler, req, res, id }) {
|
|
761
|
+
if (rejectCrossOrigin(req, res)) return;
|
|
762
|
+
const task = store.get(id);
|
|
763
|
+
if (!task) {
|
|
764
|
+
sendJson(res, 404, { ok: false, error: 'Task not found' });
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
try {
|
|
769
|
+
const outcome = await scheduler.runTask(id, { dryRun: true });
|
|
770
|
+
sendJson(res, 200, outcome || { ok: true, status: 'completed' });
|
|
771
|
+
} catch (err) {
|
|
772
|
+
sendJson(res, 500, { ok: false, error: err.message });
|
|
773
|
+
}
|
|
774
|
+
}
|
package/lib/chat-start.js
CHANGED
|
@@ -40,13 +40,33 @@ export async function chatStartHandler(ctx, req, res, parseJsonBody, sendJson, r
|
|
|
40
40
|
const model = body.model || defaultSel?.model;
|
|
41
41
|
|
|
42
42
|
const cwd = process.cwd();
|
|
43
|
+
const presets = ctx.agentPresets || ctx.get?.('agentPresets');
|
|
44
|
+
let preset = null;
|
|
45
|
+
if (presets && typeof presets.resolve === 'function') {
|
|
46
|
+
try {
|
|
47
|
+
preset = await presets.resolve(body.agentPreset || 'standard');
|
|
48
|
+
if (preset && typeof presets.standingKeyFor === 'function') {
|
|
49
|
+
await presets.standingKeyFor(preset.id);
|
|
50
|
+
}
|
|
51
|
+
} catch (presetErr) {
|
|
52
|
+
ctx.logger?.warn?.(`[dsh-cron] could not resolve agentPreset: ${presetErr.message}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
43
56
|
const handle = await agents.create({
|
|
44
57
|
sessionId: mintSessionId(),
|
|
45
58
|
meta: {
|
|
46
59
|
cwd,
|
|
47
|
-
agentPreset: 'standard',
|
|
60
|
+
agentPreset: preset ? preset.id : 'standard',
|
|
48
61
|
},
|
|
49
62
|
agentOptions: provider && model ? { provider, model } : undefined,
|
|
63
|
+
...(preset ? {
|
|
64
|
+
setup: async (agentCtx) => {
|
|
65
|
+
if (typeof presets.mount === 'function') {
|
|
66
|
+
await presets.mount(agentCtx, preset.id);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
} : {}),
|
|
50
70
|
});
|
|
51
71
|
|
|
52
72
|
const permissions = ctx.get?.('permissionPresets');
|