clawflowbang 1.0.0 → 1.0.2

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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Installer - จัดการการติดตั้งและถอนการติดตั้ง packages
2
+ * Installer - Manage installation and uninstallation of packages
3
3
  */
4
4
 
5
5
  const fs = require('fs-extra');
@@ -13,7 +13,7 @@ class Installer {
13
13
  constructor(configManager, registry) {
14
14
  this.configManager = configManager;
15
15
  this.registry = registry;
16
- this.cronManager = null; // จะ set จากภายนอก
16
+ this.cronManager = null; // set from outside
17
17
  this.openclawCLI = new OpenClawCLI(configManager);
18
18
  }
19
19
 
@@ -22,25 +22,25 @@ class Installer {
22
22
  }
23
23
 
24
24
  /**
25
- * ติดตั้ง package
25
+ * Install package
26
26
  */
27
27
  async install(packageName, options = {}) {
28
28
  const { global = false, cron: withCron = true, dryRun = false } = options;
29
29
 
30
- // ตรวจสอบว่า package มีอยู่หรือไม่ (รองรับทั้ง built-in และ npm)
30
+ // Check whether the package exists (supports built-in and npm)
31
31
  const pkg = await this.registry.getPackage(packageName);
32
32
  if (!pkg) {
33
- throw new Error(`Package "${packageName}" ไม่พบใน registry (ลองตรวจสอบชื่อ package หรือเชื่อมต่ออินเทอร์เน็ต)`);
33
+ throw new Error(`Package "${packageName}" not found in registry (check package name or internet connection)`);
34
34
  }
35
35
 
36
36
  // ตรวจสอบว่าติดตั้งแล้วหรือยัง
37
37
  const installed = this.configManager.getInstalledPackages();
38
38
  if (installed[packageName]) {
39
- console.log(chalk.yellow(`⚠️ Package "${packageName}" ติดตั้งแล้ว`));
39
+ console.log(chalk.yellow(`⚠️ Package "${packageName}" is already installed`));
40
40
  const { reinstall } = await inquirer.prompt([{
41
41
  type: 'confirm',
42
42
  name: 'reinstall',
43
- message: 'ต้องการติดตั้งใหม่หรือไม่?',
43
+ message: 'Do you want to reinstall?',
44
44
  default: false,
45
45
  }]);
46
46
  if (!reinstall) {
@@ -48,7 +48,7 @@ class Installer {
48
48
  }
49
49
  }
50
50
 
51
- console.log(chalk.cyan(`📦 กำลังติดตั้ง ${chalk.bold(packageName)}...\n`));
51
+ console.log(chalk.cyan(`📦 Installing ${chalk.bold(packageName)}...\n`));
52
52
 
53
53
  // Dry run - แสดงเฉพาะข้อมูล
54
54
  if (dryRun) {
@@ -56,26 +56,26 @@ class Installer {
56
56
  return { success: true, dryRun: true };
57
57
  }
58
58
 
59
- const spinner = ora('กำลังดำเนินการ...').start();
59
+ const spinner = ora('Processing...').start();
60
60
 
61
61
  try {
62
62
  // 1. ติดตั้ง skills
63
- spinner.text = 'กำลังติดตั้ง skills...';
63
+ spinner.text = 'Installing skills...';
64
64
  await this.installSkills(pkg.skills, global);
65
65
 
66
66
  // 2. ตั้งค่า config
67
- spinner.text = 'กำลังตั้งค่า configuration...';
67
+ spinner.text = 'Setting up configuration...';
68
68
  await this.setupConfig(packageName, pkg.config);
69
69
 
70
70
  // 3. ตั้ง cronjobs (ถ้าเปิดใช้งาน)
71
71
  let crons = [];
72
72
  if (withCron && pkg.crons && pkg.crons.length > 0) {
73
- spinner.text = 'กำลังตั้ง cronjobs...';
73
+ spinner.text = 'Setting up cronjobs...';
74
74
  crons = await this.setupCrons(pkg.crons);
75
75
  }
76
76
 
77
77
  // 4. บันทึกว่าติดตั้งแล้ว
78
- spinner.text = 'กำลังบันทึกข้อมูล...';
78
+ spinner.text = 'Saving state...';
79
79
  this.configManager.addInstalledPackage(packageName, {
80
80
  version: pkg.version,
81
81
  skills: pkg.skills.map(s => s.name),
@@ -83,14 +83,14 @@ class Installer {
83
83
  configPath: this.getPackageConfigPath(packageName),
84
84
  });
85
85
 
86
- spinner.succeed(chalk.green(`ติดตั้ง ${packageName} เสร็จสมบูรณ์!`));
86
+ spinner.succeed(chalk.green(`Installed ${packageName} successfully!`));
87
87
 
88
88
  // แสดงข้อมูลสรุป
89
89
  this.showInstallSummary(pkg, crons);
90
90
 
91
91
  // แสดง post-install message
92
92
  if (pkg.postInstall) {
93
- console.log(chalk.yellow('\n📋 ขั้นตอนถัดไป:'));
93
+ console.log(chalk.yellow('\n📋 Next steps:'));
94
94
  console.log(chalk.gray(` ${pkg.postInstall}`));
95
95
  }
96
96
 
@@ -102,20 +102,20 @@ class Installer {
102
102
  };
103
103
 
104
104
  } catch (error) {
105
- spinner.fail(chalk.red(`ติดตั้งไม่สำเร็จ: ${error.message}`));
105
+ spinner.fail(chalk.red(`Installation failed: ${error.message}`));
106
106
  throw error;
107
107
  }
108
108
  }
109
109
 
110
110
  /**
111
- * ติดตั้ง skills
111
+ * Install skills
112
112
  */
113
113
  async installSkills(skills, global = false) {
114
114
  for (const skill of skills) {
115
- console.log(chalk.gray(` → ติดตั้ง skill: ${skill.name}@${skill.version}`));
115
+ console.log(chalk.gray(` → Installing skill: ${skill.name}@${skill.version}`));
116
116
 
117
- // ติดตั้ง skill ผ่าน OpenClaw
118
- // ในโลกจริงจะเรียก OpenClaw API หรือ CLI
117
+ // Install skill via OpenClaw
118
+ // In production this would call OpenClaw API or CLI
119
119
  await this.installSkillToOpenClaw(skill, global);
120
120
  }
121
121
  }
@@ -123,33 +123,33 @@ class Installer {
123
123
  /**
124
124
  * ติดตั้ง skill ไปยัง OpenClaw
125
125
  */
126
- async installSkillToOpenClaw(skill, global = false) {
127
- void global;
128
- const skillsPath = this.configManager.getSkillsPath();
129
-
130
- try {
131
- await this.openclawCLI.installSkill(skill.name, skill.version, skillsPath);
132
-
133
- // ตรวจสอบหลังติดตั้ง (best effort)
134
- await this.openclawCLI.verifySkill(skill.name).catch(() => null);
135
-
136
- } catch (error) {
137
- try {
138
- const cloned = await this.openclawCLI.installSkillFromGit(skill, skillsPath);
139
- console.log(chalk.yellow(` ↳ fallback git clone สำเร็จ: ${cloned.repository}`));
140
- return;
141
- } catch (gitError) {
142
- throw new Error(
143
- `ไม่สามารถติดตั้ง skill "${skill.name}" ได้ทั้งจาก clawhub และ git clone\n` +
144
- `clawhub: ${error.message}\n` +
145
- `git: ${gitError.message}`,
146
- );
147
- }
148
- }
149
- }
126
+ async installSkillToOpenClaw(skill, global = false) {
127
+ void global;
128
+ const skillsPath = this.configManager.getSkillsPath();
129
+
130
+ try {
131
+ await this.openclawCLI.installSkill(skill.name, skill.version, skillsPath);
132
+
133
+ // ตรวจสอบหลังติดตั้ง (best effort)
134
+ await this.openclawCLI.verifySkill(skill.name).catch(() => null);
135
+
136
+ } catch (error) {
137
+ try {
138
+ const cloned = await this.openclawCLI.installSkillFromGit(skill, skillsPath);
139
+ console.log(chalk.yellow(` ↳ fallback git clone succeeded: ${cloned.repository}`));
140
+ return;
141
+ } catch (gitError) {
142
+ throw new Error(
143
+ `Failed to install skill "${skill.name}" from both clawhub and git clone\n` +
144
+ `clawhub: ${error.message}\n` +
145
+ `git: ${gitError.message}`,
146
+ );
147
+ }
148
+ }
149
+ }
150
150
 
151
151
  /**
152
- * ตั้งค่า config สำหรับ package
152
+ * Setup config for package
153
153
  */
154
154
  async setupConfig(packageName, configSchema) {
155
155
  if (!configSchema) return;
@@ -166,35 +166,35 @@ class Installer {
166
166
 
167
167
  for (const [key, value] of Object.entries(schema)) {
168
168
  if (value.env) {
169
- // ดึงค่าจาก environment variable
169
+ // Pull value from environment variable
170
170
  const envValue = process.env[value.env];
171
171
  if (envValue) {
172
172
  newConfig[skillName][key] = envValue;
173
173
  } else if (value.required && !existingConfig[skillName]?.[key]) {
174
- // ถ้าไม่มีใน env และจำเป็นต้องมี
174
+ // If not in env and required
175
175
  const answer = await inquirer.prompt([{
176
176
  type: 'input',
177
177
  name: key,
178
- message: `กรุณากรอก ${key} สำหรับ ${skillName} (หรือตั้งค่า ${value.env}):`,
179
- validate: (input) => input.length > 0 || 'จำเป็นต้องกรอก',
178
+ message: `Please enter ${key} for ${skillName} (or set ${value.env}):`,
179
+ validate: (input) => input.length > 0 || 'This field is required',
180
180
  }]);
181
181
  newConfig[skillName][key] = answer[key];
182
182
  }
183
183
  } else if (value.default !== undefined) {
184
184
  newConfig[skillName][key] = existingConfig[skillName]?.[key] ?? value.default;
185
185
  } else if (value.required && !existingConfig[skillName]?.[key]) {
186
- const answer = await inquirer.prompt([{
186
+ const answer = await inquirer.prompt([{
187
187
  type: 'input',
188
188
  name: key,
189
- message: `กรุณากรอก ${key} สำหรับ ${skillName}:`,
190
- validate: (input) => input.length > 0 || 'จำเป็นต้องกรอก',
189
+ message: `Please enter ${key} for ${skillName}:`,
190
+ validate: (input) => input.length > 0 || 'This field is required',
191
191
  }]);
192
192
  newConfig[skillName][key] = answer[key];
193
193
  }
194
194
  }
195
195
  }
196
196
 
197
- // รวมกับ config เดิม
197
+ // Merge with existing config
198
198
  const mergedConfig = { ...existingConfig, ...newConfig };
199
199
  fs.writeJsonSync(configPath, mergedConfig, { spaces: 2 });
200
200
  }
@@ -1,7 +1,7 @@
1
1
  const path = require('path');
2
2
  const fs = require('fs-extra');
3
3
  const { promisify } = require('util');
4
- const { execFile, spawnSync } = require('child_process');
4
+ const { execFile, spawn, spawnSync } = require('child_process');
5
5
 
6
6
  const execFileAsync = promisify(execFile);
7
7
 
@@ -33,22 +33,67 @@ class OpenClawCLI {
33
33
  }
34
34
 
35
35
  commandExists(command, args = ['--help']) {
36
- const result = spawnSync(command, args, {
37
- encoding: 'utf8',
38
- stdio: 'pipe',
39
- windowsHide: true,
40
- });
36
+ try {
37
+ const result = spawnSync(command, args, {
38
+ encoding: 'utf8',
39
+ stdio: 'pipe',
40
+ windowsHide: true,
41
+ shell: true,
42
+ });
41
43
 
42
- return result.status === 0;
44
+ return result.status === 0;
45
+ } catch (e) {
46
+ return false;
47
+ }
43
48
  }
44
49
 
45
50
  async run(command, args, options = {}) {
51
+ if (process.platform === 'win32') {
52
+ return new Promise((resolve, reject) => {
53
+ const comspec = process.env.ComSpec || 'cmd.exe';
54
+
55
+ const child = spawn(comspec, ['/d', '/s', '/c', command, ...args], {
56
+ cwd: options.cwd,
57
+ env: process.env,
58
+ windowsHide: true,
59
+ shell: false,
60
+ });
61
+
62
+ let stdout = '';
63
+ let stderr = '';
64
+
65
+ child.stdout.on('data', (chunk) => {
66
+ stdout += String(chunk);
67
+ });
68
+
69
+ child.stderr.on('data', (chunk) => {
70
+ stderr += String(chunk);
71
+ });
72
+
73
+ child.on('error', (error) => {
74
+ reject(new Error(`${command} ${args.join(' ')} ล้มเหลว: ${error.message}`));
75
+ });
76
+
77
+ child.on('close', (code) => {
78
+ const out = stdout.trim();
79
+ const err = stderr.trim();
80
+ if (code === 0) {
81
+ resolve({ stdout: out, stderr: err });
82
+ return;
83
+ }
84
+ const detail = err || out || `exit code ${code}`;
85
+ reject(new Error(`${command} ${args.join(' ')} ล้มเหลว: ${detail}`));
86
+ });
87
+ });
88
+ }
89
+
46
90
  try {
47
91
  const { stdout, stderr } = await execFileAsync(command, args, {
48
92
  cwd: options.cwd,
49
93
  env: process.env,
50
94
  maxBuffer: 1024 * 1024 * 4,
51
95
  windowsHide: true,
96
+ shell: true,
52
97
  });
53
98
  return {
54
99
  stdout: (stdout || '').trim(),
@@ -58,7 +103,7 @@ class OpenClawCLI {
58
103
  const stderr = error.stderr ? String(error.stderr).trim() : '';
59
104
  const stdout = error.stdout ? String(error.stdout).trim() : '';
60
105
  const detail = stderr || stdout || error.message;
61
- throw new Error(`${command} ${args.join(' ')} ล้มเหลว: ${detail}`);
106
+ throw new Error(`${command} ${args.join(' ')} failed: ${detail}`);
62
107
  }
63
108
  }
64
109
 
@@ -73,7 +118,7 @@ class OpenClawCLI {
73
118
 
74
119
  async installSkill(skillName, skillVersion, skillsPath) {
75
120
  if (!this.hasClawhub()) {
76
- throw new Error('ไม่พบ CLI "clawhub" (ต้องใช้สำหรับติดตั้ง skill จาก registry)');
121
+ throw new Error('CLI "clawhub" not found (required to install skills from registry)');
77
122
  }
78
123
 
79
124
  const workdir = path.dirname(skillsPath);
@@ -140,17 +185,17 @@ class OpenClawCLI {
140
185
 
141
186
  async installSkillFromGit(skill, skillsPath) {
142
187
  if (!this.hasGit()) {
143
- throw new Error('ไม่พบคำสั่ง "git" สำหรับ fallback install');
188
+ throw new Error('git command not found (required for fallback install)');
144
189
  }
145
190
 
146
191
  const repoUrl = this.resolveGitRepository(skill);
147
192
  if (!repoUrl) {
148
- throw new Error('ไม่มีข้อมูล git repository สำหรับ fallback install');
193
+ throw new Error('No git repository information available for fallback install');
149
194
  }
150
195
 
151
196
  const skillDirName = this.resolveSkillDirName(skill);
152
197
  if (!skillDirName) {
153
- throw new Error('ไม่สามารถระบุชื่อโฟลเดอร์ skill จากข้อมูลที่มี');
198
+ throw new Error('Unable to determine skill directory name from provided data');
154
199
  }
155
200
 
156
201
  const targetDir = path.join(skillsPath, skillDirName);
@@ -170,7 +215,7 @@ class OpenClawCLI {
170
215
 
171
216
  const skillFile = path.join(targetDir, 'SKILL.md');
172
217
  if (!fs.existsSync(skillFile)) {
173
- throw new Error(`repo "${repoUrl}" ไม่มีไฟล์ SKILL.md ที่ root`);
218
+ throw new Error(`Repository "${repoUrl}" does not contain SKILL.md at the repository root`);
174
219
  }
175
220
 
176
221
  return {
@@ -192,7 +237,7 @@ class OpenClawCLI {
192
237
 
193
238
  async addCronJob({ name, description, schedule, message }) {
194
239
  if (!this.hasOpenClaw()) {
195
- throw new Error('ไม่พบ CLI "openclaw" (ต้องใช้สำหรับสร้าง cronjob แบบ native)');
240
+ throw new Error('openclaw CLI not found (required to create native cronjobs)');
196
241
  }
197
242
 
198
243
  const args = [
@@ -222,7 +267,7 @@ class OpenClawCLI {
222
267
 
223
268
  async listCronJobs() {
224
269
  if (!this.hasOpenClaw()) {
225
- throw new Error('ไม่พบ CLI "openclaw" (ต้องใช้สำหรับอ่าน cronjobs)');
270
+ throw new Error('openclaw CLI not found (required to list cronjobs)');
226
271
  }
227
272
 
228
273
  const result = await this.run(this.getOpenClawBin(), ['cron', 'list', '--all', '--json']);
@@ -232,7 +277,7 @@ class OpenClawCLI {
232
277
 
233
278
  async removeCronJob(jobId) {
234
279
  if (!this.hasOpenClaw()) {
235
- throw new Error('ไม่พบ CLI "openclaw" (ต้องใช้สำหรับลบ cronjob)');
280
+ throw new Error('openclaw CLI not found (required to remove cronjobs)');
236
281
  }
237
282
 
238
283
  await this.run(this.getOpenClawBin(), ['cron', 'rm', jobId, '--json']);
@@ -241,7 +286,7 @@ class OpenClawCLI {
241
286
 
242
287
  async editCronJob(jobId, updates = {}) {
243
288
  if (!this.hasOpenClaw()) {
244
- throw new Error('ไม่พบ CLI "openclaw" (ต้องใช้สำหรับแก้ไข cronjob)');
289
+ throw new Error('openclaw CLI not found (required to edit cronjobs)');
245
290
  }
246
291
 
247
292
  const args = ['cron', 'edit', jobId];