nextclaw 0.4.4 → 0.4.6
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/dist/cli/index.js +222 -99
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -35,7 +35,7 @@ import { startUiServer } from "nextclaw-server";
|
|
|
35
35
|
import {
|
|
36
36
|
closeSync,
|
|
37
37
|
cpSync,
|
|
38
|
-
existsSync as
|
|
38
|
+
existsSync as existsSync4,
|
|
39
39
|
mkdirSync as mkdirSync2,
|
|
40
40
|
openSync,
|
|
41
41
|
readdirSync,
|
|
@@ -43,19 +43,16 @@ import {
|
|
|
43
43
|
rmSync as rmSync2,
|
|
44
44
|
writeFileSync as writeFileSync2
|
|
45
45
|
} from "fs";
|
|
46
|
-
import { dirname, join as join3, resolve as
|
|
47
|
-
import { spawn as spawn2, spawnSync as
|
|
46
|
+
import { dirname, join as join3, resolve as resolve4 } from "path";
|
|
47
|
+
import { spawn as spawn2, spawnSync as spawnSync3 } from "child_process";
|
|
48
48
|
import { createInterface } from "readline";
|
|
49
49
|
import { createRequire } from "module";
|
|
50
|
-
import { fileURLToPath as
|
|
50
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
51
51
|
import chokidar from "chokidar";
|
|
52
52
|
|
|
53
53
|
// src/cli/gateway/controller.ts
|
|
54
54
|
import { createHash } from "crypto";
|
|
55
55
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
56
|
-
import { spawnSync } from "child_process";
|
|
57
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
58
|
-
import { join as join2, resolve as resolve2 } from "path";
|
|
59
56
|
import {
|
|
60
57
|
ConfigSchema
|
|
61
58
|
} from "nextclaw-core";
|
|
@@ -98,12 +95,12 @@ async function isPortAvailable(port, host) {
|
|
|
98
95
|
return await canBindPort(port, checkHost);
|
|
99
96
|
}
|
|
100
97
|
async function canBindPort(port, host) {
|
|
101
|
-
return await new Promise((
|
|
98
|
+
return await new Promise((resolve5) => {
|
|
102
99
|
const server = createServer();
|
|
103
100
|
server.unref();
|
|
104
|
-
server.once("error", () =>
|
|
101
|
+
server.once("error", () => resolve5(false));
|
|
105
102
|
server.listen({ port, host }, () => {
|
|
106
|
-
server.close(() =>
|
|
103
|
+
server.close(() => resolve5(true));
|
|
107
104
|
});
|
|
108
105
|
});
|
|
109
106
|
}
|
|
@@ -161,7 +158,7 @@ async function waitForExit(pid, timeoutMs) {
|
|
|
161
158
|
if (!isProcessRunning(pid)) {
|
|
162
159
|
return true;
|
|
163
160
|
}
|
|
164
|
-
await new Promise((
|
|
161
|
+
await new Promise((resolve5) => setTimeout(resolve5, 200));
|
|
165
162
|
}
|
|
166
163
|
return !isProcessRunning(pid);
|
|
167
164
|
}
|
|
@@ -292,11 +289,55 @@ function printAgentResponse(response) {
|
|
|
292
289
|
async function prompt(rl, question) {
|
|
293
290
|
rl.setPrompt(question);
|
|
294
291
|
rl.prompt();
|
|
295
|
-
return new Promise((
|
|
296
|
-
rl.once("line", (line) =>
|
|
292
|
+
return new Promise((resolve5) => {
|
|
293
|
+
rl.once("line", (line) => resolve5(line));
|
|
297
294
|
});
|
|
298
295
|
}
|
|
299
296
|
|
|
297
|
+
// src/cli/update/runner.ts
|
|
298
|
+
import { spawnSync } from "child_process";
|
|
299
|
+
import { resolve as resolve2 } from "path";
|
|
300
|
+
var DEFAULT_TIMEOUT_MS = 20 * 6e4;
|
|
301
|
+
function runSelfUpdate(options = {}) {
|
|
302
|
+
const steps = [];
|
|
303
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
304
|
+
const updateCommand = options.updateCommand ?? process.env.NEXTCLAW_UPDATE_COMMAND?.trim();
|
|
305
|
+
const packageName = options.packageName ?? "nextclaw";
|
|
306
|
+
const runStep = (cmd, args, cwd) => {
|
|
307
|
+
const result = spawnSync(cmd, args, {
|
|
308
|
+
cwd,
|
|
309
|
+
encoding: "utf-8",
|
|
310
|
+
timeout: timeoutMs,
|
|
311
|
+
stdio: "pipe"
|
|
312
|
+
});
|
|
313
|
+
steps.push({
|
|
314
|
+
cmd,
|
|
315
|
+
args,
|
|
316
|
+
cwd,
|
|
317
|
+
code: result.status,
|
|
318
|
+
stdout: (result.stdout ?? "").toString().slice(0, 4e3),
|
|
319
|
+
stderr: (result.stderr ?? "").toString().slice(0, 4e3)
|
|
320
|
+
});
|
|
321
|
+
return { ok: result.status === 0, code: result.status };
|
|
322
|
+
};
|
|
323
|
+
if (updateCommand) {
|
|
324
|
+
const cwd = options.cwd ? resolve2(options.cwd) : process.cwd();
|
|
325
|
+
const ok = runStep("sh", ["-c", updateCommand], cwd);
|
|
326
|
+
if (!ok.ok) {
|
|
327
|
+
return { ok: false, error: "update command failed", strategy: "command", steps };
|
|
328
|
+
}
|
|
329
|
+
return { ok: true, strategy: "command", steps };
|
|
330
|
+
}
|
|
331
|
+
if (which("npm")) {
|
|
332
|
+
const ok = runStep("npm", ["i", "-g", packageName], process.cwd());
|
|
333
|
+
if (!ok.ok) {
|
|
334
|
+
return { ok: false, error: `npm install -g ${packageName} failed`, strategy: "npm", steps };
|
|
335
|
+
}
|
|
336
|
+
return { ok: true, strategy: "npm", steps };
|
|
337
|
+
}
|
|
338
|
+
return { ok: false, error: "no update strategy available", strategy: "none", steps };
|
|
339
|
+
}
|
|
340
|
+
|
|
300
341
|
// src/cli/gateway/controller.ts
|
|
301
342
|
var hashRaw = (raw) => createHash("sha256").update(raw).digest("hex");
|
|
302
343
|
var redactConfig = (value) => {
|
|
@@ -503,62 +544,9 @@ var GatewayControllerImpl = class {
|
|
|
503
544
|
};
|
|
504
545
|
}
|
|
505
546
|
async updateRun(params) {
|
|
506
|
-
const
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
const pkgRoot = resolve2(cliDir, "..", "..");
|
|
510
|
-
const repoRoot = existsSync2(join2(pkgRoot, ".git")) ? pkgRoot : resolve2(pkgRoot, "..", "..");
|
|
511
|
-
const steps = [];
|
|
512
|
-
const runStep = (cmd, args, cwd) => {
|
|
513
|
-
const result = spawnSync(cmd, args, {
|
|
514
|
-
cwd,
|
|
515
|
-
encoding: "utf-8",
|
|
516
|
-
timeout: timeoutMs,
|
|
517
|
-
stdio: "pipe"
|
|
518
|
-
});
|
|
519
|
-
const step = {
|
|
520
|
-
cmd,
|
|
521
|
-
args,
|
|
522
|
-
cwd,
|
|
523
|
-
code: result.status,
|
|
524
|
-
stdout: (result.stdout ?? "").toString().slice(0, 4e3),
|
|
525
|
-
stderr: (result.stderr ?? "").toString().slice(0, 4e3)
|
|
526
|
-
};
|
|
527
|
-
steps.push(step);
|
|
528
|
-
return { ok: result.status === 0, code: result.status };
|
|
529
|
-
};
|
|
530
|
-
const updateCommand = process.env.NEXTCLAW_UPDATE_COMMAND?.trim();
|
|
531
|
-
if (updateCommand) {
|
|
532
|
-
const ok = runStep("sh", ["-c", updateCommand], process.cwd());
|
|
533
|
-
if (!ok.ok) {
|
|
534
|
-
return { ok: false, error: "update command failed", steps };
|
|
535
|
-
}
|
|
536
|
-
} else if (existsSync2(join2(repoRoot, ".git"))) {
|
|
537
|
-
if (!which("git")) {
|
|
538
|
-
return { ok: false, error: "git not found for repo update", steps };
|
|
539
|
-
}
|
|
540
|
-
const ok = runStep("git", ["-C", repoRoot, "pull", "--rebase"], repoRoot);
|
|
541
|
-
if (!ok.ok) {
|
|
542
|
-
return { ok: false, error: "git pull failed", steps };
|
|
543
|
-
}
|
|
544
|
-
if (existsSync2(join2(repoRoot, "pnpm-lock.yaml")) && which("pnpm")) {
|
|
545
|
-
const installOk = runStep("pnpm", ["install"], repoRoot);
|
|
546
|
-
if (!installOk.ok) {
|
|
547
|
-
return { ok: false, error: "pnpm install failed", steps };
|
|
548
|
-
}
|
|
549
|
-
} else if (existsSync2(join2(repoRoot, "package.json")) && which("npm")) {
|
|
550
|
-
const installOk = runStep("npm", ["install"], repoRoot);
|
|
551
|
-
if (!installOk.ok) {
|
|
552
|
-
return { ok: false, error: "npm install failed", steps };
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
} else if (which("npm")) {
|
|
556
|
-
const ok = runStep("npm", ["i", "-g", "nextclaw"], process.cwd());
|
|
557
|
-
if (!ok.ok) {
|
|
558
|
-
return { ok: false, error: "npm install -g nextclaw failed", steps };
|
|
559
|
-
}
|
|
560
|
-
} else {
|
|
561
|
-
return { ok: false, error: "no update strategy available", steps };
|
|
547
|
+
const result = runSelfUpdate({ timeoutMs: params.timeoutMs });
|
|
548
|
+
if (!result.ok) {
|
|
549
|
+
return { ok: false, error: result.error ?? "update failed", steps: result.steps };
|
|
562
550
|
}
|
|
563
551
|
const delayMs = params.restartDelayMs ?? 0;
|
|
564
552
|
scheduleRestart(delayMs, "update.run");
|
|
@@ -566,11 +554,82 @@ var GatewayControllerImpl = class {
|
|
|
566
554
|
ok: true,
|
|
567
555
|
note: params.note ?? null,
|
|
568
556
|
restart: { scheduled: true, delayMs },
|
|
569
|
-
|
|
557
|
+
strategy: result.strategy,
|
|
558
|
+
steps: result.steps
|
|
570
559
|
};
|
|
571
560
|
}
|
|
572
561
|
};
|
|
573
562
|
|
|
563
|
+
// src/cli/skills/clawhub.ts
|
|
564
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
565
|
+
import { existsSync as existsSync3 } from "fs";
|
|
566
|
+
import { isAbsolute, join as join2, resolve as resolve3 } from "path";
|
|
567
|
+
async function installClawHubSkill(options) {
|
|
568
|
+
const slug = options.slug.trim();
|
|
569
|
+
if (!slug) {
|
|
570
|
+
throw new Error("Skill slug is required.");
|
|
571
|
+
}
|
|
572
|
+
const workdir = resolve3(options.workdir);
|
|
573
|
+
if (!existsSync3(workdir)) {
|
|
574
|
+
throw new Error(`Workdir does not exist: ${workdir}`);
|
|
575
|
+
}
|
|
576
|
+
const dirName = options.dir?.trim() || "skills";
|
|
577
|
+
const destinationDir = isAbsolute(dirName) ? resolve3(dirName, slug) : resolve3(workdir, dirName, slug);
|
|
578
|
+
const skillFile = join2(destinationDir, "SKILL.md");
|
|
579
|
+
if (!options.force && existsSync3(destinationDir)) {
|
|
580
|
+
if (existsSync3(skillFile)) {
|
|
581
|
+
return {
|
|
582
|
+
slug,
|
|
583
|
+
version: options.version,
|
|
584
|
+
registry: options.registry,
|
|
585
|
+
destinationDir,
|
|
586
|
+
alreadyInstalled: true
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
throw new Error(`Skill directory already exists: ${destinationDir} (use --force)`);
|
|
590
|
+
}
|
|
591
|
+
const args = buildClawHubArgs(slug, options);
|
|
592
|
+
const result = spawnSync2("npx", args, {
|
|
593
|
+
cwd: workdir,
|
|
594
|
+
stdio: "pipe",
|
|
595
|
+
env: process.env
|
|
596
|
+
});
|
|
597
|
+
if (result.error) {
|
|
598
|
+
throw new Error(`Failed to run npx clawhub: ${String(result.error)}`);
|
|
599
|
+
}
|
|
600
|
+
if (result.status !== 0) {
|
|
601
|
+
const stdout = result.stdout ? String(result.stdout).trim() : "";
|
|
602
|
+
const stderr = result.stderr ? String(result.stderr).trim() : "";
|
|
603
|
+
const details = [stderr, stdout].filter(Boolean).join("\n");
|
|
604
|
+
throw new Error(details || `clawhub install failed with code ${result.status ?? 1}`);
|
|
605
|
+
}
|
|
606
|
+
return {
|
|
607
|
+
slug,
|
|
608
|
+
version: options.version,
|
|
609
|
+
registry: options.registry,
|
|
610
|
+
destinationDir
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
function buildClawHubArgs(slug, options) {
|
|
614
|
+
const args = ["--yes", "clawhub", "install", slug];
|
|
615
|
+
if (options.version) {
|
|
616
|
+
args.push("--version", options.version);
|
|
617
|
+
}
|
|
618
|
+
if (options.registry) {
|
|
619
|
+
args.push("--registry", options.registry);
|
|
620
|
+
}
|
|
621
|
+
if (options.workdir) {
|
|
622
|
+
args.push("--workdir", options.workdir);
|
|
623
|
+
}
|
|
624
|
+
if (options.dir) {
|
|
625
|
+
args.push("--dir", options.dir);
|
|
626
|
+
}
|
|
627
|
+
if (options.force) {
|
|
628
|
+
args.push("--force");
|
|
629
|
+
}
|
|
630
|
+
return args;
|
|
631
|
+
}
|
|
632
|
+
|
|
574
633
|
// src/cli/runtime.ts
|
|
575
634
|
var LOGO = "\u{1F916}";
|
|
576
635
|
var EXIT_COMMANDS = /* @__PURE__ */ new Set(["exit", "quit", "/exit", "/quit", ":q"]);
|
|
@@ -699,7 +758,7 @@ var CliRuntime = class {
|
|
|
699
758
|
const force = Boolean(options.force);
|
|
700
759
|
const configPath = getConfigPath();
|
|
701
760
|
let createdConfig = false;
|
|
702
|
-
if (!
|
|
761
|
+
if (!existsSync4(configPath)) {
|
|
703
762
|
const config2 = ConfigSchema2.parse({});
|
|
704
763
|
saveConfig(config2);
|
|
705
764
|
createdConfig = true;
|
|
@@ -707,7 +766,7 @@ var CliRuntime = class {
|
|
|
707
766
|
const config = loadConfig();
|
|
708
767
|
const workspaceSetting = config.agents.defaults.workspace;
|
|
709
768
|
const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ? join3(getDataDir2(), DEFAULT_WORKSPACE_DIR) : expandHome(workspaceSetting);
|
|
710
|
-
const workspaceExisted =
|
|
769
|
+
const workspaceExisted = existsSync4(workspacePath);
|
|
711
770
|
mkdirSync2(workspacePath, { recursive: true });
|
|
712
771
|
const templateResult = this.createWorkspaceTemplates(workspacePath, { force });
|
|
713
772
|
if (createdConfig) {
|
|
@@ -872,9 +931,9 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
872
931
|
console.log(`${this.logo} Interactive mode (type exit or Ctrl+C to quit)
|
|
873
932
|
`);
|
|
874
933
|
const historyFile = join3(getDataDir2(), "history", "cli_history");
|
|
875
|
-
const historyDir =
|
|
934
|
+
const historyDir = resolve4(historyFile, "..");
|
|
876
935
|
mkdirSync2(historyDir, { recursive: true });
|
|
877
|
-
const history =
|
|
936
|
+
const history = existsSync4(historyFile) ? readFileSync3(historyFile, "utf-8").split("\n").filter(Boolean) : [];
|
|
878
937
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
879
938
|
rl.on("close", () => {
|
|
880
939
|
const merged = history.concat(rl.history ?? []);
|
|
@@ -900,6 +959,62 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
900
959
|
printAgentResponse(response);
|
|
901
960
|
}
|
|
902
961
|
}
|
|
962
|
+
async update(opts) {
|
|
963
|
+
let timeoutMs;
|
|
964
|
+
if (opts.timeout !== void 0) {
|
|
965
|
+
const parsed = Number(opts.timeout);
|
|
966
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
967
|
+
console.error("Invalid --timeout value. Provide milliseconds (e.g. 1200000).");
|
|
968
|
+
process.exit(1);
|
|
969
|
+
}
|
|
970
|
+
timeoutMs = parsed;
|
|
971
|
+
}
|
|
972
|
+
const result = runSelfUpdate({ timeoutMs, cwd: process.cwd() });
|
|
973
|
+
const printSteps = () => {
|
|
974
|
+
for (const step of result.steps) {
|
|
975
|
+
console.log(`- ${step.cmd} ${step.args.join(" ")} (code ${step.code ?? "?"})`);
|
|
976
|
+
if (step.stderr) {
|
|
977
|
+
console.log(` stderr: ${step.stderr}`);
|
|
978
|
+
}
|
|
979
|
+
if (step.stdout) {
|
|
980
|
+
console.log(` stdout: ${step.stdout}`);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
};
|
|
984
|
+
if (!result.ok) {
|
|
985
|
+
console.error(`Update failed: ${result.error ?? "unknown error"}`);
|
|
986
|
+
if (result.steps.length > 0) {
|
|
987
|
+
printSteps();
|
|
988
|
+
}
|
|
989
|
+
process.exit(1);
|
|
990
|
+
}
|
|
991
|
+
console.log(`\u2713 Update complete (${result.strategy})`);
|
|
992
|
+
const state = readServiceState();
|
|
993
|
+
if (state && isProcessRunning(state.pid)) {
|
|
994
|
+
console.log(`Tip: restart ${APP_NAME} to apply the update.`);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
async skillsInstall(options) {
|
|
998
|
+
const workdir = options.workdir ? expandHome(options.workdir) : getWorkspacePath();
|
|
999
|
+
const result = await installClawHubSkill({
|
|
1000
|
+
slug: options.slug,
|
|
1001
|
+
version: options.version,
|
|
1002
|
+
registry: options.registry,
|
|
1003
|
+
workdir,
|
|
1004
|
+
dir: options.dir,
|
|
1005
|
+
force: options.force
|
|
1006
|
+
});
|
|
1007
|
+
const versionLabel = result.version ?? "latest";
|
|
1008
|
+
if (result.alreadyInstalled) {
|
|
1009
|
+
console.log(`\u2713 ${result.slug} is already installed`);
|
|
1010
|
+
} else {
|
|
1011
|
+
console.log(`\u2713 Installed ${result.slug}@${versionLabel}`);
|
|
1012
|
+
}
|
|
1013
|
+
if (result.registry) {
|
|
1014
|
+
console.log(` Registry: ${result.registry}`);
|
|
1015
|
+
}
|
|
1016
|
+
console.log(` Path: ${result.destinationDir}`);
|
|
1017
|
+
}
|
|
903
1018
|
channelsStatus() {
|
|
904
1019
|
const config = loadConfig();
|
|
905
1020
|
console.log("Channel Status");
|
|
@@ -915,7 +1030,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
915
1030
|
const bridgeDir = this.getBridgeDir();
|
|
916
1031
|
console.log(`${this.logo} Starting bridge...`);
|
|
917
1032
|
console.log("Scan the QR code to connect.\n");
|
|
918
|
-
const result =
|
|
1033
|
+
const result = spawnSync3("npm", ["start"], { cwd: bridgeDir, stdio: "inherit" });
|
|
919
1034
|
if (result.status !== 0) {
|
|
920
1035
|
console.error(`Bridge failed: ${result.status ?? 1}`);
|
|
921
1036
|
}
|
|
@@ -996,8 +1111,8 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
996
1111
|
const workspace = getWorkspacePath(config.agents.defaults.workspace);
|
|
997
1112
|
console.log(`${this.logo} ${APP_NAME} Status
|
|
998
1113
|
`);
|
|
999
|
-
console.log(`Config: ${configPath} ${
|
|
1000
|
-
console.log(`Workspace: ${workspace} ${
|
|
1114
|
+
console.log(`Config: ${configPath} ${existsSync4(configPath) ? "\u2713" : "\u2717"}`);
|
|
1115
|
+
console.log(`Workspace: ${workspace} ${existsSync4(workspace) ? "\u2713" : "\u2717"}`);
|
|
1001
1116
|
console.log(`Model: ${config.agents.defaults.model}`);
|
|
1002
1117
|
for (const spec of PROVIDERS) {
|
|
1003
1118
|
const provider = config.providers[spec.name];
|
|
@@ -1182,7 +1297,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
1182
1297
|
console.log("Warning: UI frontend not found. Use --frontend to start the dev server.");
|
|
1183
1298
|
}
|
|
1184
1299
|
const logPath = resolveServiceLogPath();
|
|
1185
|
-
const logDir =
|
|
1300
|
+
const logDir = resolve4(logPath, "..");
|
|
1186
1301
|
mkdirSync2(logDir, { recursive: true });
|
|
1187
1302
|
const logFd = openSync(logPath, "a");
|
|
1188
1303
|
const serveArgs = buildServeArgs({
|
|
@@ -1292,11 +1407,11 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
1292
1407
|
];
|
|
1293
1408
|
for (const entry of templateFiles) {
|
|
1294
1409
|
const filePath = join3(workspace, entry.target);
|
|
1295
|
-
if (!force &&
|
|
1410
|
+
if (!force && existsSync4(filePath)) {
|
|
1296
1411
|
continue;
|
|
1297
1412
|
}
|
|
1298
1413
|
const templatePath = join3(templateDir, entry.source);
|
|
1299
|
-
if (!
|
|
1414
|
+
if (!existsSync4(templatePath)) {
|
|
1300
1415
|
console.warn(`Warning: Template file missing: ${templatePath}`);
|
|
1301
1416
|
continue;
|
|
1302
1417
|
}
|
|
@@ -1307,12 +1422,12 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
1307
1422
|
created.push(entry.target);
|
|
1308
1423
|
}
|
|
1309
1424
|
const memoryDir = join3(workspace, "memory");
|
|
1310
|
-
if (!
|
|
1425
|
+
if (!existsSync4(memoryDir)) {
|
|
1311
1426
|
mkdirSync2(memoryDir, { recursive: true });
|
|
1312
1427
|
created.push(join3("memory", ""));
|
|
1313
1428
|
}
|
|
1314
1429
|
const skillsDir = join3(workspace, "skills");
|
|
1315
|
-
if (!
|
|
1430
|
+
if (!existsSync4(skillsDir)) {
|
|
1316
1431
|
mkdirSync2(skillsDir, { recursive: true });
|
|
1317
1432
|
created.push(join3("skills", ""));
|
|
1318
1433
|
}
|
|
@@ -1338,11 +1453,11 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
1338
1453
|
continue;
|
|
1339
1454
|
}
|
|
1340
1455
|
const src = join3(sourceDir, entry.name);
|
|
1341
|
-
if (!
|
|
1456
|
+
if (!existsSync4(join3(src, "SKILL.md"))) {
|
|
1342
1457
|
continue;
|
|
1343
1458
|
}
|
|
1344
1459
|
const dest = join3(targetDir, entry.name);
|
|
1345
|
-
if (!force &&
|
|
1460
|
+
if (!force && existsSync4(dest)) {
|
|
1346
1461
|
continue;
|
|
1347
1462
|
}
|
|
1348
1463
|
cpSync(src, dest, { recursive: true, force: true });
|
|
@@ -1354,13 +1469,13 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
1354
1469
|
try {
|
|
1355
1470
|
const require2 = createRequire(import.meta.url);
|
|
1356
1471
|
const entry = require2.resolve("nextclaw-core");
|
|
1357
|
-
const pkgRoot =
|
|
1472
|
+
const pkgRoot = resolve4(dirname(entry), "..");
|
|
1358
1473
|
const distSkills = join3(pkgRoot, "dist", "skills");
|
|
1359
|
-
if (
|
|
1474
|
+
if (existsSync4(distSkills)) {
|
|
1360
1475
|
return distSkills;
|
|
1361
1476
|
}
|
|
1362
1477
|
const srcSkills = join3(pkgRoot, "src", "agent", "skills");
|
|
1363
|
-
if (
|
|
1478
|
+
if (existsSync4(srcSkills)) {
|
|
1364
1479
|
return srcSkills;
|
|
1365
1480
|
}
|
|
1366
1481
|
return null;
|
|
@@ -1373,11 +1488,11 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
1373
1488
|
if (override) {
|
|
1374
1489
|
return override;
|
|
1375
1490
|
}
|
|
1376
|
-
const cliDir =
|
|
1377
|
-
const pkgRoot =
|
|
1491
|
+
const cliDir = resolve4(fileURLToPath2(new URL(".", import.meta.url)));
|
|
1492
|
+
const pkgRoot = resolve4(cliDir, "..", "..");
|
|
1378
1493
|
const candidates = [join3(pkgRoot, "templates")];
|
|
1379
1494
|
for (const candidate of candidates) {
|
|
1380
|
-
if (
|
|
1495
|
+
if (existsSync4(candidate)) {
|
|
1381
1496
|
return candidate;
|
|
1382
1497
|
}
|
|
1383
1498
|
}
|
|
@@ -1385,21 +1500,21 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
1385
1500
|
}
|
|
1386
1501
|
getBridgeDir() {
|
|
1387
1502
|
const userBridge = join3(getDataDir2(), "bridge");
|
|
1388
|
-
if (
|
|
1503
|
+
if (existsSync4(join3(userBridge, "dist", "index.js"))) {
|
|
1389
1504
|
return userBridge;
|
|
1390
1505
|
}
|
|
1391
1506
|
if (!which("npm")) {
|
|
1392
1507
|
console.error("npm not found. Please install Node.js >= 18.");
|
|
1393
1508
|
process.exit(1);
|
|
1394
1509
|
}
|
|
1395
|
-
const cliDir =
|
|
1396
|
-
const pkgRoot =
|
|
1510
|
+
const cliDir = resolve4(fileURLToPath2(new URL(".", import.meta.url)));
|
|
1511
|
+
const pkgRoot = resolve4(cliDir, "..", "..");
|
|
1397
1512
|
const pkgBridge = join3(pkgRoot, "bridge");
|
|
1398
1513
|
const srcBridge = join3(pkgRoot, "..", "..", "bridge");
|
|
1399
1514
|
let source = null;
|
|
1400
|
-
if (
|
|
1515
|
+
if (existsSync4(join3(pkgBridge, "package.json"))) {
|
|
1401
1516
|
source = pkgBridge;
|
|
1402
|
-
} else if (
|
|
1517
|
+
} else if (existsSync4(join3(srcBridge, "package.json"))) {
|
|
1403
1518
|
source = srcBridge;
|
|
1404
1519
|
}
|
|
1405
1520
|
if (!source) {
|
|
@@ -1407,15 +1522,15 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
1407
1522
|
process.exit(1);
|
|
1408
1523
|
}
|
|
1409
1524
|
console.log(`${this.logo} Setting up bridge...`);
|
|
1410
|
-
mkdirSync2(
|
|
1411
|
-
if (
|
|
1525
|
+
mkdirSync2(resolve4(userBridge, ".."), { recursive: true });
|
|
1526
|
+
if (existsSync4(userBridge)) {
|
|
1412
1527
|
rmSync2(userBridge, { recursive: true, force: true });
|
|
1413
1528
|
}
|
|
1414
1529
|
cpSync(source, userBridge, {
|
|
1415
1530
|
recursive: true,
|
|
1416
1531
|
filter: (src) => !src.includes("node_modules") && !src.includes("dist")
|
|
1417
1532
|
});
|
|
1418
|
-
const install =
|
|
1533
|
+
const install = spawnSync3("npm", ["install"], { cwd: userBridge, stdio: "pipe" });
|
|
1419
1534
|
if (install.status !== 0) {
|
|
1420
1535
|
console.error(`Bridge install failed: ${install.status ?? 1}`);
|
|
1421
1536
|
if (install.stderr) {
|
|
@@ -1423,7 +1538,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
1423
1538
|
}
|
|
1424
1539
|
process.exit(1);
|
|
1425
1540
|
}
|
|
1426
|
-
const build =
|
|
1541
|
+
const build = spawnSync3("npm", ["run", "build"], { cwd: userBridge, stdio: "pipe" });
|
|
1427
1542
|
if (build.status !== 0) {
|
|
1428
1543
|
console.error(`Bridge build failed: ${build.status ?? 1}`);
|
|
1429
1544
|
if (build.stderr) {
|
|
@@ -1448,6 +1563,14 @@ program.command("start").description(`Start the ${APP_NAME2} gateway + UI in the
|
|
|
1448
1563
|
program.command("serve").description(`Run the ${APP_NAME2} gateway + UI in the foreground`).option("--ui-host <host>", "UI host").option("--ui-port <port>", "UI port").option("--frontend", "Start UI frontend dev server").option("--frontend-port <port>", "UI frontend dev server port").option("--open", "Open browser after start", false).action(async (opts) => runtime.serve(opts));
|
|
1449
1564
|
program.command("stop").description(`Stop the ${APP_NAME2} background service`).action(async () => runtime.stop());
|
|
1450
1565
|
program.command("agent").description("Interact with the agent directly").option("-m, --message <message>", "Message to send to the agent").option("-s, --session <session>", "Session ID", "cli:default").option("--no-markdown", "Disable Markdown rendering").action(async (opts) => runtime.agent(opts));
|
|
1566
|
+
program.command("update").description(`Update ${APP_NAME2}`).option("--timeout <ms>", "Update command timeout in milliseconds").action(async (opts) => runtime.update(opts));
|
|
1567
|
+
var registerClawHubInstall = (cmd) => {
|
|
1568
|
+
cmd.command("install <slug>").description("Install a skill from ClawHub").option("--version <version>", "Skill version (default: latest)").option("--registry <url>", "ClawHub registry base URL").option("--workdir <dir>", "Workspace directory to install into").option("--dir <dir>", "Skills directory name (default: skills)").option("-f, --force", "Overwrite existing skill files", false).action(async (slug, opts) => runtime.skillsInstall({ slug, ...opts }));
|
|
1569
|
+
};
|
|
1570
|
+
var skills = program.command("skills").description("Manage skills");
|
|
1571
|
+
registerClawHubInstall(skills);
|
|
1572
|
+
var clawhub = program.command("clawhub").description("Install skills from ClawHub");
|
|
1573
|
+
registerClawHubInstall(clawhub);
|
|
1451
1574
|
var channels = program.command("channels").description("Manage channels");
|
|
1452
1575
|
channels.command("status").description("Show channel status").action(() => runtime.channelsStatus());
|
|
1453
1576
|
channels.command("login").description("Link device via QR code").action(() => runtime.channelsLogin());
|