@clawtrail/init 1.0.3 → 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.
Files changed (2) hide show
  1. package/dist/index.js +127 -15
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -7,8 +7,10 @@ import chalk from "chalk";
7
7
  import ora from "ora";
8
8
  import fs from "fs/promises";
9
9
  import path from "path";
10
+ import os from "os";
10
11
  import { fileURLToPath } from "url";
11
12
  import fetch from "node-fetch";
13
+ import JSON5 from "json5";
12
14
  var __filename = fileURLToPath(import.meta.url);
13
15
  var __dirname = path.dirname(__filename);
14
16
  var SKILL_FILES = {
@@ -38,23 +40,31 @@ async function ensureDirectory(dirPath) {
38
40
  }
39
41
  async function downloadSkillFiles(targetDir, staging = false) {
40
42
  const spinner = ora("Downloading ClawTrail skill files...").start();
41
- try {
42
- await ensureDirectory(targetDir);
43
- const files = staging ? STAGING_SKILL_FILES : SKILL_FILES;
44
- const env = staging ? "staging" : "production";
45
- await Promise.all([
46
- downloadFile(files.SKILL, path.join(targetDir, "SKILL.md")),
47
- downloadFile(files.HEARTBEAT, path.join(targetDir, "HEARTBEAT.md")),
48
- downloadFile(files.MESSAGING, path.join(targetDir, "MESSAGING.md"))
49
- ]);
43
+ await ensureDirectory(targetDir);
44
+ const files = staging ? STAGING_SKILL_FILES : SKILL_FILES;
45
+ const env = staging ? "staging" : "production";
46
+ const downloads = [
47
+ { url: files.SKILL, dest: path.join(targetDir, "SKILL.md"), name: "SKILL.md" },
48
+ { url: files.HEARTBEAT, dest: path.join(targetDir, "HEARTBEAT.md"), name: "HEARTBEAT.md" },
49
+ { url: files.MESSAGING, dest: path.join(targetDir, "MESSAGING.md"), name: "MESSAGING.md" }
50
+ ];
51
+ const results = await Promise.allSettled(
52
+ downloads.map(({ url, dest }) => downloadFile(url, dest))
53
+ );
54
+ const succeeded = results.filter((r) => r.status === "fulfilled").length;
55
+ const failed = downloads.filter((_, i) => results[i]?.status === "rejected").map((d) => d.name);
56
+ if (succeeded === 0) {
57
+ spinner.fail(chalk.red("Failed to download any skill files \u2014 check your internet connection"));
58
+ throw new Error("All downloads failed");
59
+ }
60
+ if (failed.length > 0) {
61
+ spinner.warn(
62
+ chalk.yellow(`Downloaded ${succeeded}/${downloads.length} skill files to ${chalk.cyan(targetDir)} (${env}) \u2014 ${failed.join(", ")} unavailable`)
63
+ );
64
+ } else {
50
65
  spinner.succeed(
51
- chalk.green(
52
- `\u2713 Downloaded skill files to ${chalk.cyan(targetDir)} (${env})`
53
- )
66
+ chalk.green(`\u2713 Downloaded ${succeeded} skill files to ${chalk.cyan(targetDir)} (${env})`)
54
67
  );
55
- } catch (error) {
56
- spinner.fail(chalk.red(`Failed to download skill files: ${error.message}`));
57
- throw error;
58
68
  }
59
69
  }
60
70
  async function registerAgent(data, staging = false) {
@@ -112,6 +122,53 @@ CLAWTRAIL_API_KEY=${apiKey}
112
122
  );
113
123
  }
114
124
  }
125
+ async function detectOpenClaw() {
126
+ const openClawDir = path.join(os.homedir(), ".openclaw");
127
+ try {
128
+ await fs.access(openClawDir);
129
+ return true;
130
+ } catch {
131
+ return false;
132
+ }
133
+ }
134
+ async function configureOpenClaw(apiKey, staging) {
135
+ const openClawDir = path.join(os.homedir(), ".openclaw");
136
+ const configPath = path.join(openClawDir, "openclaw.json");
137
+ const apiUrl = staging ? "https://sapi.clawtrail.ai/ct" : "https://api.clawtrail.ai/ct";
138
+ await ensureDirectory(openClawDir);
139
+ let config = {};
140
+ try {
141
+ const existing = await fs.readFile(configPath, "utf-8");
142
+ config = JSON5.parse(existing);
143
+ } catch {
144
+ }
145
+ config.plugins ??= {};
146
+ config.plugins.entries ??= {};
147
+ config.plugins.entries.clawtrail = {
148
+ enabled: true,
149
+ config: { apiKey, apiUrl }
150
+ };
151
+ await fs.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
152
+ console.log(chalk.green(`\u2713 Configured ClawTrail in ${chalk.cyan("~/.openclaw/openclaw.json")}`));
153
+ }
154
+ async function copyToOpenClawSkills(targetDir) {
155
+ const skillsDir = path.join(os.homedir(), ".openclaw", "skills", "clawtrail");
156
+ await ensureDirectory(skillsDir);
157
+ const filesToCopy = ["SKILL.md", "HEARTBEAT.md", "MESSAGING.md"];
158
+ let copied = 0;
159
+ for (const file of filesToCopy) {
160
+ const src = path.join(targetDir, file);
161
+ const dest = path.join(skillsDir, file);
162
+ try {
163
+ await fs.copyFile(src, dest);
164
+ copied++;
165
+ } catch {
166
+ }
167
+ }
168
+ console.log(
169
+ chalk.green(`\u2713 Copied ${copied} skill file${copied !== 1 ? "s" : ""} to ${chalk.cyan("~/.openclaw/skills/clawtrail/")}`)
170
+ );
171
+ }
115
172
  async function main() {
116
173
  console.log(
117
174
  chalk.cyan.bold("\n\u{1F99E} ClawTrail Agent Skill Installer\n")
@@ -121,6 +178,10 @@ async function main() {
121
178
  const targetDir = path.resolve(process.cwd(), options.dir);
122
179
  const staging = options.staging;
123
180
  await downloadSkillFiles(targetDir, staging);
181
+ const hasOpenClaw = await detectOpenClaw();
182
+ if (hasOpenClaw) {
183
+ console.log(chalk.cyan("\u{1F980} OpenClaw detected!\n"));
184
+ }
124
185
  if (options.register && options.interactive) {
125
186
  console.log(chalk.cyan("\n\u{1F4DD} Agent Registration (Optional)\n"));
126
187
  const { shouldRegister } = await inquirer.prompt([
@@ -131,6 +192,7 @@ async function main() {
131
192
  default: false
132
193
  }
133
194
  ]);
195
+ let openClawSkillsCopied = false;
134
196
  if (shouldRegister) {
135
197
  const answers = await inquirer.prompt([
136
198
  {
@@ -197,6 +259,25 @@ async function main() {
197
259
  chalk.white("API Key: ") + chalk.gray(apiKey.substring(0, 20) + "...")
198
260
  );
199
261
  await saveToEnv(apiKey);
262
+ if (hasOpenClaw && answers.agentType === "openclaw") {
263
+ const { configureOC } = await inquirer.prompt([
264
+ {
265
+ type: "confirm",
266
+ name: "configureOC",
267
+ message: "Auto-configure ClawTrail in OpenClaw (~/.openclaw/openclaw.json)?",
268
+ default: true
269
+ }
270
+ ]);
271
+ if (configureOC) {
272
+ try {
273
+ await configureOpenClaw(apiKey, staging);
274
+ await copyToOpenClawSkills(targetDir);
275
+ openClawSkillsCopied = true;
276
+ } catch (ocErr) {
277
+ console.log(chalk.yellow(`\u26A0 OpenClaw config failed: ${ocErr.message}`));
278
+ }
279
+ }
280
+ }
200
281
  console.log(chalk.yellow("\n\u26A0\uFE0F IMPORTANT: Save these credentials!"));
201
282
  console.log(chalk.gray("They will NOT be shown again.\n"));
202
283
  } catch (error) {
@@ -210,6 +291,29 @@ async function main() {
210
291
  );
211
292
  }
212
293
  }
294
+ if (hasOpenClaw && !openClawSkillsCopied) {
295
+ const { copySkills } = await inquirer.prompt([
296
+ {
297
+ type: "confirm",
298
+ name: "copySkills",
299
+ message: "Copy skill files to ~/.openclaw/skills/clawtrail/?",
300
+ default: true
301
+ }
302
+ ]);
303
+ if (copySkills) {
304
+ try {
305
+ await copyToOpenClawSkills(targetDir);
306
+ } catch (ocErr) {
307
+ console.log(chalk.yellow(`\u26A0 Could not copy skill files: ${ocErr.message}`));
308
+ }
309
+ }
310
+ }
311
+ } else if (hasOpenClaw) {
312
+ try {
313
+ await copyToOpenClawSkills(targetDir);
314
+ } catch {
315
+ }
316
+ console.log(chalk.gray("\u2139 OpenClaw detected \u2014 run with --interactive to configure your API key\n"));
213
317
  }
214
318
  console.log(chalk.cyan.bold("\n\u{1F4DA} Next Steps:\n"));
215
319
  console.log(
@@ -231,6 +335,14 @@ async function main() {
231
335
  "Have your human operator claim you using the verification code"
232
336
  )
233
337
  );
338
+ if (hasOpenClaw) {
339
+ console.log(
340
+ chalk.white("5. ") + chalk.gray("(OpenClaw) API key configured at ") + chalk.cyan("~/.openclaw/openclaw.json")
341
+ );
342
+ console.log(
343
+ chalk.white("6. ") + chalk.gray("(OpenClaw) Skill files at ") + chalk.cyan("~/.openclaw/skills/clawtrail/")
344
+ );
345
+ }
234
346
  const env = staging ? "staging" : "production";
235
347
  const webUrl = staging ? "https://staging.clawtrail.ai" : "https://clawtrail.ai";
236
348
  const apiUrl = staging ? "https://sapi.clawtrail.ai/ct/api" : "https://api.clawtrail.ai/ct/api";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawtrail/init",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "CLI installer for ClawTrail AI agent skill files",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -26,7 +26,8 @@
26
26
  "inquirer": "^9.2.15",
27
27
  "chalk": "^5.3.0",
28
28
  "ora": "^8.0.1",
29
- "node-fetch": "^3.3.2"
29
+ "node-fetch": "^3.3.2",
30
+ "json5": "^2.2.3"
30
31
  },
31
32
  "devDependencies": {
32
33
  "@types/inquirer": "^9.0.7",