nextclaw 0.4.5 → 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.
Files changed (2) hide show
  1. package/dist/cli/index.js +117 -92
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -43,19 +43,16 @@ import {
43
43
  rmSync as rmSync2,
44
44
  writeFileSync as writeFileSync2
45
45
  } from "fs";
46
- import { dirname, join as join4, resolve as resolve4 } from "path";
46
+ import { dirname, join as join3, resolve as resolve4 } from "path";
47
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 fileURLToPath3 } from "url";
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";
@@ -297,6 +294,50 @@ async function prompt(rl, question) {
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 timeoutMs = params.timeoutMs ?? 20 * 6e4;
507
- const gatewayDir = resolve2(fileURLToPath2(new URL(".", import.meta.url)));
508
- const cliDir = resolve2(gatewayDir, "..");
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,7 +554,8 @@ var GatewayControllerImpl = class {
566
554
  ok: true,
567
555
  note: params.note ?? null,
568
556
  restart: { scheduled: true, delayMs },
569
- steps
557
+ strategy: result.strategy,
558
+ steps: result.steps
570
559
  };
571
560
  }
572
561
  };
@@ -574,7 +563,7 @@ var GatewayControllerImpl = class {
574
563
  // src/cli/skills/clawhub.ts
575
564
  import { spawnSync as spawnSync2 } from "child_process";
576
565
  import { existsSync as existsSync3 } from "fs";
577
- import { isAbsolute, join as join3, resolve as resolve3 } from "path";
566
+ import { isAbsolute, join as join2, resolve as resolve3 } from "path";
578
567
  async function installClawHubSkill(options) {
579
568
  const slug = options.slug.trim();
580
569
  if (!slug) {
@@ -586,7 +575,7 @@ async function installClawHubSkill(options) {
586
575
  }
587
576
  const dirName = options.dir?.trim() || "skills";
588
577
  const destinationDir = isAbsolute(dirName) ? resolve3(dirName, slug) : resolve3(workdir, dirName, slug);
589
- const skillFile = join3(destinationDir, "SKILL.md");
578
+ const skillFile = join2(destinationDir, "SKILL.md");
590
579
  if (!options.force && existsSync3(destinationDir)) {
591
580
  if (existsSync3(skillFile)) {
592
581
  return {
@@ -776,7 +765,7 @@ var CliRuntime = class {
776
765
  }
777
766
  const config = loadConfig();
778
767
  const workspaceSetting = config.agents.defaults.workspace;
779
- const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ? join4(getDataDir2(), DEFAULT_WORKSPACE_DIR) : expandHome(workspaceSetting);
768
+ const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ? join3(getDataDir2(), DEFAULT_WORKSPACE_DIR) : expandHome(workspaceSetting);
780
769
  const workspaceExisted = existsSync4(workspacePath);
781
770
  mkdirSync2(workspacePath, { recursive: true });
782
771
  const templateResult = this.createWorkspaceTemplates(workspacePath, { force });
@@ -941,7 +930,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
941
930
  }
942
931
  console.log(`${this.logo} Interactive mode (type exit or Ctrl+C to quit)
943
932
  `);
944
- const historyFile = join4(getDataDir2(), "history", "cli_history");
933
+ const historyFile = join3(getDataDir2(), "history", "cli_history");
945
934
  const historyDir = resolve4(historyFile, "..");
946
935
  mkdirSync2(historyDir, { recursive: true });
947
936
  const history = existsSync4(historyFile) ? readFileSync3(historyFile, "utf-8").split("\n").filter(Boolean) : [];
@@ -970,6 +959,41 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
970
959
  printAgentResponse(response);
971
960
  }
972
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
+ }
973
997
  async skillsInstall(options) {
974
998
  const workdir = options.workdir ? expandHome(options.workdir) : getWorkspacePath();
975
999
  const result = await installClawHubSkill({
@@ -1012,7 +1036,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1012
1036
  }
1013
1037
  }
1014
1038
  cronList(opts) {
1015
- const storePath = join4(getDataDir2(), "cron", "jobs.json");
1039
+ const storePath = join3(getDataDir2(), "cron", "jobs.json");
1016
1040
  const service = new CronService(storePath);
1017
1041
  const jobs = service.listJobs(Boolean(opts.all));
1018
1042
  if (!jobs.length) {
@@ -1032,7 +1056,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1032
1056
  }
1033
1057
  }
1034
1058
  cronAdd(opts) {
1035
- const storePath = join4(getDataDir2(), "cron", "jobs.json");
1059
+ const storePath = join3(getDataDir2(), "cron", "jobs.json");
1036
1060
  const service = new CronService(storePath);
1037
1061
  let schedule = null;
1038
1062
  if (opts.every) {
@@ -1057,7 +1081,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1057
1081
  console.log(`\u2713 Added job '${job.name}' (${job.id})`);
1058
1082
  }
1059
1083
  cronRemove(jobId) {
1060
- const storePath = join4(getDataDir2(), "cron", "jobs.json");
1084
+ const storePath = join3(getDataDir2(), "cron", "jobs.json");
1061
1085
  const service = new CronService(storePath);
1062
1086
  if (service.removeJob(jobId)) {
1063
1087
  console.log(`\u2713 Removed job ${jobId}`);
@@ -1066,7 +1090,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1066
1090
  }
1067
1091
  }
1068
1092
  cronEnable(jobId, opts) {
1069
- const storePath = join4(getDataDir2(), "cron", "jobs.json");
1093
+ const storePath = join3(getDataDir2(), "cron", "jobs.json");
1070
1094
  const service = new CronService(storePath);
1071
1095
  const job = service.enableJob(jobId, !opts.disable);
1072
1096
  if (job) {
@@ -1076,7 +1100,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1076
1100
  }
1077
1101
  }
1078
1102
  async cronRun(jobId, opts) {
1079
- const storePath = join4(getDataDir2(), "cron", "jobs.json");
1103
+ const storePath = join3(getDataDir2(), "cron", "jobs.json");
1080
1104
  const service = new CronService(storePath);
1081
1105
  const ok = await service.runJob(jobId, Boolean(opts.force));
1082
1106
  console.log(ok ? "\u2713 Job executed" : `Failed to run job ${jobId}`);
@@ -1108,7 +1132,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1108
1132
  const provider = options.allowMissingProvider === true ? this.makeProvider(config, { allowMissing: true }) : this.makeProvider(config);
1109
1133
  const providerManager = provider ? new ProviderManager(provider) : null;
1110
1134
  const sessionManager = new SessionManager(getWorkspacePath(config.agents.defaults.workspace));
1111
- const cronStorePath = join4(getDataDir2(), "cron", "jobs.json");
1135
+ const cronStorePath = join3(getDataDir2(), "cron", "jobs.json");
1112
1136
  const cron2 = new CronService(cronStorePath);
1113
1137
  const uiConfig = resolveUiConfig(config, options.uiOverrides);
1114
1138
  const uiStaticDir = options.uiStaticDir === void 0 ? resolveUiStaticDir() : options.uiStaticDir;
@@ -1382,11 +1406,11 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1382
1406
  { source: "memory/MEMORY.md", target: "memory/MEMORY.md" }
1383
1407
  ];
1384
1408
  for (const entry of templateFiles) {
1385
- const filePath = join4(workspace, entry.target);
1409
+ const filePath = join3(workspace, entry.target);
1386
1410
  if (!force && existsSync4(filePath)) {
1387
1411
  continue;
1388
1412
  }
1389
- const templatePath = join4(templateDir, entry.source);
1413
+ const templatePath = join3(templateDir, entry.source);
1390
1414
  if (!existsSync4(templatePath)) {
1391
1415
  console.warn(`Warning: Template file missing: ${templatePath}`);
1392
1416
  continue;
@@ -1397,15 +1421,15 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1397
1421
  writeFileSync2(filePath, content);
1398
1422
  created.push(entry.target);
1399
1423
  }
1400
- const memoryDir = join4(workspace, "memory");
1424
+ const memoryDir = join3(workspace, "memory");
1401
1425
  if (!existsSync4(memoryDir)) {
1402
1426
  mkdirSync2(memoryDir, { recursive: true });
1403
- created.push(join4("memory", ""));
1427
+ created.push(join3("memory", ""));
1404
1428
  }
1405
- const skillsDir = join4(workspace, "skills");
1429
+ const skillsDir = join3(workspace, "skills");
1406
1430
  if (!existsSync4(skillsDir)) {
1407
1431
  mkdirSync2(skillsDir, { recursive: true });
1408
- created.push(join4("skills", ""));
1432
+ created.push(join3("skills", ""));
1409
1433
  }
1410
1434
  const seeded = this.seedBuiltinSkills(skillsDir, { force });
1411
1435
  if (seeded > 0) {
@@ -1428,11 +1452,11 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1428
1452
  if (!entry.isDirectory()) {
1429
1453
  continue;
1430
1454
  }
1431
- const src = join4(sourceDir, entry.name);
1432
- if (!existsSync4(join4(src, "SKILL.md"))) {
1455
+ const src = join3(sourceDir, entry.name);
1456
+ if (!existsSync4(join3(src, "SKILL.md"))) {
1433
1457
  continue;
1434
1458
  }
1435
- const dest = join4(targetDir, entry.name);
1459
+ const dest = join3(targetDir, entry.name);
1436
1460
  if (!force && existsSync4(dest)) {
1437
1461
  continue;
1438
1462
  }
@@ -1446,11 +1470,11 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1446
1470
  const require2 = createRequire(import.meta.url);
1447
1471
  const entry = require2.resolve("nextclaw-core");
1448
1472
  const pkgRoot = resolve4(dirname(entry), "..");
1449
- const distSkills = join4(pkgRoot, "dist", "skills");
1473
+ const distSkills = join3(pkgRoot, "dist", "skills");
1450
1474
  if (existsSync4(distSkills)) {
1451
1475
  return distSkills;
1452
1476
  }
1453
- const srcSkills = join4(pkgRoot, "src", "agent", "skills");
1477
+ const srcSkills = join3(pkgRoot, "src", "agent", "skills");
1454
1478
  if (existsSync4(srcSkills)) {
1455
1479
  return srcSkills;
1456
1480
  }
@@ -1464,9 +1488,9 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1464
1488
  if (override) {
1465
1489
  return override;
1466
1490
  }
1467
- const cliDir = resolve4(fileURLToPath3(new URL(".", import.meta.url)));
1491
+ const cliDir = resolve4(fileURLToPath2(new URL(".", import.meta.url)));
1468
1492
  const pkgRoot = resolve4(cliDir, "..", "..");
1469
- const candidates = [join4(pkgRoot, "templates")];
1493
+ const candidates = [join3(pkgRoot, "templates")];
1470
1494
  for (const candidate of candidates) {
1471
1495
  if (existsSync4(candidate)) {
1472
1496
  return candidate;
@@ -1475,22 +1499,22 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1475
1499
  return null;
1476
1500
  }
1477
1501
  getBridgeDir() {
1478
- const userBridge = join4(getDataDir2(), "bridge");
1479
- if (existsSync4(join4(userBridge, "dist", "index.js"))) {
1502
+ const userBridge = join3(getDataDir2(), "bridge");
1503
+ if (existsSync4(join3(userBridge, "dist", "index.js"))) {
1480
1504
  return userBridge;
1481
1505
  }
1482
1506
  if (!which("npm")) {
1483
1507
  console.error("npm not found. Please install Node.js >= 18.");
1484
1508
  process.exit(1);
1485
1509
  }
1486
- const cliDir = resolve4(fileURLToPath3(new URL(".", import.meta.url)));
1510
+ const cliDir = resolve4(fileURLToPath2(new URL(".", import.meta.url)));
1487
1511
  const pkgRoot = resolve4(cliDir, "..", "..");
1488
- const pkgBridge = join4(pkgRoot, "bridge");
1489
- const srcBridge = join4(pkgRoot, "..", "..", "bridge");
1512
+ const pkgBridge = join3(pkgRoot, "bridge");
1513
+ const srcBridge = join3(pkgRoot, "..", "..", "bridge");
1490
1514
  let source = null;
1491
- if (existsSync4(join4(pkgBridge, "package.json"))) {
1515
+ if (existsSync4(join3(pkgBridge, "package.json"))) {
1492
1516
  source = pkgBridge;
1493
- } else if (existsSync4(join4(srcBridge, "package.json"))) {
1517
+ } else if (existsSync4(join3(srcBridge, "package.json"))) {
1494
1518
  source = srcBridge;
1495
1519
  }
1496
1520
  if (!source) {
@@ -1539,6 +1563,7 @@ program.command("start").description(`Start the ${APP_NAME2} gateway + UI in the
1539
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));
1540
1564
  program.command("stop").description(`Stop the ${APP_NAME2} background service`).action(async () => runtime.stop());
1541
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));
1542
1567
  var registerClawHubInstall = (cmd) => {
1543
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 }));
1544
1569
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nextclaw",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
4
4
  "description": "Lightweight personal AI assistant with CLI, multi-provider routing, and channel integrations.",
5
5
  "private": false,
6
6
  "type": "module",