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.
- package/README.md +164 -49
- package/bin/clawflowhub.js +150 -62
- package/package.json +17 -5
- package/src/commands/create.js +286 -0
- package/src/commands/cron.js +78 -18
- package/src/commands/doctor.js +146 -0
- package/src/commands/explore.js +49 -0
- package/src/commands/init.js +3 -3
- package/src/commands/register.js +83 -0
- package/src/commands/update.js +35 -0
- package/src/core/ConfigManager.js +132 -132
- package/src/core/CronManager.js +245 -245
- package/src/core/Installer.js +226 -109
- package/src/core/OpenClawCLI.js +52 -7
- package/src/core/TerminalUI.js +10 -13
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -35
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -23
- package/.github/pull_request_template.md +0 -19
- package/CODE_OF_CONDUCT.md +0 -29
- package/SECURITY.md +0 -20
- package/SUPPORT.md +0 -21
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create command - Generate templates for skills and agents
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const chalk = require("chalk");
|
|
6
|
+
const fs = require("fs-extra");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const inquirer = require("inquirer");
|
|
9
|
+
const yaml = require("yaml");
|
|
10
|
+
|
|
11
|
+
module.exports = async function createCommand(type, name, _options) {
|
|
12
|
+
console.log(chalk.cyan.bold("\n🛠️ ClawFlow Create\n"));
|
|
13
|
+
|
|
14
|
+
// 1. Determine Type
|
|
15
|
+
let targetType = type;
|
|
16
|
+
if (!targetType || !["skill", "agent"].includes(targetType.toLowerCase())) {
|
|
17
|
+
const { typeAnswer } = await inquirer.prompt([
|
|
18
|
+
{
|
|
19
|
+
type: "list",
|
|
20
|
+
name: "typeAnswer",
|
|
21
|
+
message: "What do you want to create?",
|
|
22
|
+
choices: [
|
|
23
|
+
{ name: "Skill (Executable tool/function)", value: "skill" },
|
|
24
|
+
{ name: "Agent (High-level LLM orchestrator)", value: "agent" },
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
]);
|
|
28
|
+
targetType = typeAnswer;
|
|
29
|
+
} else {
|
|
30
|
+
targetType = targetType.toLowerCase();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 2. Determine Name
|
|
34
|
+
let targetName = name;
|
|
35
|
+
if (!targetName) {
|
|
36
|
+
const { nameAnswer } = await inquirer.prompt([
|
|
37
|
+
{
|
|
38
|
+
type: "input",
|
|
39
|
+
name: "nameAnswer",
|
|
40
|
+
message: `Enter ${targetType} name:`,
|
|
41
|
+
default: `my-cool-${targetType}`,
|
|
42
|
+
validate: (input) => {
|
|
43
|
+
if (/^[a-z0-9-_]+$/.test(input)) return true;
|
|
44
|
+
return "Name must be lowercase and contain only alphanumeric characters, dashes, or underscores.";
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
]);
|
|
48
|
+
targetName = nameAnswer;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const targetDir = path.resolve(process.cwd(), targetName);
|
|
52
|
+
|
|
53
|
+
// 3. Check if directory exists
|
|
54
|
+
if (fs.existsSync(targetDir)) {
|
|
55
|
+
const { overwrite } = await inquirer.prompt([
|
|
56
|
+
{
|
|
57
|
+
type: "confirm",
|
|
58
|
+
name: "overwrite",
|
|
59
|
+
message: `Directory ${targetName} already exists. Overwrite?`,
|
|
60
|
+
default: false,
|
|
61
|
+
},
|
|
62
|
+
]);
|
|
63
|
+
if (!overwrite) {
|
|
64
|
+
console.log(chalk.yellow("Aborted."));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
await fs.remove(targetDir);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 4. Gather Metadata
|
|
71
|
+
const metadata = await inquirer.prompt([
|
|
72
|
+
{
|
|
73
|
+
type: "input",
|
|
74
|
+
name: "description",
|
|
75
|
+
message: "Description:",
|
|
76
|
+
default: `A new ${targetType} created with ClawFlow`,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
type: "input",
|
|
80
|
+
name: "author",
|
|
81
|
+
message: "Author:",
|
|
82
|
+
default: process.env.USER || process.env.USERNAME || "anonymous",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
type: "list",
|
|
86
|
+
name: "language",
|
|
87
|
+
message: "Language:",
|
|
88
|
+
choices: ["TypeScript", "Python"],
|
|
89
|
+
when: targetType === "skill",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
type: "confirm",
|
|
93
|
+
name: "gitInit",
|
|
94
|
+
message: "Initialize a git repository?",
|
|
95
|
+
default: true,
|
|
96
|
+
},
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
// 5. Generate Files
|
|
100
|
+
try {
|
|
101
|
+
if (targetType === "skill") {
|
|
102
|
+
await generateSkill(targetDir, targetName, metadata);
|
|
103
|
+
} else {
|
|
104
|
+
await generateAgent(targetDir, targetName, metadata);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Git Init
|
|
108
|
+
if (metadata.gitInit) {
|
|
109
|
+
try {
|
|
110
|
+
const { execSync } = require("child_process");
|
|
111
|
+
execSync("git init", { cwd: targetDir, stdio: "ignore" });
|
|
112
|
+
console.log(chalk.gray(" ✓ Git repository initialized"));
|
|
113
|
+
} catch (e) {
|
|
114
|
+
console.log(chalk.yellow(" ⚠️ Failed to initialize git repository"));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
console.log(
|
|
119
|
+
chalk.green(`\n✅ ${targetType} "${targetName}" created successfully!`),
|
|
120
|
+
);
|
|
121
|
+
console.log(chalk.gray(`Path: ${targetDir}\n`));
|
|
122
|
+
console.log(chalk.cyan("Next steps:"));
|
|
123
|
+
console.log(chalk.white(` cd ${targetName}`));
|
|
124
|
+
if (targetType === "skill" && metadata.language === "TypeScript") {
|
|
125
|
+
console.log(chalk.white(" npm install"));
|
|
126
|
+
}
|
|
127
|
+
console.log(
|
|
128
|
+
chalk.white(" Edit skill.yaml/agent.yaml to define your logic\n"),
|
|
129
|
+
);
|
|
130
|
+
} catch (err) {
|
|
131
|
+
console.error(chalk.red("\n❌ Error generating files:"), err.message);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
async function generateSkill(dir, name, metadata) {
|
|
136
|
+
const isTS = metadata.language === "TypeScript";
|
|
137
|
+
const ext = isTS ? "ts" : "py";
|
|
138
|
+
|
|
139
|
+
await fs.ensureDir(dir);
|
|
140
|
+
await fs.ensureDir(path.join(dir, "examples"));
|
|
141
|
+
|
|
142
|
+
// skill.yaml
|
|
143
|
+
const skillYaml = {
|
|
144
|
+
name: name,
|
|
145
|
+
version: "1.0.0",
|
|
146
|
+
description: metadata.description,
|
|
147
|
+
author: metadata.author,
|
|
148
|
+
type: "tool",
|
|
149
|
+
runtime: metadata.language.toLowerCase(),
|
|
150
|
+
entry: `index.${ext}`,
|
|
151
|
+
parameters: {
|
|
152
|
+
type: "object",
|
|
153
|
+
properties: {
|
|
154
|
+
input: {
|
|
155
|
+
type: "string",
|
|
156
|
+
description: "Input for the skill",
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
required: ["input"],
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
await fs.writeFile(path.join(dir, "skill.yaml"), yaml.stringify(skillYaml));
|
|
164
|
+
|
|
165
|
+
// Entry file
|
|
166
|
+
let content = "";
|
|
167
|
+
if (isTS) {
|
|
168
|
+
content = `
|
|
169
|
+
// ${name} - Generated by ClawFlow
|
|
170
|
+
export interface Input {
|
|
171
|
+
input: string;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export async function run(params: Input) {
|
|
175
|
+
console.log(\`Running ${name} with input: \${params.input}\`);
|
|
176
|
+
return {
|
|
177
|
+
success: true,
|
|
178
|
+
message: \`Processed: \${params.input}\`,
|
|
179
|
+
timestamp: new Date().toISOString()
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
`;
|
|
183
|
+
// package.json for TS
|
|
184
|
+
const pkgJson = {
|
|
185
|
+
name: name,
|
|
186
|
+
version: "1.0.0",
|
|
187
|
+
description: metadata.description,
|
|
188
|
+
main: "index.ts",
|
|
189
|
+
scripts: {
|
|
190
|
+
test: 'echo "Error: no test specified" && exit 1',
|
|
191
|
+
},
|
|
192
|
+
dependencies: {
|
|
193
|
+
"@types/node": "^20.0.0",
|
|
194
|
+
typescript: "^5.0.0",
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
await fs.writeJson(path.join(dir, "package.json"), pkgJson, { spaces: 2 });
|
|
198
|
+
} else {
|
|
199
|
+
content = `
|
|
200
|
+
# ${name} - Generated by ClawFlow
|
|
201
|
+
import sys
|
|
202
|
+
import json
|
|
203
|
+
import datetime
|
|
204
|
+
|
|
205
|
+
def run(params):
|
|
206
|
+
input_str = params.get('input', '')
|
|
207
|
+
print(f"Running ${name} with input: {input_str}")
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
"success": True,
|
|
211
|
+
"message": f"Processed: {input_str}",
|
|
212
|
+
"timestamp": datetime.datetime.now().isoformat()
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if __name__ == "__main__":
|
|
216
|
+
# For local testing
|
|
217
|
+
test_params = {"input": "Hello from ClawFlow"}
|
|
218
|
+
print(json.dumps(run(test_params), indent=2))
|
|
219
|
+
`;
|
|
220
|
+
// requirements.txt for Python
|
|
221
|
+
await fs.writeFile(
|
|
222
|
+
path.join(dir, "requirements.txt"),
|
|
223
|
+
"# Add your dependencies here\n",
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
await fs.writeFile(path.join(dir, `index.${ext}`), content.trim());
|
|
228
|
+
|
|
229
|
+
// Example
|
|
230
|
+
const example = {
|
|
231
|
+
input: "Hello world",
|
|
232
|
+
};
|
|
233
|
+
await fs.writeJson(path.join(dir, "examples", "basic.json"), example, {
|
|
234
|
+
spaces: 2,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// README
|
|
238
|
+
await fs.writeFile(
|
|
239
|
+
path.join(dir, "README.md"),
|
|
240
|
+
`# ${name}\n\n${metadata.description}`,
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function generateAgent(dir, name, metadata) {
|
|
245
|
+
await fs.ensureDir(dir);
|
|
246
|
+
await fs.ensureDir(path.join(dir, "prompts"));
|
|
247
|
+
await fs.ensureDir(path.join(dir, "tools"));
|
|
248
|
+
|
|
249
|
+
// agent.yaml
|
|
250
|
+
const agentYaml = {
|
|
251
|
+
name: name,
|
|
252
|
+
version: "1.0.0",
|
|
253
|
+
description: metadata.description,
|
|
254
|
+
author: metadata.author,
|
|
255
|
+
model: "gpt-4-turbo",
|
|
256
|
+
prompts: {
|
|
257
|
+
system: "prompts/system.md",
|
|
258
|
+
user: "prompts/user.md",
|
|
259
|
+
},
|
|
260
|
+
tools: [
|
|
261
|
+
// 'example-skill'
|
|
262
|
+
],
|
|
263
|
+
config: {
|
|
264
|
+
temperature: 0.7,
|
|
265
|
+
max_tokens: 1000,
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
await fs.writeFile(path.join(dir, "agent.yaml"), yaml.stringify(agentYaml));
|
|
270
|
+
|
|
271
|
+
// Prompts
|
|
272
|
+
await fs.writeFile(
|
|
273
|
+
path.join(dir, "prompts", "system.md"),
|
|
274
|
+
`# System Prompt for ${name}\n\nYou are a helpful assistant specialized in...`,
|
|
275
|
+
);
|
|
276
|
+
await fs.writeFile(
|
|
277
|
+
path.join(dir, "prompts", "user.md"),
|
|
278
|
+
`# User Prompt for ${name}\n\nTask: {{input}}`,
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
// README
|
|
282
|
+
await fs.writeFile(
|
|
283
|
+
path.join(dir, "README.md"),
|
|
284
|
+
`# ${name} (Agent)\n\n${metadata.description}`,
|
|
285
|
+
);
|
|
286
|
+
}
|
package/src/commands/cron.js
CHANGED
|
@@ -2,20 +2,56 @@
|
|
|
2
2
|
* Cron commands - จัดการ cronjobs
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
const chalk = require(
|
|
6
|
-
const ClawFlow = require(
|
|
7
|
-
const { normalizeCronExpression } = require(
|
|
5
|
+
const chalk = require("chalk");
|
|
6
|
+
const ClawFlow = require("../index");
|
|
7
|
+
const { normalizeCronExpression } = require("../core/CronFormat");
|
|
8
|
+
const YAML = require("yaml");
|
|
8
9
|
|
|
9
10
|
function parseJsonParams(params) {
|
|
10
|
-
if (params === undefined || params === null || params ===
|
|
11
|
+
if (params === undefined || params === null || params === "") {
|
|
11
12
|
return {};
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
if (typeof params ===
|
|
15
|
+
if (typeof params === "object") {
|
|
15
16
|
return params;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
const raw = String(params).trim();
|
|
20
|
+
const unquoted =
|
|
21
|
+
(raw.startsWith("'") && raw.endsWith("'")) ||
|
|
22
|
+
(raw.startsWith('"') && raw.endsWith('"'))
|
|
23
|
+
? raw.slice(1, -1).trim()
|
|
24
|
+
: raw;
|
|
25
|
+
|
|
26
|
+
const candidates = [raw, unquoted];
|
|
27
|
+
|
|
28
|
+
for (const candidate of candidates) {
|
|
29
|
+
if (!candidate) continue;
|
|
30
|
+
try {
|
|
31
|
+
const parsed = JSON.parse(candidate);
|
|
32
|
+
if (parsed && typeof parsed === "object") {
|
|
33
|
+
return parsed;
|
|
34
|
+
}
|
|
35
|
+
} catch (_error) {
|
|
36
|
+
// Continue to YAML fallback
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
for (const candidate of candidates) {
|
|
41
|
+
if (!candidate) continue;
|
|
42
|
+
try {
|
|
43
|
+
const parsed = YAML.parse(candidate);
|
|
44
|
+
if (parsed && typeof parsed === "object") {
|
|
45
|
+
return parsed;
|
|
46
|
+
}
|
|
47
|
+
} catch (_error) {
|
|
48
|
+
// Continue
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
throw new Error(
|
|
53
|
+
'รูปแบบ --params ไม่ถูกต้อง (ใช้ JSON object เช่น {"message":"hello"} หรือ YAML object เช่น message: hello)',
|
|
54
|
+
);
|
|
19
55
|
}
|
|
20
56
|
|
|
21
57
|
function resolveScheduleInput(options) {
|
|
@@ -23,7 +59,7 @@ function resolveScheduleInput(options) {
|
|
|
23
59
|
return normalizeCronExpression(`every ${options.every}`);
|
|
24
60
|
}
|
|
25
61
|
|
|
26
|
-
return normalizeCronExpression(options.schedule ||
|
|
62
|
+
return normalizeCronExpression(options.schedule || "*/5 * * * *");
|
|
27
63
|
}
|
|
28
64
|
|
|
29
65
|
module.exports = {
|
|
@@ -31,18 +67,21 @@ module.exports = {
|
|
|
31
67
|
const hub = new ClawFlow();
|
|
32
68
|
|
|
33
69
|
try {
|
|
34
|
-
console.log(chalk.cyan.bold(
|
|
70
|
+
console.log(chalk.cyan.bold("\n⏰ Configured Cronjobs:\n"));
|
|
35
71
|
|
|
36
72
|
const crons = await hub.listCrons();
|
|
37
73
|
|
|
38
74
|
if (crons.length === 0) {
|
|
39
|
-
console.log(chalk.gray(
|
|
75
|
+
console.log(chalk.gray(" No cronjobs configured"));
|
|
40
76
|
} else {
|
|
41
77
|
crons.forEach((job) => {
|
|
42
|
-
const status =
|
|
78
|
+
const status =
|
|
79
|
+
job.enabled !== false ? chalk.green("●") : chalk.gray("○");
|
|
43
80
|
console.log(` ${status} ${chalk.bold(job.skill || job.name)}`);
|
|
44
|
-
console.log(` ${chalk.blue(
|
|
45
|
-
console.log(
|
|
81
|
+
console.log(` ${chalk.blue("Schedule:")} ${job.schedule}`);
|
|
82
|
+
console.log(
|
|
83
|
+
` ${chalk.blue("Description:")} ${job.description || "-"}`,
|
|
84
|
+
);
|
|
46
85
|
console.log();
|
|
47
86
|
});
|
|
48
87
|
}
|
|
@@ -53,7 +92,7 @@ module.exports = {
|
|
|
53
92
|
},
|
|
54
93
|
|
|
55
94
|
async add(skillName, options) {
|
|
56
|
-
const { params =
|
|
95
|
+
const { params = "{}", description = "" } = options;
|
|
57
96
|
|
|
58
97
|
const hub = new ClawFlow();
|
|
59
98
|
|
|
@@ -61,9 +100,26 @@ module.exports = {
|
|
|
61
100
|
const parsedParams = parseJsonParams(params);
|
|
62
101
|
const schedule = resolveScheduleInput(options);
|
|
63
102
|
|
|
64
|
-
|
|
103
|
+
if (options.dryRun) {
|
|
104
|
+
console.log(chalk.yellow("\n📋 Cronjob Preview (Dry Run):\n"));
|
|
105
|
+
console.log(` Skill: ${chalk.bold(skillName)}`);
|
|
106
|
+
console.log(` Schedule: ${schedule}`);
|
|
107
|
+
console.log(` Description: ${description || "-"}`);
|
|
108
|
+
console.log(` Params: ${JSON.stringify(parsedParams)}`);
|
|
109
|
+
console.log(chalk.gray("\n(Nothing was added)"));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
console.log(
|
|
114
|
+
chalk.cyan(`\n➕ Adding cronjob for ${chalk.bold(skillName)}...\n`),
|
|
115
|
+
);
|
|
65
116
|
|
|
66
|
-
const job = await hub.cronManager.add(
|
|
117
|
+
const job = await hub.cronManager.add(
|
|
118
|
+
skillName,
|
|
119
|
+
schedule,
|
|
120
|
+
parsedParams,
|
|
121
|
+
description,
|
|
122
|
+
);
|
|
67
123
|
|
|
68
124
|
console.log(chalk.green(`\n✓ Cronjob added successfully`));
|
|
69
125
|
console.log(` ID: ${job.id}`);
|
|
@@ -78,7 +134,9 @@ module.exports = {
|
|
|
78
134
|
const hub = new ClawFlow();
|
|
79
135
|
|
|
80
136
|
try {
|
|
81
|
-
console.log(
|
|
137
|
+
console.log(
|
|
138
|
+
chalk.cyan(`\n🗑️ Removing cronjob ${chalk.bold(jobId)}...\n`),
|
|
139
|
+
);
|
|
82
140
|
|
|
83
141
|
await hub.cronManager.remove(jobId);
|
|
84
142
|
|
|
@@ -97,7 +155,7 @@ module.exports = {
|
|
|
97
155
|
if (options.schedule || options.every) {
|
|
98
156
|
updates.schedule = resolveScheduleInput(options);
|
|
99
157
|
}
|
|
100
|
-
if (typeof options.description ===
|
|
158
|
+
if (typeof options.description === "string") {
|
|
101
159
|
updates.description = options.description;
|
|
102
160
|
}
|
|
103
161
|
if (options.params !== undefined) {
|
|
@@ -105,7 +163,9 @@ module.exports = {
|
|
|
105
163
|
}
|
|
106
164
|
|
|
107
165
|
if (Object.keys(updates).length === 0) {
|
|
108
|
-
throw new Error(
|
|
166
|
+
throw new Error(
|
|
167
|
+
"No update data provided (use --schedule/--every/--description/--params)",
|
|
168
|
+
);
|
|
109
169
|
}
|
|
110
170
|
|
|
111
171
|
const updated = await hub.cronManager.edit(jobId, updates);
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doctor command - Check system compatibility and configuration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const chalk = require("chalk");
|
|
6
|
+
const fs = require("fs-extra");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const { execSync } = require("child_process");
|
|
9
|
+
|
|
10
|
+
module.exports = async function doctorCommand() {
|
|
11
|
+
console.log(chalk.cyan.bold("\n🩺 ClawFlow Doctor - System Diagnostic\n"));
|
|
12
|
+
|
|
13
|
+
const checks = [
|
|
14
|
+
{ name: "Node.js version", check: checkNode },
|
|
15
|
+
{ name: "OpenClaw CLI", check: checkOpenClaw },
|
|
16
|
+
{ name: "ClawHub CLI", check: checkClawHub },
|
|
17
|
+
{ name: "Git CLI", check: checkGit },
|
|
18
|
+
{ name: "Configuration", check: checkConfig },
|
|
19
|
+
{ name: "Skills Path", check: checkSkillsPath },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
let errors = 0;
|
|
23
|
+
let warnings = 0;
|
|
24
|
+
|
|
25
|
+
for (const item of checks) {
|
|
26
|
+
process.stdout.write(chalk.white(` Checking ${item.name}... `));
|
|
27
|
+
try {
|
|
28
|
+
const result = await item.check();
|
|
29
|
+
if (result.status === "ok") {
|
|
30
|
+
process.stdout.write(chalk.green("✓\n"));
|
|
31
|
+
if (result.message) console.log(chalk.gray(` ${result.message}`));
|
|
32
|
+
} else if (result.status === "warn") {
|
|
33
|
+
process.stdout.write(chalk.yellow("⚠️\n"));
|
|
34
|
+
console.log(chalk.yellow(` ${result.message}`));
|
|
35
|
+
warnings++;
|
|
36
|
+
} else {
|
|
37
|
+
process.stdout.write(chalk.red("✗\n"));
|
|
38
|
+
console.log(chalk.red(` ${result.message}`));
|
|
39
|
+
errors++;
|
|
40
|
+
}
|
|
41
|
+
} catch (err) {
|
|
42
|
+
process.stdout.write(chalk.red("✗\n"));
|
|
43
|
+
console.log(chalk.red(` Error: ${err.message}`));
|
|
44
|
+
errors++;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log("\n" + "─".repeat(50));
|
|
49
|
+
if (errors === 0 && warnings === 0) {
|
|
50
|
+
console.log(
|
|
51
|
+
chalk.green.bold("✨ Everything looks great! You are ready to go."),
|
|
52
|
+
);
|
|
53
|
+
} else if (errors === 0) {
|
|
54
|
+
console.log(
|
|
55
|
+
chalk.yellow.bold(`⚠️ System ready but with ${warnings} warning(s).`),
|
|
56
|
+
);
|
|
57
|
+
} else {
|
|
58
|
+
console.log(
|
|
59
|
+
chalk.red.bold(`❌ Found ${errors} error(s) and ${warnings} warning(s).`),
|
|
60
|
+
);
|
|
61
|
+
console.log(
|
|
62
|
+
chalk.gray(" Please fix the errors above before continuing."),
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
console.log();
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
async function checkNode() {
|
|
69
|
+
const version = process.version;
|
|
70
|
+
const major = parseInt(version.slice(1).split(".")[0], 10);
|
|
71
|
+
if (major < 16) {
|
|
72
|
+
return {
|
|
73
|
+
status: "error",
|
|
74
|
+
message: `Node.js ${version} is too old. Please use v16 or newer.`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return { status: "ok", message: version };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function checkOpenClaw() {
|
|
81
|
+
try {
|
|
82
|
+
const out = execSync("openclaw --version", { stdio: "pipe" })
|
|
83
|
+
.toString()
|
|
84
|
+
.trim();
|
|
85
|
+
return { status: "ok", message: out };
|
|
86
|
+
} catch (e) {
|
|
87
|
+
return {
|
|
88
|
+
status: "warn",
|
|
89
|
+
message: "OpenClaw CLI not found in PATH. Make sure it is installed.",
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function checkClawHub() {
|
|
95
|
+
try {
|
|
96
|
+
const out = execSync("clawhub --version", { stdio: "pipe" })
|
|
97
|
+
.toString()
|
|
98
|
+
.trim();
|
|
99
|
+
return { status: "ok", message: out };
|
|
100
|
+
} catch (e) {
|
|
101
|
+
return {
|
|
102
|
+
status: "warn",
|
|
103
|
+
message: "ClawHub CLI not found. Registry installs might fail.",
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function checkGit() {
|
|
109
|
+
try {
|
|
110
|
+
const out = execSync("git --version", { stdio: "pipe" }).toString().trim();
|
|
111
|
+
return { status: "ok", message: out };
|
|
112
|
+
} catch (e) {
|
|
113
|
+
return {
|
|
114
|
+
status: "error",
|
|
115
|
+
message: "Git is required for many ClawFlow features.",
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function checkConfig() {
|
|
121
|
+
const configFile = path.join(process.cwd(), ".clawflowhub", "config.json");
|
|
122
|
+
if (fs.existsSync(configFile)) {
|
|
123
|
+
return { status: "ok", message: "Local configuration found." };
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
status: "warn",
|
|
127
|
+
message: 'No local .clawflowhub folder. Run "clawflow init".',
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function checkSkillsPath() {
|
|
132
|
+
const configFile = path.join(process.cwd(), ".clawflowhub", "config.json");
|
|
133
|
+
if (!fs.existsSync(configFile))
|
|
134
|
+
return { status: "warn", message: "Unknown skills path." };
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const config = fs.readJsonSync(configFile);
|
|
138
|
+
const skillsPath = config.openclaw?.skillsPath;
|
|
139
|
+
if (skillsPath && fs.existsSync(skillsPath)) {
|
|
140
|
+
return { status: "ok", message: skillsPath };
|
|
141
|
+
}
|
|
142
|
+
return { status: "error", message: `Skills path not found: ${skillsPath}` };
|
|
143
|
+
} catch (e) {
|
|
144
|
+
return { status: "error", message: "Failed to read config.json" };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Explore command - Discover new skills
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const chalk = require("chalk");
|
|
6
|
+
const boxen = require("boxen");
|
|
7
|
+
|
|
8
|
+
module.exports = async function exploreCommand() {
|
|
9
|
+
console.log(chalk.magenta.bold("\n✨ Explore ClawHub Ecosystem\n"));
|
|
10
|
+
|
|
11
|
+
const featured = [
|
|
12
|
+
{
|
|
13
|
+
name: "ClawGraph-Visualizer",
|
|
14
|
+
desc: "Visualize your agent node relations",
|
|
15
|
+
author: "clawflow-team",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: "Crypto-Whale-Watcher",
|
|
19
|
+
desc: "Monitor large transactions on-chain",
|
|
20
|
+
author: "crypto-dev",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "Research-Pro-Agent",
|
|
24
|
+
desc: "Multi-step research orchestrator",
|
|
25
|
+
author: "openkrab",
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
console.log(chalk.white("Featured Skills & Agents:"));
|
|
30
|
+
|
|
31
|
+
featured.forEach((item) => {
|
|
32
|
+
const content = `${chalk.bold.cyan(item.name)}\n${chalk.gray(item.desc)}\n${chalk.yellow("By: " + item.author)}`;
|
|
33
|
+
console.log(
|
|
34
|
+
boxen(content, {
|
|
35
|
+
padding: 1,
|
|
36
|
+
margin: { top: 1, bottom: 0 },
|
|
37
|
+
borderStyle: "round",
|
|
38
|
+
borderColor: "magenta",
|
|
39
|
+
}),
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
console.log(
|
|
44
|
+
chalk.gray('\nRun "clawflow search <query>" to find more packages!'),
|
|
45
|
+
);
|
|
46
|
+
console.log(
|
|
47
|
+
chalk.gray("Or visit https://hub.clawflow.dev for the full registry.\n"),
|
|
48
|
+
);
|
|
49
|
+
};
|
package/src/commands/init.js
CHANGED
|
@@ -65,9 +65,9 @@ module.exports = async function initCommand(options) {
|
|
|
65
65
|
// สร้าง config
|
|
66
66
|
const config = {
|
|
67
67
|
version: '1.0.0',
|
|
68
|
-
openclaw: {
|
|
69
|
-
baseUrl: 'http://localhost:
|
|
70
|
-
apiKey: null,
|
|
68
|
+
openclaw: {
|
|
69
|
+
baseUrl: 'http://localhost:18789',
|
|
70
|
+
apiKey: null,
|
|
71
71
|
cliBin: answers.openclawBin,
|
|
72
72
|
clawhubBin: answers.clawhubBin,
|
|
73
73
|
workspacePath: path.join(cwd, '.openclaw'),
|