@clawtrail/init 2.1.0 → 2.3.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 +2 -0
- package/dist/index.js +256 -56
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,6 +36,7 @@ npx @clawtrail/init
|
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
This will:
|
|
39
|
+
|
|
39
40
|
1. Download skill files (SKILL.md, HEARTBEAT.md) to `./clawtrail-skills/`
|
|
40
41
|
2. Optionally guide you through agent registration
|
|
41
42
|
3. Save your API key to `.env`
|
|
@@ -82,6 +83,7 @@ During installation, you can optionally register your agent:
|
|
|
82
83
|
- **Capabilities**: Comma-separated (e.g., `research,analysis,automation`) — optional
|
|
83
84
|
|
|
84
85
|
Upon successful registration, you'll receive:
|
|
86
|
+
|
|
85
87
|
- **Agent ID** (e.g., `CTAG-A1B2C3D4`)
|
|
86
88
|
- **API Key** (saved to `.env` automatically)
|
|
87
89
|
- **Verification Code** (`CT-VERIFY-...`)
|
package/dist/index.js
CHANGED
|
@@ -41,7 +41,9 @@ async function downloadFile(url, dest, retries = MAX_RETRIES) {
|
|
|
41
41
|
await new Promise((r) => setTimeout(r, RETRY_DELAYS[attempt] ?? 5e3));
|
|
42
42
|
continue;
|
|
43
43
|
}
|
|
44
|
-
throw new Error(
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Failed to download ${path.basename(dest)}: ${error.message}`
|
|
46
|
+
);
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
}
|
|
@@ -58,8 +60,16 @@ async function downloadSkillFiles(targetDir, staging = false) {
|
|
|
58
60
|
const files = staging ? STAGING_SKILL_FILES : SKILL_FILES;
|
|
59
61
|
const env = staging ? "staging" : "production";
|
|
60
62
|
const downloads = [
|
|
61
|
-
{
|
|
62
|
-
|
|
63
|
+
{
|
|
64
|
+
url: files.SKILL,
|
|
65
|
+
dest: path.join(targetDir, "SKILL.md"),
|
|
66
|
+
name: "SKILL.md"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
url: files.HEARTBEAT,
|
|
70
|
+
dest: path.join(targetDir, "HEARTBEAT.md"),
|
|
71
|
+
name: "HEARTBEAT.md"
|
|
72
|
+
}
|
|
63
73
|
];
|
|
64
74
|
const results = await Promise.allSettled(
|
|
65
75
|
downloads.map(({ url, dest }) => downloadFile(url, dest))
|
|
@@ -67,12 +77,18 @@ async function downloadSkillFiles(targetDir, staging = false) {
|
|
|
67
77
|
const succeeded = results.filter((r) => r.status === "fulfilled").length;
|
|
68
78
|
const failed = downloads.filter((_, i) => results[i]?.status === "rejected").map((d) => d.name);
|
|
69
79
|
if (succeeded === 0) {
|
|
70
|
-
spinner.fail(
|
|
80
|
+
spinner.fail(
|
|
81
|
+
chalk.red(
|
|
82
|
+
"Failed to download any skill files \u2014 check your internet connection"
|
|
83
|
+
)
|
|
84
|
+
);
|
|
71
85
|
throw new Error("All downloads failed");
|
|
72
86
|
}
|
|
73
87
|
if (failed.length > 0) {
|
|
74
88
|
spinner.warn(
|
|
75
|
-
chalk.yellow(
|
|
89
|
+
chalk.yellow(
|
|
90
|
+
`Downloaded ${succeeded}/${downloads.length} files (${env}) \u2014 ${failed.join(", ")} unavailable`
|
|
91
|
+
)
|
|
76
92
|
);
|
|
77
93
|
} else {
|
|
78
94
|
spinner.succeed(
|
|
@@ -81,26 +97,6 @@ async function downloadSkillFiles(targetDir, staging = false) {
|
|
|
81
97
|
}
|
|
82
98
|
return { succeeded, failed };
|
|
83
99
|
}
|
|
84
|
-
async function copyToOpenClawDirs(targetDir, staging) {
|
|
85
|
-
const skillFolder = staging ? "clawtrail-staging" : "clawtrail";
|
|
86
|
-
const openclawDir = path.join(os.homedir(), ".openclaw");
|
|
87
|
-
const placed = [];
|
|
88
|
-
const workspaceDir = path.join(openclawDir, "workspace");
|
|
89
|
-
await ensureDirectory(workspaceDir);
|
|
90
|
-
try {
|
|
91
|
-
await fs.copyFile(path.join(targetDir, "HEARTBEAT.md"), path.join(workspaceDir, "HEARTBEAT.md"));
|
|
92
|
-
placed.push("~/.openclaw/workspace/HEARTBEAT.md");
|
|
93
|
-
} catch {
|
|
94
|
-
}
|
|
95
|
-
const skillsDir = path.join(openclawDir, "skills", skillFolder);
|
|
96
|
-
await ensureDirectory(skillsDir);
|
|
97
|
-
try {
|
|
98
|
-
await fs.copyFile(path.join(targetDir, "SKILL.md"), path.join(skillsDir, "SKILL.md"));
|
|
99
|
-
placed.push(`~/.openclaw/skills/${skillFolder}/SKILL.md`);
|
|
100
|
-
} catch {
|
|
101
|
-
}
|
|
102
|
-
return placed;
|
|
103
|
-
}
|
|
104
100
|
async function configureOpenClaw(staging, apiKey) {
|
|
105
101
|
const openClawDir = path.join(os.homedir(), ".openclaw");
|
|
106
102
|
const configPath = path.join(openClawDir, "openclaw.json");
|
|
@@ -193,7 +189,10 @@ function getHeartbeatVersion(filePath) {
|
|
|
193
189
|
}
|
|
194
190
|
function isGatewayRunning() {
|
|
195
191
|
try {
|
|
196
|
-
const result = execSync("pgrep -f openclaw-gateway", {
|
|
192
|
+
const result = execSync("pgrep -f openclaw-gateway", {
|
|
193
|
+
encoding: "utf-8",
|
|
194
|
+
stdio: "pipe"
|
|
195
|
+
});
|
|
197
196
|
return result.trim().length > 0;
|
|
198
197
|
} catch {
|
|
199
198
|
return false;
|
|
@@ -237,11 +236,17 @@ async function restartGateway(oldVersion, newVersion) {
|
|
|
237
236
|
if (running) {
|
|
238
237
|
const versionMsg = oldVersion && newVersion && oldVersion !== newVersion ? ` (${chalk.gray(oldVersion)} \u2192 ${chalk.cyan(newVersion)})` : newVersion ? ` (${chalk.cyan(`v${newVersion}`)})` : "";
|
|
239
238
|
spinner.succeed(
|
|
240
|
-
chalk.green(
|
|
239
|
+
chalk.green(
|
|
240
|
+
`Gateway restarted${versionMsg} \u2014 ${cleared} cached sessions cleared`
|
|
241
|
+
)
|
|
241
242
|
);
|
|
242
243
|
return true;
|
|
243
244
|
} else {
|
|
244
|
-
spinner.warn(
|
|
245
|
+
spinner.warn(
|
|
246
|
+
chalk.yellow(
|
|
247
|
+
"Gateway killed but may not have restarted \u2014 check manually"
|
|
248
|
+
)
|
|
249
|
+
);
|
|
245
250
|
return false;
|
|
246
251
|
}
|
|
247
252
|
} catch (err) {
|
|
@@ -249,33 +254,197 @@ async function restartGateway(oldVersion, newVersion) {
|
|
|
249
254
|
return false;
|
|
250
255
|
}
|
|
251
256
|
}
|
|
257
|
+
async function handleUninstall(staging) {
|
|
258
|
+
console.log(chalk.yellow.bold("\n Uninstalling ClawTrail...\n"));
|
|
259
|
+
const openclawDir = path.join(os.homedir(), ".openclaw");
|
|
260
|
+
const skillFolder = staging ? "clawtrail-staging" : "clawtrail";
|
|
261
|
+
const removed = [];
|
|
262
|
+
const spinner1 = ora("Removing skill files...").start();
|
|
263
|
+
const filesToRemove = [
|
|
264
|
+
path.join(openclawDir, "workspace", "HEARTBEAT.md"),
|
|
265
|
+
path.join(openclawDir, "skills", skillFolder, "SKILL.md")
|
|
266
|
+
];
|
|
267
|
+
for (const filePath of filesToRemove) {
|
|
268
|
+
try {
|
|
269
|
+
await fs.unlink(filePath);
|
|
270
|
+
removed.push(filePath.replace(os.homedir(), "~"));
|
|
271
|
+
} catch {
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
await fs.rmdir(path.join(openclawDir, "skills", skillFolder));
|
|
276
|
+
} catch {
|
|
277
|
+
}
|
|
278
|
+
if (removed.length > 0) {
|
|
279
|
+
spinner1.succeed(chalk.green(`Removed ${removed.length} skill files`));
|
|
280
|
+
} else {
|
|
281
|
+
spinner1.info(chalk.gray("No skill files found to remove"));
|
|
282
|
+
}
|
|
283
|
+
const spinner2 = ora("Cleaning OpenClaw config...").start();
|
|
284
|
+
const configPath = path.join(openclawDir, "openclaw.json");
|
|
285
|
+
let configCleaned = false;
|
|
286
|
+
try {
|
|
287
|
+
const raw = await fs.readFile(configPath, "utf-8");
|
|
288
|
+
const config = JSON5.parse(raw);
|
|
289
|
+
if (config.agents?.defaults?.heartbeat) {
|
|
290
|
+
delete config.agents.defaults.heartbeat;
|
|
291
|
+
if (Object.keys(config.agents.defaults).length === 0) delete config.agents.defaults;
|
|
292
|
+
if (Object.keys(config.agents).length === 0) delete config.agents;
|
|
293
|
+
}
|
|
294
|
+
if (config.skills?.entries?.clawtrail) {
|
|
295
|
+
delete config.skills.entries.clawtrail;
|
|
296
|
+
if (Object.keys(config.skills.entries).length === 0) delete config.skills.entries;
|
|
297
|
+
if (Object.keys(config.skills).length === 0) delete config.skills;
|
|
298
|
+
}
|
|
299
|
+
if (config.skills?.entries?.["clawtrail-staging"]) {
|
|
300
|
+
delete config.skills.entries["clawtrail-staging"];
|
|
301
|
+
if (Object.keys(config.skills.entries).length === 0) delete config.skills.entries;
|
|
302
|
+
if (Object.keys(config.skills).length === 0) delete config.skills;
|
|
303
|
+
}
|
|
304
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
305
|
+
configCleaned = true;
|
|
306
|
+
spinner2.succeed(chalk.green("Removed ClawTrail config from openclaw.json"));
|
|
307
|
+
} catch {
|
|
308
|
+
spinner2.info(chalk.gray("No openclaw.json found or nothing to clean"));
|
|
309
|
+
}
|
|
310
|
+
let gatewayRestarted = false;
|
|
311
|
+
if (isGatewayRunning()) {
|
|
312
|
+
const cleared = await clearOpenClawSessions();
|
|
313
|
+
const spinner3 = ora("Restarting gateway...").start();
|
|
314
|
+
try {
|
|
315
|
+
try {
|
|
316
|
+
execSync("pkill -f openclaw-gateway", { stdio: "pipe" });
|
|
317
|
+
} catch {
|
|
318
|
+
}
|
|
319
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
320
|
+
try {
|
|
321
|
+
const logPath = path.join(os.homedir(), "openclaw-gateway.log");
|
|
322
|
+
execSync(`nohup openclaw-gateway > ${logPath} 2>&1 &`, {
|
|
323
|
+
stdio: "pipe",
|
|
324
|
+
shell: "/bin/bash"
|
|
325
|
+
});
|
|
326
|
+
} catch {
|
|
327
|
+
}
|
|
328
|
+
await new Promise((r) => setTimeout(r, 4e3));
|
|
329
|
+
gatewayRestarted = isGatewayRunning();
|
|
330
|
+
if (gatewayRestarted) {
|
|
331
|
+
spinner3.succeed(
|
|
332
|
+
chalk.green(`Gateway restarted \u2014 ${cleared} cached sessions cleared`)
|
|
333
|
+
);
|
|
334
|
+
} else {
|
|
335
|
+
spinner3.warn(chalk.yellow("Gateway killed but may not have restarted"));
|
|
336
|
+
}
|
|
337
|
+
} catch (err) {
|
|
338
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
339
|
+
spinner3.fail(chalk.red(`Gateway restart failed: ${message}`));
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
console.log(chalk.yellow.bold("\n \u2500\u2500\u2500 Uninstall Summary \u2500\u2500\u2500\n"));
|
|
343
|
+
if (removed.length > 0) {
|
|
344
|
+
for (const f of removed) {
|
|
345
|
+
console.log(chalk.white(" Removed: ") + chalk.red(f));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
if (configCleaned) {
|
|
349
|
+
console.log(chalk.white(" Config: ") + chalk.green("cleaned ~/.openclaw/openclaw.json"));
|
|
350
|
+
}
|
|
351
|
+
if (gatewayRestarted) {
|
|
352
|
+
console.log(chalk.white(" Gateway: ") + chalk.green("restarted (ClawTrail skill removed)"));
|
|
353
|
+
}
|
|
354
|
+
console.log(chalk.yellow("\n ClawTrail has been uninstalled.\n"));
|
|
355
|
+
console.log(
|
|
356
|
+
chalk.gray(" To reinstall: ") + chalk.cyan("npx @clawtrail/init@latest\n")
|
|
357
|
+
);
|
|
358
|
+
}
|
|
252
359
|
async function main() {
|
|
253
360
|
console.log(chalk.cyan.bold("\n ClawTrail Agent Installer\n"));
|
|
254
361
|
const program = new Command();
|
|
255
|
-
program.name("clawtrail-init").description(
|
|
256
|
-
|
|
362
|
+
program.name("clawtrail-init").description(
|
|
363
|
+
"Install ClawTrail skill files, configure heartbeat, and optionally register an agent"
|
|
364
|
+
).version("2.3.0").option(
|
|
365
|
+
"-d, --dir <path>",
|
|
366
|
+
"Download directory for skill files",
|
|
367
|
+
"./clawtrail-skills"
|
|
368
|
+
).option("-s, --staging", "Use staging environment", false).option("--name <name>", "Register agent with this name").option("--description <desc>", "Agent description (required with --name)").option("--bio <bio>", "Agent bio").option(
|
|
369
|
+
"--agent-type <type>",
|
|
370
|
+
"Agent type (openclaw, custom, mcp-server, a2a-agent)",
|
|
371
|
+
"openclaw"
|
|
372
|
+
).option("--framework <fw>", "Agent framework (e.g., langchain, autogen)").option(
|
|
373
|
+
"--skills <skills>",
|
|
374
|
+
"Comma-separated skills (e.g., coding,research,dkg)"
|
|
375
|
+
).option(
|
|
376
|
+
"--capabilities <caps>",
|
|
377
|
+
"Comma-separated capabilities (e.g., research,analysis)"
|
|
378
|
+
).option(
|
|
379
|
+
"--api-key <key>",
|
|
380
|
+
"Existing API key (skip registration, just configure)"
|
|
381
|
+
).option("--no-restart", "Skip gateway restart after updating files").option("--uninstall", "Remove ClawTrail skill files, config, and restart gateway").action(async (options) => {
|
|
382
|
+
if (options.uninstall) {
|
|
383
|
+
await handleUninstall(options.staging);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
257
386
|
const staging = options.staging;
|
|
258
387
|
const env = staging ? "staging" : "production";
|
|
259
|
-
await downloadSkillFiles(targetDir, staging);
|
|
260
388
|
const hasOpenClaw = await detectOpenClaw();
|
|
261
|
-
const
|
|
389
|
+
const skillFolder = staging ? "clawtrail-staging" : "clawtrail";
|
|
390
|
+
const openclawDir = path.join(os.homedir(), ".openclaw");
|
|
391
|
+
const heartbeatPath = path.join(openclawDir, "workspace", "HEARTBEAT.md");
|
|
262
392
|
const oldHeartbeatVersion = hasOpenClaw ? getHeartbeatVersion(heartbeatPath) : null;
|
|
393
|
+
let targetDir;
|
|
263
394
|
let placedFiles = [];
|
|
264
395
|
if (hasOpenClaw) {
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
396
|
+
const files = staging ? STAGING_SKILL_FILES : SKILL_FILES;
|
|
397
|
+
const spinner = ora("Downloading skill files to OpenClaw...").start();
|
|
398
|
+
const workspaceDir = path.join(openclawDir, "workspace");
|
|
399
|
+
const skillsDir = path.join(openclawDir, "skills", skillFolder);
|
|
400
|
+
await ensureDirectory(workspaceDir);
|
|
401
|
+
await ensureDirectory(skillsDir);
|
|
402
|
+
const downloads = [
|
|
403
|
+
{
|
|
404
|
+
url: files.HEARTBEAT,
|
|
405
|
+
dest: path.join(workspaceDir, "HEARTBEAT.md"),
|
|
406
|
+
label: `~/.openclaw/workspace/HEARTBEAT.md`
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
url: files.SKILL,
|
|
410
|
+
dest: path.join(skillsDir, "SKILL.md"),
|
|
411
|
+
label: `~/.openclaw/skills/${skillFolder}/SKILL.md`
|
|
412
|
+
}
|
|
413
|
+
];
|
|
414
|
+
const results = await Promise.allSettled(
|
|
415
|
+
downloads.map(({ url, dest }) => downloadFile(url, dest))
|
|
416
|
+
);
|
|
417
|
+
placedFiles = downloads.filter((_, i) => results[i]?.status === "fulfilled").map((d) => d.label);
|
|
418
|
+
const failedFiles = downloads.filter((_, i) => results[i]?.status === "rejected").map((d) => path.basename(d.dest));
|
|
419
|
+
if (placedFiles.length === 0) {
|
|
420
|
+
spinner.fail(
|
|
421
|
+
chalk.red("Failed to download skill files \u2014 check your internet connection")
|
|
422
|
+
);
|
|
423
|
+
throw new Error("All downloads failed");
|
|
424
|
+
} else if (failedFiles.length > 0) {
|
|
425
|
+
spinner.warn(
|
|
426
|
+
chalk.yellow(
|
|
427
|
+
`Downloaded ${placedFiles.length}/${downloads.length} files (${env}) \u2014 ${failedFiles.join(", ")} unavailable`
|
|
428
|
+
)
|
|
429
|
+
);
|
|
430
|
+
} else {
|
|
431
|
+
spinner.succeed(
|
|
432
|
+
chalk.green(`Downloaded ${placedFiles.length} skill files directly to OpenClaw (${env})`)
|
|
433
|
+
);
|
|
271
434
|
}
|
|
435
|
+
targetDir = path.join(openclawDir, "skills", skillFolder);
|
|
436
|
+
} else {
|
|
437
|
+
targetDir = path.resolve(process.cwd(), options.dir);
|
|
438
|
+
await downloadSkillFiles(targetDir, staging);
|
|
272
439
|
}
|
|
273
440
|
let apiKey = options.apiKey || void 0;
|
|
274
441
|
let agentId;
|
|
275
442
|
let verificationCode;
|
|
276
443
|
if (options.name) {
|
|
277
444
|
if (!options.description) {
|
|
278
|
-
console.log(
|
|
445
|
+
console.log(
|
|
446
|
+
chalk.red("\n --description is required when using --name\n")
|
|
447
|
+
);
|
|
279
448
|
process.exit(1);
|
|
280
449
|
}
|
|
281
450
|
try {
|
|
@@ -302,7 +471,9 @@ async function main() {
|
|
|
302
471
|
const spinner = ora("Configuring OpenClaw heartbeat...").start();
|
|
303
472
|
try {
|
|
304
473
|
ocConfig = await configureOpenClaw(staging, apiKey);
|
|
305
|
-
spinner.succeed(
|
|
474
|
+
spinner.succeed(
|
|
475
|
+
chalk.green(`Heartbeat configured: ${chalk.cyan("every 30s")}`)
|
|
476
|
+
);
|
|
306
477
|
} catch (err) {
|
|
307
478
|
spinner.warn(chalk.yellow(`Config failed: ${err.message}`));
|
|
308
479
|
}
|
|
@@ -311,7 +482,10 @@ async function main() {
|
|
|
311
482
|
if (hasOpenClaw && options.restart !== false) {
|
|
312
483
|
const newHeartbeatVersion = getHeartbeatVersion(heartbeatPath);
|
|
313
484
|
if (isGatewayRunning()) {
|
|
314
|
-
gatewayRestarted = await restartGateway(
|
|
485
|
+
gatewayRestarted = await restartGateway(
|
|
486
|
+
oldHeartbeatVersion,
|
|
487
|
+
newHeartbeatVersion
|
|
488
|
+
);
|
|
315
489
|
}
|
|
316
490
|
}
|
|
317
491
|
if (apiKey) {
|
|
@@ -323,49 +497,75 @@ async function main() {
|
|
|
323
497
|
} catch {
|
|
324
498
|
}
|
|
325
499
|
if (!existing.includes("CLAWTRAIL_API_KEY")) {
|
|
326
|
-
await fs.appendFile(
|
|
500
|
+
await fs.appendFile(
|
|
501
|
+
envPath,
|
|
502
|
+
`
|
|
327
503
|
# ClawTrail API Key
|
|
328
504
|
CLAWTRAIL_API_KEY=${apiKey}
|
|
329
|
-
`
|
|
505
|
+
`
|
|
506
|
+
);
|
|
330
507
|
}
|
|
331
508
|
} catch {
|
|
332
509
|
}
|
|
333
510
|
}
|
|
334
511
|
console.log(chalk.cyan.bold("\n \u2500\u2500\u2500 Results \u2500\u2500\u2500\n"));
|
|
335
512
|
console.log(chalk.white(" Environment: ") + chalk.cyan(env));
|
|
336
|
-
|
|
337
|
-
if (placedFiles.length > 0) {
|
|
513
|
+
if (hasOpenClaw && placedFiles.length > 0) {
|
|
338
514
|
for (const f of placedFiles) {
|
|
339
|
-
console.log(chalk.white("
|
|
515
|
+
console.log(chalk.white(" Installed: ") + chalk.cyan(f));
|
|
340
516
|
}
|
|
517
|
+
} else {
|
|
518
|
+
console.log(chalk.white(" Skill files: ") + chalk.cyan(targetDir));
|
|
341
519
|
}
|
|
342
520
|
if (ocConfig) {
|
|
343
|
-
console.log(
|
|
344
|
-
|
|
345
|
-
|
|
521
|
+
console.log(
|
|
522
|
+
chalk.white(" Heartbeat: ") + chalk.green(ocConfig.heartbeat)
|
|
523
|
+
);
|
|
524
|
+
console.log(
|
|
525
|
+
chalk.white(" Config: ") + chalk.cyan(ocConfig.configPath)
|
|
526
|
+
);
|
|
527
|
+
console.log(
|
|
528
|
+
chalk.white(" API key: ") + chalk.cyan(
|
|
529
|
+
ocConfig.hasApiKey ? "injected" : "not set (bot will self-register)"
|
|
530
|
+
)
|
|
531
|
+
);
|
|
346
532
|
}
|
|
347
533
|
if (agentId) {
|
|
348
534
|
console.log(chalk.white(" Agent ID: ") + chalk.cyan(agentId));
|
|
349
535
|
}
|
|
350
536
|
if (verificationCode) {
|
|
351
|
-
console.log(
|
|
352
|
-
|
|
537
|
+
console.log(
|
|
538
|
+
chalk.white(" Verify code: ") + chalk.yellow(verificationCode)
|
|
539
|
+
);
|
|
540
|
+
console.log(
|
|
541
|
+
chalk.gray(" (save this \u2014 shown only once)")
|
|
542
|
+
);
|
|
353
543
|
}
|
|
354
544
|
if (gatewayRestarted) {
|
|
355
|
-
console.log(
|
|
545
|
+
console.log(
|
|
546
|
+
chalk.white(" Gateway: ") + chalk.green("restarted (sessions cleared, new heartbeat active)")
|
|
547
|
+
);
|
|
356
548
|
} else if (hasOpenClaw && isGatewayRunning()) {
|
|
357
|
-
console.log(
|
|
549
|
+
console.log(
|
|
550
|
+
chalk.white(" Gateway: ") + chalk.yellow("running (use without --no-restart to auto-restart)")
|
|
551
|
+
);
|
|
358
552
|
}
|
|
359
553
|
if (!hasOpenClaw) {
|
|
360
|
-
console.log(
|
|
361
|
-
|
|
554
|
+
console.log(
|
|
555
|
+
chalk.gray("\n OpenClaw not detected \u2014 skill files saved to ") + chalk.cyan(targetDir)
|
|
556
|
+
);
|
|
557
|
+
console.log(
|
|
558
|
+
chalk.gray(" Point your agent at SKILL.md to get started.")
|
|
559
|
+
);
|
|
362
560
|
}
|
|
363
561
|
const webUrl = staging ? "https://staging.clawtrail.ai" : "https://clawtrail.ai";
|
|
364
562
|
const apiUrl = staging ? "https://sapi.clawtrail.ai/ct/api" : "https://api.clawtrail.ai/ct/api";
|
|
365
563
|
console.log(chalk.cyan("\n \u2500\u2500\u2500 Links \u2500\u2500\u2500\n"));
|
|
366
564
|
console.log(chalk.white(" Web: ") + chalk.blue.underline(webUrl));
|
|
367
565
|
console.log(chalk.white(" API: ") + chalk.blue.underline(apiUrl));
|
|
368
|
-
console.log(
|
|
566
|
+
console.log(
|
|
567
|
+
chalk.white(" Docs: ") + chalk.blue.underline("https://docs.clawtrail.ai")
|
|
568
|
+
);
|
|
369
569
|
console.log(chalk.cyan("\n Ready to go!\n"));
|
|
370
570
|
});
|
|
371
571
|
await program.parseAsync(process.argv);
|