@dcrays/scheduled-task 0.1.6-beta.2 → 0.1.6-beta.4

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/cordis.patch.yml CHANGED
@@ -1,13 +1,13 @@
1
- - insert:
2
- - id: cron-automation
3
- name: '@dcrays/scheduled-task'
4
- inject:
5
- - agentDefaultModel
6
- - agents
7
- - sessionPersistence
8
- - webServer
9
- - tools
10
- config:
11
- legacyDatabaseEnvironment: production
12
- maxPromptChars: 16384
13
- listPushIntervalSeconds: 30
1
+ - insert:
2
+ - id: cron-automation
3
+ name: '@dcrays/scheduled-task'
4
+ inject:
5
+ - agentDefaultModel
6
+ - agents
7
+ - sessionPersistence
8
+ - webServer
9
+ - tools
10
+ config:
11
+ legacyDatabaseEnvironment: production
12
+ maxPromptChars: 16384
13
+ listPushIntervalSeconds: 30
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dcrays/scheduled-task",
3
- "version": "0.1.6-beta.2",
3
+ "version": "0.1.6-beta.4",
4
4
  "description": "Persistent client-facing cron automation service for DeepSeek Harness",
5
5
  "type": "module",
6
6
  "main": "plugin/index.js",
package/plugin/index.js CHANGED
@@ -1269,13 +1269,17 @@ var CronAutomationManager = class {
1269
1269
  async dispatchDue() {
1270
1270
  if (this.stopping || this.schedulingPaused)
1271
1271
  return;
1272
+ const claimedJobs = [];
1272
1273
  for (; ; ) {
1273
1274
  const claimed = await this.enqueue(() => this.claimNextDueJob());
1274
1275
  if (!claimed)
1275
1276
  break;
1277
+ claimedJobs.push(claimed);
1278
+ }
1279
+ await Promise.all(claimedJobs.map(async (claimed) => {
1276
1280
  const delivered = await this.deliverTracked(claimed.job, claimed.occurrenceAtMs);
1277
1281
  await this.enqueue(() => this.finalizeScheduledRun(claimed.job.id, claimed.occurrenceAtMs, delivered));
1278
- }
1282
+ }));
1279
1283
  await this.enqueue(async () => {
1280
1284
  this.arm();
1281
1285
  });
package/src/manager.js CHANGED
@@ -573,13 +573,17 @@ export class CronAutomationManager {
573
573
  async dispatchDue() {
574
574
  if (this.stopping || this.schedulingPaused)
575
575
  return;
576
+ const claimedJobs = [];
576
577
  for (;;) {
577
578
  const claimed = await this.enqueue(() => this.claimNextDueJob());
578
579
  if (!claimed)
579
580
  break;
581
+ claimedJobs.push(claimed);
582
+ }
583
+ await Promise.all(claimedJobs.map(async (claimed) => {
580
584
  const delivered = await this.deliverTracked(claimed.job, claimed.occurrenceAtMs);
581
585
  await this.enqueue(() => this.finalizeScheduledRun(claimed.job.id, claimed.occurrenceAtMs, delivered));
582
- }
586
+ }));
583
587
  await this.enqueue(async () => {
584
588
  this.arm();
585
589
  });
package/src/repository.js CHANGED
@@ -130,153 +130,153 @@ export class CronAutomationRepository {
130
130
  this.database.exec('PRAGMA journal_mode = WAL');
131
131
  this.database.exec('PRAGMA synchronous = FULL');
132
132
  this.database.exec('PRAGMA busy_timeout = 5000');
133
- this.database.exec(`
134
- CREATE TABLE IF NOT EXISTS cron_jobs (
135
- store_key TEXT NOT NULL,
136
- job_id TEXT NOT NULL,
137
- name TEXT NOT NULL,
138
- description TEXT,
139
- enabled INTEGER NOT NULL,
140
- delete_after_run INTEGER,
141
- created_at_ms INTEGER NOT NULL,
142
- agent_id TEXT,
143
- session_key TEXT,
144
- schedule_kind TEXT NOT NULL,
145
- schedule_expr TEXT,
146
- schedule_tz TEXT,
147
- every_ms INTEGER,
148
- anchor_ms INTEGER,
149
- at TEXT,
150
- stagger_ms INTEGER,
151
- session_target TEXT NOT NULL,
152
- wake_mode TEXT NOT NULL,
153
- payload_kind TEXT NOT NULL,
154
- payload_message TEXT,
155
- payload_model TEXT,
156
- payload_fallbacks_json TEXT,
157
- payload_thinking TEXT,
158
- payload_timeout_seconds INTEGER,
159
- payload_allow_unsafe_external_content INTEGER,
160
- payload_external_content_source_json TEXT,
161
- payload_light_context INTEGER,
162
- payload_tools_allow_json TEXT,
163
- delivery_mode TEXT,
164
- delivery_channel TEXT,
165
- delivery_to TEXT,
166
- delivery_thread_id TEXT,
167
- delivery_account_id TEXT,
168
- delivery_best_effort INTEGER,
169
- delivery_completion_mode TEXT,
170
- delivery_completion_to TEXT,
171
- failure_delivery_mode TEXT,
172
- failure_delivery_channel TEXT,
173
- failure_delivery_to TEXT,
174
- failure_delivery_account_id TEXT,
175
- failure_alert_disabled INTEGER,
176
- failure_alert_after INTEGER,
177
- failure_alert_channel TEXT,
178
- failure_alert_to TEXT,
179
- failure_alert_cooldown_ms INTEGER,
180
- failure_alert_include_skipped INTEGER,
181
- failure_alert_mode TEXT,
182
- failure_alert_account_id TEXT,
183
- next_run_at_ms INTEGER,
184
- running_at_ms INTEGER,
185
- last_run_at_ms INTEGER,
186
- last_run_status TEXT,
187
- last_error TEXT,
188
- last_duration_ms INTEGER,
189
- consecutive_errors INTEGER,
190
- consecutive_skipped INTEGER,
191
- schedule_error_count INTEGER,
192
- last_delivery_status TEXT,
193
- last_delivery_error TEXT,
194
- last_delivered INTEGER,
195
- last_failure_alert_at_ms INTEGER,
196
- job_json TEXT NOT NULL,
197
- state_json TEXT NOT NULL DEFAULT '{}',
198
- runtime_updated_at_ms INTEGER,
199
- schedule_identity TEXT,
200
- sort_order INTEGER NOT NULL DEFAULT 0,
201
- updated_at INTEGER NOT NULL,
202
- PRIMARY KEY (store_key, job_id)
203
- );
204
-
205
- CREATE INDEX IF NOT EXISTS idx_cron_jobs_store_updated
206
- ON cron_jobs(store_key, sort_order ASC, updated_at DESC, job_id);
207
- CREATE INDEX IF NOT EXISTS idx_cron_jobs_store_order
208
- ON cron_jobs(store_key, sort_order ASC, updated_at ASC, job_id);
209
- CREATE INDEX IF NOT EXISTS idx_cron_jobs_enabled_next_run
210
- ON cron_jobs(store_key, enabled, next_run_at_ms, job_id)
211
- WHERE next_run_at_ms IS NOT NULL;
212
- CREATE INDEX IF NOT EXISTS idx_cron_jobs_agent_session
213
- ON cron_jobs(agent_id, session_key, updated_at DESC, job_id)
214
- WHERE agent_id IS NOT NULL OR session_key IS NOT NULL;
215
-
216
- CREATE TABLE IF NOT EXISTS cron_imported_jobs (
217
- source TEXT NOT NULL,
218
- job_id TEXT NOT NULL,
219
- imported_at INTEGER NOT NULL,
220
- PRIMARY KEY (source, job_id)
221
- );
222
-
223
- CREATE TABLE IF NOT EXISTS cron_migrations (
224
- migration_id TEXT PRIMARY KEY,
225
- completed_at INTEGER NOT NULL
226
- );
133
+ this.database.exec(`
134
+ CREATE TABLE IF NOT EXISTS cron_jobs (
135
+ store_key TEXT NOT NULL,
136
+ job_id TEXT NOT NULL,
137
+ name TEXT NOT NULL,
138
+ description TEXT,
139
+ enabled INTEGER NOT NULL,
140
+ delete_after_run INTEGER,
141
+ created_at_ms INTEGER NOT NULL,
142
+ agent_id TEXT,
143
+ session_key TEXT,
144
+ schedule_kind TEXT NOT NULL,
145
+ schedule_expr TEXT,
146
+ schedule_tz TEXT,
147
+ every_ms INTEGER,
148
+ anchor_ms INTEGER,
149
+ at TEXT,
150
+ stagger_ms INTEGER,
151
+ session_target TEXT NOT NULL,
152
+ wake_mode TEXT NOT NULL,
153
+ payload_kind TEXT NOT NULL,
154
+ payload_message TEXT,
155
+ payload_model TEXT,
156
+ payload_fallbacks_json TEXT,
157
+ payload_thinking TEXT,
158
+ payload_timeout_seconds INTEGER,
159
+ payload_allow_unsafe_external_content INTEGER,
160
+ payload_external_content_source_json TEXT,
161
+ payload_light_context INTEGER,
162
+ payload_tools_allow_json TEXT,
163
+ delivery_mode TEXT,
164
+ delivery_channel TEXT,
165
+ delivery_to TEXT,
166
+ delivery_thread_id TEXT,
167
+ delivery_account_id TEXT,
168
+ delivery_best_effort INTEGER,
169
+ delivery_completion_mode TEXT,
170
+ delivery_completion_to TEXT,
171
+ failure_delivery_mode TEXT,
172
+ failure_delivery_channel TEXT,
173
+ failure_delivery_to TEXT,
174
+ failure_delivery_account_id TEXT,
175
+ failure_alert_disabled INTEGER,
176
+ failure_alert_after INTEGER,
177
+ failure_alert_channel TEXT,
178
+ failure_alert_to TEXT,
179
+ failure_alert_cooldown_ms INTEGER,
180
+ failure_alert_include_skipped INTEGER,
181
+ failure_alert_mode TEXT,
182
+ failure_alert_account_id TEXT,
183
+ next_run_at_ms INTEGER,
184
+ running_at_ms INTEGER,
185
+ last_run_at_ms INTEGER,
186
+ last_run_status TEXT,
187
+ last_error TEXT,
188
+ last_duration_ms INTEGER,
189
+ consecutive_errors INTEGER,
190
+ consecutive_skipped INTEGER,
191
+ schedule_error_count INTEGER,
192
+ last_delivery_status TEXT,
193
+ last_delivery_error TEXT,
194
+ last_delivered INTEGER,
195
+ last_failure_alert_at_ms INTEGER,
196
+ job_json TEXT NOT NULL,
197
+ state_json TEXT NOT NULL DEFAULT '{}',
198
+ runtime_updated_at_ms INTEGER,
199
+ schedule_identity TEXT,
200
+ sort_order INTEGER NOT NULL DEFAULT 0,
201
+ updated_at INTEGER NOT NULL,
202
+ PRIMARY KEY (store_key, job_id)
203
+ );
204
+
205
+ CREATE INDEX IF NOT EXISTS idx_cron_jobs_store_updated
206
+ ON cron_jobs(store_key, sort_order ASC, updated_at DESC, job_id);
207
+ CREATE INDEX IF NOT EXISTS idx_cron_jobs_store_order
208
+ ON cron_jobs(store_key, sort_order ASC, updated_at ASC, job_id);
209
+ CREATE INDEX IF NOT EXISTS idx_cron_jobs_enabled_next_run
210
+ ON cron_jobs(store_key, enabled, next_run_at_ms, job_id)
211
+ WHERE next_run_at_ms IS NOT NULL;
212
+ CREATE INDEX IF NOT EXISTS idx_cron_jobs_agent_session
213
+ ON cron_jobs(agent_id, session_key, updated_at DESC, job_id)
214
+ WHERE agent_id IS NOT NULL OR session_key IS NOT NULL;
215
+
216
+ CREATE TABLE IF NOT EXISTS cron_imported_jobs (
217
+ source TEXT NOT NULL,
218
+ job_id TEXT NOT NULL,
219
+ imported_at INTEGER NOT NULL,
220
+ PRIMARY KEY (source, job_id)
221
+ );
222
+
223
+ CREATE TABLE IF NOT EXISTS cron_migrations (
224
+ migration_id TEXT PRIMARY KEY,
225
+ completed_at INTEGER NOT NULL
226
+ );
227
227
  `);
228
- this.insertJobStatement = this.database.prepare(`
229
- INSERT INTO cron_jobs (
230
- store_key, job_id, name, description, enabled, delete_after_run,
231
- created_at_ms, agent_id, session_key,
232
- schedule_kind, schedule_expr, schedule_tz, every_ms, anchor_ms, at,
233
- session_target, wake_mode,
234
- payload_kind, payload_message, payload_timeout_seconds,
235
- delivery_mode, delivery_channel, delivery_to, delivery_account_id, delivery_best_effort,
236
- next_run_at_ms, running_at_ms, last_run_at_ms, last_run_status,
237
- job_json, state_json, runtime_updated_at_ms, sort_order, updated_at
238
- ) VALUES (
239
- ?, ?, ?, ?, ?, ?,
240
- ?, ?, ?,
241
- ?, ?, ?, ?, ?, ?,
242
- ?, ?,
243
- ?, ?, ?,
244
- ?, ?, ?, ?, ?,
245
- ?, ?, ?, ?,
246
- ?, ?, ?, ?, ?
247
- )
248
- ON CONFLICT(store_key, job_id) DO UPDATE SET
249
- name = excluded.name,
250
- description = excluded.description,
251
- enabled = excluded.enabled,
252
- delete_after_run = excluded.delete_after_run,
253
- created_at_ms = excluded.created_at_ms,
254
- agent_id = excluded.agent_id,
255
- session_key = excluded.session_key,
256
- schedule_kind = excluded.schedule_kind,
257
- schedule_expr = excluded.schedule_expr,
258
- schedule_tz = excluded.schedule_tz,
259
- every_ms = excluded.every_ms,
260
- anchor_ms = excluded.anchor_ms,
261
- at = excluded.at,
262
- session_target = excluded.session_target,
263
- wake_mode = excluded.wake_mode,
264
- payload_kind = excluded.payload_kind,
265
- payload_message = excluded.payload_message,
266
- payload_timeout_seconds = excluded.payload_timeout_seconds,
267
- delivery_mode = excluded.delivery_mode,
268
- delivery_channel = excluded.delivery_channel,
269
- delivery_to = excluded.delivery_to,
270
- delivery_account_id = excluded.delivery_account_id,
271
- delivery_best_effort = excluded.delivery_best_effort,
272
- next_run_at_ms = excluded.next_run_at_ms,
273
- running_at_ms = excluded.running_at_ms,
274
- last_run_at_ms = excluded.last_run_at_ms,
275
- last_run_status = excluded.last_run_status,
276
- job_json = excluded.job_json,
277
- state_json = excluded.state_json,
278
- runtime_updated_at_ms = excluded.runtime_updated_at_ms,
279
- updated_at = excluded.updated_at
228
+ this.insertJobStatement = this.database.prepare(`
229
+ INSERT INTO cron_jobs (
230
+ store_key, job_id, name, description, enabled, delete_after_run,
231
+ created_at_ms, agent_id, session_key,
232
+ schedule_kind, schedule_expr, schedule_tz, every_ms, anchor_ms, at,
233
+ session_target, wake_mode,
234
+ payload_kind, payload_message, payload_timeout_seconds,
235
+ delivery_mode, delivery_channel, delivery_to, delivery_account_id, delivery_best_effort,
236
+ next_run_at_ms, running_at_ms, last_run_at_ms, last_run_status,
237
+ job_json, state_json, runtime_updated_at_ms, sort_order, updated_at
238
+ ) VALUES (
239
+ ?, ?, ?, ?, ?, ?,
240
+ ?, ?, ?,
241
+ ?, ?, ?, ?, ?, ?,
242
+ ?, ?,
243
+ ?, ?, ?,
244
+ ?, ?, ?, ?, ?,
245
+ ?, ?, ?, ?,
246
+ ?, ?, ?, ?, ?
247
+ )
248
+ ON CONFLICT(store_key, job_id) DO UPDATE SET
249
+ name = excluded.name,
250
+ description = excluded.description,
251
+ enabled = excluded.enabled,
252
+ delete_after_run = excluded.delete_after_run,
253
+ created_at_ms = excluded.created_at_ms,
254
+ agent_id = excluded.agent_id,
255
+ session_key = excluded.session_key,
256
+ schedule_kind = excluded.schedule_kind,
257
+ schedule_expr = excluded.schedule_expr,
258
+ schedule_tz = excluded.schedule_tz,
259
+ every_ms = excluded.every_ms,
260
+ anchor_ms = excluded.anchor_ms,
261
+ at = excluded.at,
262
+ session_target = excluded.session_target,
263
+ wake_mode = excluded.wake_mode,
264
+ payload_kind = excluded.payload_kind,
265
+ payload_message = excluded.payload_message,
266
+ payload_timeout_seconds = excluded.payload_timeout_seconds,
267
+ delivery_mode = excluded.delivery_mode,
268
+ delivery_channel = excluded.delivery_channel,
269
+ delivery_to = excluded.delivery_to,
270
+ delivery_account_id = excluded.delivery_account_id,
271
+ delivery_best_effort = excluded.delivery_best_effort,
272
+ next_run_at_ms = excluded.next_run_at_ms,
273
+ running_at_ms = excluded.running_at_ms,
274
+ last_run_at_ms = excluded.last_run_at_ms,
275
+ last_run_status = excluded.last_run_status,
276
+ job_json = excluded.job_json,
277
+ state_json = excluded.state_json,
278
+ runtime_updated_at_ms = excluded.runtime_updated_at_ms,
279
+ updated_at = excluded.updated_at
280
280
  `);
281
281
  try {
282
282
  fs.chmodSync(file, 0o600);
@@ -295,11 +295,11 @@ export class CronAutomationRepository {
295
295
  async load() {
296
296
  this.assertOpen();
297
297
  const rows = this.database
298
- .prepare(`
299
- SELECT job_json
300
- FROM cron_jobs
301
- WHERE store_key = ?
302
- ORDER BY sort_order ASC, updated_at ASC, job_id ASC
298
+ .prepare(`
299
+ SELECT job_json
300
+ FROM cron_jobs
301
+ WHERE store_key = ?
302
+ ORDER BY sort_order ASC, updated_at ASC, job_id ASC
303
303
  `)
304
304
  .all(STORE_KEY);
305
305
  return rows.map((row) => decodeJob(JSON.parse(row.job_json)));
@@ -332,9 +332,9 @@ export class CronAutomationRepository {
332
332
  const result = this.planImport(source, decoded);
333
333
  const nextOrder = this.nextSortOrder();
334
334
  result.added.forEach((job, index) => this.insertJob(job, nextOrder + index));
335
- const mark = this.database.prepare(`
336
- INSERT OR IGNORE INTO cron_imported_jobs (source, job_id, imported_at)
337
- VALUES (?, ?, ?)
335
+ const mark = this.database.prepare(`
336
+ INSERT OR IGNORE INTO cron_imported_jobs (source, job_id, imported_at)
337
+ VALUES (?, ?, ?)
338
338
  `);
339
339
  const importedAt = Date.now();
340
340
  for (const job of decoded)
@@ -358,10 +358,10 @@ export class CronAutomationRepository {
358
358
  throw new Error('cron import source must be non-empty');
359
359
  return this.writeTransaction(() => {
360
360
  const rows = this.database
361
- .prepare(`
362
- SELECT jobs.job_id FROM cron_jobs AS jobs
363
- INNER JOIN cron_imported_jobs AS imported ON imported.job_id = jobs.job_id
364
- WHERE jobs.store_key = ? AND imported.source = ?
361
+ .prepare(`
362
+ SELECT jobs.job_id FROM cron_jobs AS jobs
363
+ INNER JOIN cron_imported_jobs AS imported ON imported.job_id = jobs.job_id
364
+ WHERE jobs.store_key = ? AND imported.source = ?
365
365
  `)
366
366
  .all(STORE_KEY, source);
367
367
  const removed = rows.map((row) => row.job_id).filter((id) => !presentIds.has(id));
@@ -382,9 +382,9 @@ export class CronAutomationRepository {
382
382
  const result = this.planImport(migrationId, decoded);
383
383
  const nextOrder = this.nextSortOrder();
384
384
  result.added.forEach((job, index) => this.insertJob(job, nextOrder + index));
385
- const markImported = this.database.prepare(`
386
- INSERT OR IGNORE INTO cron_imported_jobs (source, job_id, imported_at)
387
- VALUES (?, ?, ?)
385
+ const markImported = this.database.prepare(`
386
+ INSERT OR IGNORE INTO cron_imported_jobs (source, job_id, imported_at)
387
+ VALUES (?, ?, ?)
388
388
  `);
389
389
  const importedAt = Date.now();
390
390
  for (const job of importedJobs)
@@ -418,10 +418,10 @@ export class CronAutomationRepository {
418
418
  }
419
419
  nextSortOrder() {
420
420
  const row = this.database
421
- .prepare(`
422
- SELECT COALESCE(MAX(sort_order), -1) + 1 AS next_order
423
- FROM cron_jobs
424
- WHERE store_key = ?
421
+ .prepare(`
422
+ SELECT COALESCE(MAX(sort_order), -1) + 1 AS next_order
423
+ FROM cron_jobs
424
+ WHERE store_key = ?
425
425
  `)
426
426
  .get(STORE_KEY);
427
427
  return row.next_order;