clawflowbang 1.0.1 → 1.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.
@@ -3,46 +3,46 @@
3
3
  */
4
4
 
5
5
  const fs = require('fs-extra');
6
- const path = require('path');
7
- const cron = require('node-cron');
8
- const chalk = require('chalk');
9
- const OpenClawCLI = require('./OpenClawCLI');
10
- const { normalizeCronExpression } = require('./CronFormat');
11
-
12
- class CronManager {
13
- constructor(configManager) {
14
- this.configManager = configManager;
15
- this.tasks = new Map();
16
- this.jobsFile = configManager.getCronJobsFilePath();
17
- this.openclawCLI = new OpenClawCLI(configManager);
18
- this.useOpenClawCron = this.openclawCLI.hasOpenClaw();
19
-
20
- if (!this.useOpenClawCron) {
21
- this.ensureJobsFile();
22
- }
23
- }
6
+ const path = require('path');
7
+ const cron = require('node-cron');
8
+ const chalk = require('chalk');
9
+ const OpenClawCLI = require('./OpenClawCLI');
10
+ const { normalizeCronExpression } = require('./CronFormat');
11
+
12
+ class CronManager {
13
+ constructor(configManager) {
14
+ this.configManager = configManager;
15
+ this.tasks = new Map();
16
+ this.jobsFile = configManager.getCronJobsFilePath();
17
+ this.openclawCLI = new OpenClawCLI(configManager);
18
+ this.useOpenClawCron = this.openclawCLI.hasOpenClaw();
19
+
20
+ if (!this.useOpenClawCron) {
21
+ this.ensureJobsFile();
22
+ }
23
+ }
24
24
 
25
25
  /**
26
26
  * สร้างไฟล์ jobs ถ้ายังไม่มี
27
27
  */
28
- ensureJobsFile() {
29
- fs.ensureDirSync(path.dirname(this.jobsFile));
30
- if (!fs.existsSync(this.jobsFile)) {
31
- fs.writeJsonSync(this.jobsFile, { jobs: [] }, { spaces: 2 });
32
- }
33
- }
28
+ ensureJobsFile() {
29
+ fs.ensureDirSync(path.dirname(this.jobsFile));
30
+ if (!fs.existsSync(this.jobsFile)) {
31
+ fs.writeJsonSync(this.jobsFile, { jobs: [] }, { spaces: 2 });
32
+ }
33
+ }
34
34
 
35
35
  /**
36
36
  * อ่านรายการ jobs
37
37
  */
38
- getJobs() {
39
- if (!fs.existsSync(this.jobsFile)) {
40
- return [];
41
- }
42
-
43
- const data = fs.readJsonSync(this.jobsFile);
44
- return data.jobs || [];
45
- }
38
+ getJobs() {
39
+ if (!fs.existsSync(this.jobsFile)) {
40
+ return [];
41
+ }
42
+
43
+ const data = fs.readJsonSync(this.jobsFile);
44
+ return data.jobs || [];
45
+ }
46
46
 
47
47
  /**
48
48
  * บันทึกรายการ jobs
@@ -54,76 +54,76 @@ class CronManager {
54
54
  /**
55
55
  * เพิ่ม cronjob
56
56
  */
57
- async add(skillName, schedule, params = {}, description = '') {
58
- const normalizedSchedule = normalizeCronExpression(schedule);
59
-
60
- if (this.useOpenClawCron) {
61
- const uniqueName = `cfh:${skillName}:${Date.now()}`;
62
- const message = `Run skill "${skillName}" with params: ${JSON.stringify(params)}`;
63
- const created = await this.openclawCLI.addCronJob({
64
- name: uniqueName,
65
- description: description || `Run ${skillName}`,
66
- schedule: normalizedSchedule,
67
- message,
68
- });
69
-
70
- let jobId = created.jobId;
71
- if (!jobId) {
72
- const jobs = await this.openclawCLI.listCronJobs();
73
- const matched = jobs.find((job) => job.name === uniqueName);
74
- jobId = matched?.id || null;
75
- }
76
-
77
- if (!jobId) {
78
- throw new Error('สร้าง cronjob ผ่าน openclaw สำเร็จ แต่ไม่สามารถระบุ job id ได้');
79
- }
80
-
81
- this.configManager.addCron({
82
- id: jobId,
83
- skill: skillName,
84
- schedule: normalizedSchedule,
85
- description: description || `Run ${skillName}`,
86
- });
87
-
88
- console.log(chalk.green(`✓ เพิ่ม cronjob (openclaw): ${skillName} (${normalizedSchedule})`));
89
-
90
- return {
91
- id: jobId,
92
- skill: skillName,
93
- schedule: normalizedSchedule,
94
- params,
95
- description: description || `Run ${skillName}`,
96
- enabled: true,
97
- };
98
- }
99
-
100
- const jobs = this.getJobs();
101
- const jobId = `cron_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
57
+ async add(skillName, schedule, params = {}, description = '') {
58
+ const normalizedSchedule = normalizeCronExpression(schedule);
59
+
60
+ if (this.useOpenClawCron) {
61
+ const uniqueName = `cfh:${skillName}:${Date.now()}`;
62
+ const message = `Run skill ${skillName} with params: ${JSON.stringify(params)}`;
63
+ const created = await this.openclawCLI.addCronJob({
64
+ name: uniqueName,
65
+ description: description || `Run ${skillName}`,
66
+ schedule: normalizedSchedule,
67
+ message,
68
+ });
69
+
70
+ let jobId = created.jobId;
71
+ if (!jobId) {
72
+ const jobs = await this.openclawCLI.listCronJobs();
73
+ const matched = jobs.find((job) => job.name === uniqueName);
74
+ jobId = matched?.id || null;
75
+ }
76
+
77
+ if (!jobId) {
78
+ throw new Error('สร้าง cronjob ผ่าน openclaw สำเร็จ แต่ไม่สามารถระบุ job id ได้');
79
+ }
80
+
81
+ this.configManager.addCron({
82
+ id: jobId,
83
+ skill: skillName,
84
+ schedule: normalizedSchedule,
85
+ description: description || `Run ${skillName}`,
86
+ });
87
+
88
+ console.log(chalk.green(`✓ เพิ่ม cronjob (openclaw): ${skillName} (${normalizedSchedule})`));
89
+
90
+ return {
91
+ id: jobId,
92
+ skill: skillName,
93
+ schedule: normalizedSchedule,
94
+ params,
95
+ description: description || `Run ${skillName}`,
96
+ enabled: true,
97
+ };
98
+ }
99
+
100
+ const jobs = this.getJobs();
101
+ const jobId = `cron_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
102
102
 
103
103
  const job = {
104
- id: jobId,
105
- skill: skillName,
106
- schedule: normalizedSchedule,
107
- params,
108
- description: description || `Run ${skillName}`,
109
- enabled: true,
110
- createdAt: new Date().toISOString(),
111
- lastRun: null,
112
- nextRun: this.getNextRun(normalizedSchedule),
113
- runCount: 0,
114
- errorCount: 0,
115
- };
104
+ id: jobId,
105
+ skill: skillName,
106
+ schedule: normalizedSchedule,
107
+ params,
108
+ description: description || `Run ${skillName}`,
109
+ enabled: true,
110
+ createdAt: new Date().toISOString(),
111
+ lastRun: null,
112
+ nextRun: this.getNextRun(normalizedSchedule),
113
+ runCount: 0,
114
+ errorCount: 0,
115
+ };
116
116
 
117
117
  jobs.push(job);
118
118
  this.saveJobs(jobs);
119
119
 
120
120
  // บันทึกลง config ด้วย
121
121
  this.configManager.addCron({
122
- id: jobId,
123
- skill: skillName,
124
- schedule: normalizedSchedule,
125
- description,
126
- });
122
+ id: jobId,
123
+ skill: skillName,
124
+ schedule: normalizedSchedule,
125
+ description,
126
+ });
127
127
 
128
128
  // สร้าง cron script
129
129
  this.createCronScript(job);
@@ -133,89 +133,89 @@ class CronManager {
133
133
  this.startJob(job);
134
134
  }
135
135
 
136
- console.log(chalk.green(`✓ เพิ่ม cronjob: ${skillName} (${normalizedSchedule})`));
137
-
138
- return job;
139
- }
140
-
141
- /**
142
- * แก้ไข cronjob
143
- */
144
- async edit(jobId, updates = {}) {
145
- const normalized = { ...updates };
146
-
147
- if (updates.schedule) {
148
- normalized.schedule = normalizeCronExpression(updates.schedule);
149
- }
150
-
151
- if (this.useOpenClawCron) {
152
- const tracked = this.configManager.getCrons().find((c) => c.id === jobId);
153
- const message =
154
- Object.prototype.hasOwnProperty.call(updates, 'params') && tracked?.skill
155
- ? `Run skill "${tracked.skill}" with params: ${JSON.stringify(updates.params || {})}`
156
- : undefined;
157
-
158
- if (Object.prototype.hasOwnProperty.call(updates, 'params') && !tracked?.skill) {
159
- throw new Error('แก้ไข params ไม่ได้ เพราะไม่พบ mapping skill ของ cron นี้ใน config');
160
- }
161
-
162
- await this.openclawCLI.editCronJob(jobId, {
163
- schedule: normalized.schedule,
164
- description: normalized.description,
165
- message,
166
- });
167
-
168
- this.configManager.updateCron(jobId, {
169
- ...(normalized.schedule ? { schedule: normalized.schedule } : {}),
170
- ...(typeof normalized.description === 'string' ? { description: normalized.description } : {}),
171
- ...(Object.prototype.hasOwnProperty.call(updates, 'params') ? { params: updates.params } : {}),
172
- });
173
-
174
- return { id: jobId, ...normalized };
175
- }
176
-
177
- const jobs = this.getJobs();
178
- const job = jobs.find((j) => j.id === jobId);
179
-
180
- if (!job) {
181
- throw new Error(`ไม่พบ cronjob ID: ${jobId}`);
182
- }
183
-
184
- if (normalized.schedule) {
185
- job.schedule = normalized.schedule;
186
- job.nextRun = this.getNextRun(normalized.schedule);
187
- }
188
-
189
- if (typeof normalized.description === 'string') {
190
- job.description = normalized.description;
191
- }
192
-
193
- if (Object.prototype.hasOwnProperty.call(updates, 'params')) {
194
- job.params = updates.params || {};
195
- }
196
-
197
- this.saveJobs(jobs);
198
- this.configManager.updateCron(jobId, {
199
- ...(normalized.schedule ? { schedule: normalized.schedule } : {}),
200
- ...(typeof normalized.description === 'string' ? { description: normalized.description } : {}),
201
- ...(Object.prototype.hasOwnProperty.call(updates, 'params') ? { params: updates.params } : {}),
202
- });
203
-
204
- return job;
205
- }
136
+ console.log(chalk.green(`✓ เพิ่ม cronjob: ${skillName} (${normalizedSchedule})`));
137
+
138
+ return job;
139
+ }
140
+
141
+ /**
142
+ * แก้ไข cronjob
143
+ */
144
+ async edit(jobId, updates = {}) {
145
+ const normalized = { ...updates };
146
+
147
+ if (updates.schedule) {
148
+ normalized.schedule = normalizeCronExpression(updates.schedule);
149
+ }
150
+
151
+ if (this.useOpenClawCron) {
152
+ const tracked = this.configManager.getCrons().find((c) => c.id === jobId);
153
+ const message =
154
+ Object.prototype.hasOwnProperty.call(updates, 'params') && tracked?.skill
155
+ ? `Run skill ${tracked.skill} with params: ${JSON.stringify(updates.params || {})}`
156
+ : undefined;
157
+
158
+ if (Object.prototype.hasOwnProperty.call(updates, 'params') && !tracked?.skill) {
159
+ throw new Error('แก้ไข params ไม่ได้ เพราะไม่พบ mapping skill ของ cron นี้ใน config');
160
+ }
161
+
162
+ await this.openclawCLI.editCronJob(jobId, {
163
+ schedule: normalized.schedule,
164
+ description: normalized.description,
165
+ message,
166
+ });
167
+
168
+ this.configManager.updateCron(jobId, {
169
+ ...(normalized.schedule ? { schedule: normalized.schedule } : {}),
170
+ ...(typeof normalized.description === 'string' ? { description: normalized.description } : {}),
171
+ ...(Object.prototype.hasOwnProperty.call(updates, 'params') ? { params: updates.params } : {}),
172
+ });
173
+
174
+ return { id: jobId, ...normalized };
175
+ }
176
+
177
+ const jobs = this.getJobs();
178
+ const job = jobs.find((j) => j.id === jobId);
179
+
180
+ if (!job) {
181
+ throw new Error(`ไม่พบ cronjob ID: ${jobId}`);
182
+ }
183
+
184
+ if (normalized.schedule) {
185
+ job.schedule = normalized.schedule;
186
+ job.nextRun = this.getNextRun(normalized.schedule);
187
+ }
188
+
189
+ if (typeof normalized.description === 'string') {
190
+ job.description = normalized.description;
191
+ }
192
+
193
+ if (Object.prototype.hasOwnProperty.call(updates, 'params')) {
194
+ job.params = updates.params || {};
195
+ }
196
+
197
+ this.saveJobs(jobs);
198
+ this.configManager.updateCron(jobId, {
199
+ ...(normalized.schedule ? { schedule: normalized.schedule } : {}),
200
+ ...(typeof normalized.description === 'string' ? { description: normalized.description } : {}),
201
+ ...(Object.prototype.hasOwnProperty.call(updates, 'params') ? { params: updates.params } : {}),
202
+ });
203
+
204
+ return job;
205
+ }
206
206
 
207
207
  /**
208
208
  * ลบ cronjob
209
209
  */
210
- async remove(jobId) {
211
- if (this.useOpenClawCron) {
212
- await this.openclawCLI.removeCronJob(jobId);
213
- this.configManager.removeCron(jobId);
214
- console.log(chalk.green(`✓ ลบ cronjob (openclaw): ${jobId}`));
215
- return { success: true, removed: { id: jobId } };
216
- }
217
-
218
- let jobs = this.getJobs();
210
+ async remove(jobId) {
211
+ if (this.useOpenClawCron) {
212
+ await this.openclawCLI.removeCronJob(jobId);
213
+ this.configManager.removeCron(jobId);
214
+ console.log(chalk.green(`✓ ลบ cronjob (openclaw): ${jobId}`));
215
+ return { success: true, removed: { id: jobId } };
216
+ }
217
+
218
+ let jobs = this.getJobs();
219
219
  const job = jobs.find(j => j.id === jobId);
220
220
 
221
221
  if (!job) {
@@ -243,12 +243,12 @@ class CronManager {
243
243
  /**
244
244
  * แสดงรายการ cronjobs
245
245
  */
246
- list() {
247
- if (this.useOpenClawCron) {
248
- return this.listViaOpenClaw();
249
- }
250
-
251
- const jobs = this.getJobs();
246
+ list() {
247
+ if (this.useOpenClawCron) {
248
+ return this.listViaOpenClaw();
249
+ }
250
+
251
+ const jobs = this.getJobs();
252
252
 
253
253
  if (jobs.length === 0) {
254
254
  console.log(chalk.gray('ไม่มี cronjob ที่ตั้งไว้'));
@@ -265,30 +265,30 @@ class CronManager {
265
265
  nextRun: job.nextRun,
266
266
  runCount: job.runCount,
267
267
  }));
268
- }
269
-
270
- /**
271
- * แสดงรายการ cronjobs ผ่าน OpenClaw CLI
272
- */
273
- async listViaOpenClaw() {
274
- const jobs = await this.openclawCLI.listCronJobs();
275
-
276
- if (!jobs || jobs.length === 0) {
277
- console.log(chalk.gray('ไม่มี cronjob ที่ตั้งไว้'));
278
- return [];
279
- }
280
-
281
- return jobs.map((job) => ({
282
- id: job.id,
283
- skill: job.name || 'unknown',
284
- schedule: job.schedule?.expr || job.schedule?.kind || '-',
285
- description: job.description || '',
286
- enabled: job.enabled !== false,
287
- lastRun: job.lastRunAt || null,
288
- nextRun: job.nextRunAt || null,
289
- runCount: job.runCount || 0,
290
- }));
291
- }
268
+ }
269
+
270
+ /**
271
+ * แสดงรายการ cronjobs ผ่าน OpenClaw CLI
272
+ */
273
+ async listViaOpenClaw() {
274
+ const jobs = await this.openclawCLI.listCronJobs();
275
+
276
+ if (!jobs || jobs.length === 0) {
277
+ console.log(chalk.gray('ไม่มี cronjob ที่ตั้งไว้'));
278
+ return [];
279
+ }
280
+
281
+ return jobs.map((job) => ({
282
+ id: job.id,
283
+ skill: job.name || 'unknown',
284
+ schedule: job.schedule?.expr || job.schedule?.kind || '-',
285
+ description: job.description || '',
286
+ enabled: job.enabled !== false,
287
+ lastRun: job.lastRunAt || null,
288
+ nextRun: job.nextRunAt || null,
289
+ runCount: job.runCount || 0,
290
+ }));
291
+ }
292
292
 
293
293
  /**
294
294
  * เริ่มต้น job
@@ -311,16 +311,16 @@ class CronManager {
311
311
  /**
312
312
  * หยุด job
313
313
  */
314
- stopJob(jobId) {
315
- const task = this.tasks.get(jobId);
316
- if (task) {
317
- task.stop();
318
- if (typeof task.destroy === 'function') {
319
- task.destroy();
320
- }
321
- this.tasks.delete(jobId);
322
- }
323
- }
314
+ stopJob(jobId) {
315
+ const task = this.tasks.get(jobId);
316
+ if (task) {
317
+ task.stop();
318
+ if (typeof task.destroy === 'function') {
319
+ task.destroy();
320
+ }
321
+ this.tasks.delete(jobId);
322
+ }
323
+ }
324
324
 
325
325
  /**
326
326
  * รัน job
@@ -362,10 +362,10 @@ class CronManager {
362
362
  /**
363
363
  * รัน skill
364
364
  */
365
- async runSkill(skillName, params) {
366
- // ในโลกจริงจะเรียก OpenClaw API
367
- // const response = await axios.post(`${baseUrl}/api/skills/execute`, {
368
- // name: skillName,
365
+ async runSkill(skillName, params) {
366
+ // ในโลกจริงจะเรียก OpenClaw API
367
+ // const response = await axios.post(`${baseUrl}/api/skills/execute`, {
368
+ // name: skillName,
369
369
  // params,
370
370
  // });
371
371
 
@@ -392,7 +392,7 @@ async function run() {
392
392
  console.log(\`[\${new Date().toISOString()}] Running \${skill}...\`);
393
393
 
394
394
  // Call OpenClaw API
395
- const baseUrl = process.env.OPENCLAW_URL || 'http://localhost:3000';
395
+ const baseUrl = process.env.OPENCLAW_URL || 'http://localhost:18789';
396
396
  const response = await axios.post(\`\${baseUrl}/api/skills/execute\`, {
397
397
  name: skill,
398
398
  params: { ...config, ...params },
@@ -435,7 +435,7 @@ run();
435
435
  /**
436
436
  * คำนวณเวลารันครั้งถัดไป
437
437
  */
438
- getNextRun(_schedule) {
438
+ getNextRun(_schedule) {
439
439
  // ง่ายๆ แค่ return null ตอนนี้
440
440
  // ในอนาคตอาจใช้ library คำนวณจริง
441
441
  return null;
@@ -444,13 +444,13 @@ run();
444
444
  /**
445
445
  * เปิด/ปิด job
446
446
  */
447
- toggleJob(jobId, enabled) {
448
- if (this.useOpenClawCron) {
449
- throw new Error('โหมด openclaw cron: ให้ใช้คำสั่ง "openclaw cron enable|disable <id>"');
450
- }
451
-
452
- const jobs = this.getJobs();
453
- const job = jobs.find(j => j.id === jobId);
447
+ toggleJob(jobId, enabled) {
448
+ if (this.useOpenClawCron) {
449
+ throw new Error('โหมด openclaw cron: ให้ใช้คำสั่ง "openclaw cron enable|disable <id>"');
450
+ }
451
+
452
+ const jobs = this.getJobs();
453
+ const job = jobs.find(j => j.id === jobId);
454
454
 
455
455
  if (!job) {
456
456
  throw new Error(`ไม่พบ cronjob ID: ${jobId}`);
@@ -473,13 +473,13 @@ run();
473
473
  /**
474
474
  * เริ่มต้นระบบ cron (เรียกตอน start)
475
475
  */
476
- startAll() {
477
- if (this.useOpenClawCron) {
478
- console.log(chalk.green('✓ ใช้ openclaw cron scheduler (ไม่ต้อง start local tasks)'));
479
- return;
480
- }
481
-
482
- const jobs = this.getJobs().filter(j => j.enabled);
476
+ startAll() {
477
+ if (this.useOpenClawCron) {
478
+ console.log(chalk.green('✓ ใช้ openclaw cron scheduler (ไม่ต้อง start local tasks)'));
479
+ return;
480
+ }
481
+
482
+ const jobs = this.getJobs().filter(j => j.enabled);
483
483
 
484
484
  for (const job of jobs) {
485
485
  this.startJob(job);
@@ -491,18 +491,18 @@ run();
491
491
  /**
492
492
  * หยุดระบบ cron ทั้งหมด
493
493
  */
494
- stopAll() {
495
- if (this.useOpenClawCron) {
496
- console.log(chalk.yellow('⏹️ openclaw cron scheduler ยังทำงานภายนอก process นี้'));
497
- return;
498
- }
499
-
500
- for (const [, task] of this.tasks) {
501
- task.stop();
502
- if (typeof task.destroy === 'function') {
503
- task.destroy();
504
- }
505
- }
494
+ stopAll() {
495
+ if (this.useOpenClawCron) {
496
+ console.log(chalk.yellow('⏹️ openclaw cron scheduler ยังทำงานภายนอก process นี้'));
497
+ return;
498
+ }
499
+
500
+ for (const [, task] of this.tasks) {
501
+ task.stop();
502
+ if (typeof task.destroy === 'function') {
503
+ task.destroy();
504
+ }
505
+ }
506
506
  this.tasks.clear();
507
507
 
508
508
  console.log(chalk.yellow('⏹️ หยุดระบบ cron'));