nextclaw 0.4.5 → 0.4.7

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 +129 -159
  2. package/package.json +5 -4
package/dist/cli/index.js CHANGED
@@ -43,21 +43,20 @@ 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
- ConfigSchema
57
+ buildConfigSchema,
58
+ ConfigSchema,
59
+ redactConfigObject
61
60
  } from "nextclaw-core";
62
61
 
63
62
  // src/cli/utils.ts
@@ -66,7 +65,7 @@ import { join, resolve } from "path";
66
65
  import { spawn } from "child_process";
67
66
  import { createServer } from "net";
68
67
  import { fileURLToPath } from "url";
69
- import { getDataDir } from "nextclaw-core";
68
+ import { getDataDir, getPackageVersion } from "nextclaw-core";
70
69
  function resolveUiConfig(config, overrides) {
71
70
  const base = config.ui ?? { enabled: false, host: "127.0.0.1", port: 18791, open: false };
72
71
  return { ...base, ...overrides ?? {} };
@@ -207,17 +206,6 @@ function openBrowser(url) {
207
206
  const child = spawn(command, args, { stdio: "ignore", detached: true });
208
207
  child.unref();
209
208
  }
210
- function getPackageVersion() {
211
- try {
212
- const cliDir = resolve(fileURLToPath(new URL(".", import.meta.url)));
213
- const pkgPath = resolve(cliDir, "..", "..", "package.json");
214
- const raw = readFileSync(pkgPath, "utf-8");
215
- const parsed = JSON.parse(raw);
216
- return typeof parsed.version === "string" ? parsed.version : "0.0.0";
217
- } catch {
218
- return "0.0.0";
219
- }
220
- }
221
209
  function which(binary) {
222
210
  const paths = (process.env.PATH ?? "").split(":");
223
211
  for (const dir of paths) {
@@ -297,26 +285,52 @@ async function prompt(rl, question) {
297
285
  });
298
286
  }
299
287
 
300
- // src/cli/gateway/controller.ts
301
- var hashRaw = (raw) => createHash("sha256").update(raw).digest("hex");
302
- var redactConfig = (value) => {
303
- if (Array.isArray(value)) {
304
- return value.map((entry) => redactConfig(entry));
305
- }
306
- if (!value || typeof value !== "object") {
307
- return value;
288
+ // src/cli/update/runner.ts
289
+ import { spawnSync } from "child_process";
290
+ import { resolve as resolve2 } from "path";
291
+ var DEFAULT_TIMEOUT_MS = 20 * 6e4;
292
+ function runSelfUpdate(options = {}) {
293
+ const steps = [];
294
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
295
+ const updateCommand = options.updateCommand ?? process.env.NEXTCLAW_UPDATE_COMMAND?.trim();
296
+ const packageName = options.packageName ?? "nextclaw";
297
+ const runStep = (cmd, args, cwd) => {
298
+ const result = spawnSync(cmd, args, {
299
+ cwd,
300
+ encoding: "utf-8",
301
+ timeout: timeoutMs,
302
+ stdio: "pipe"
303
+ });
304
+ steps.push({
305
+ cmd,
306
+ args,
307
+ cwd,
308
+ code: result.status,
309
+ stdout: (result.stdout ?? "").toString().slice(0, 4e3),
310
+ stderr: (result.stderr ?? "").toString().slice(0, 4e3)
311
+ });
312
+ return { ok: result.status === 0, code: result.status };
313
+ };
314
+ if (updateCommand) {
315
+ const cwd = options.cwd ? resolve2(options.cwd) : process.cwd();
316
+ const ok = runStep("sh", ["-c", updateCommand], cwd);
317
+ if (!ok.ok) {
318
+ return { ok: false, error: "update command failed", strategy: "command", steps };
319
+ }
320
+ return { ok: true, strategy: "command", steps };
308
321
  }
309
- const entries = value;
310
- const output = {};
311
- for (const [key, val] of Object.entries(entries)) {
312
- if (/apiKey|token|secret|password|appId|clientSecret|accessKey/i.test(key)) {
313
- output[key] = val ? "***" : val;
314
- continue;
322
+ if (which("npm")) {
323
+ const ok = runStep("npm", ["i", "-g", packageName], process.cwd());
324
+ if (!ok.ok) {
325
+ return { ok: false, error: `npm install -g ${packageName} failed`, strategy: "npm", steps };
315
326
  }
316
- output[key] = redactConfig(val);
327
+ return { ok: true, strategy: "npm", steps };
317
328
  }
318
- return output;
319
- };
329
+ return { ok: false, error: "no update strategy available", strategy: "none", steps };
330
+ }
331
+
332
+ // src/cli/gateway/controller.ts
333
+ var hashRaw = (raw) => createHash("sha256").update(raw).digest("hex");
320
334
  var readConfigSnapshot = (getConfigPath2) => {
321
335
  const path = getConfigPath2();
322
336
  let raw = "";
@@ -341,9 +355,14 @@ var readConfigSnapshot = (getConfigPath2) => {
341
355
  raw = JSON.stringify(config, null, 2);
342
356
  }
343
357
  const hash = hashRaw(raw);
344
- const redacted = redactConfig(config);
358
+ const schema = buildConfigSchema({ version: getPackageVersion() });
359
+ const redacted = redactConfigObject(config, schema.uiHints);
345
360
  return { raw: valid ? JSON.stringify(redacted, null, 2) : null, hash: valid ? hash : null, config, redacted, valid };
346
361
  };
362
+ var redactValue = (value) => {
363
+ const schema = buildConfigSchema({ version: getPackageVersion() });
364
+ return redactConfigObject(value, schema.uiHints);
365
+ };
347
366
  var mergeDeep = (base, patch) => {
348
367
  const next = { ...base };
349
368
  for (const [key, value] of Object.entries(patch)) {
@@ -360,29 +379,6 @@ var mergeDeep = (base, patch) => {
360
379
  }
361
380
  return next;
362
381
  };
363
- var buildSchemaFromValue = (value) => {
364
- if (Array.isArray(value)) {
365
- const item = value.length ? buildSchemaFromValue(value[0]) : { type: "string" };
366
- return { type: "array", items: item };
367
- }
368
- if (value && typeof value === "object") {
369
- const props = {};
370
- for (const [key, val] of Object.entries(value)) {
371
- props[key] = buildSchemaFromValue(val);
372
- }
373
- return { type: "object", properties: props };
374
- }
375
- if (typeof value === "number") {
376
- return { type: "number" };
377
- }
378
- if (typeof value === "boolean") {
379
- return { type: "boolean" };
380
- }
381
- if (value === null) {
382
- return { type: ["null", "string"] };
383
- }
384
- return { type: "string" };
385
- };
386
382
  var scheduleRestart = (delayMs, reason) => {
387
383
  const delay = typeof delayMs === "number" && Number.isFinite(delayMs) ? Math.max(0, delayMs) : 100;
388
384
  console.log(`Gateway restart requested via tool${reason ? ` (${reason})` : ""}.`);
@@ -421,17 +417,7 @@ var GatewayControllerImpl = class {
421
417
  };
422
418
  }
423
419
  async getConfigSchema() {
424
- const base = ConfigSchema.parse({});
425
- return {
426
- schema: {
427
- ...buildSchemaFromValue(base),
428
- title: "NextClawConfig",
429
- description: "NextClaw config schema (simplified)"
430
- },
431
- uiHints: {},
432
- version: getPackageVersion(),
433
- generatedAt: (/* @__PURE__ */ new Date()).toISOString()
434
- };
420
+ return buildConfigSchema({ version: getPackageVersion() });
435
421
  }
436
422
  async applyConfig(params) {
437
423
  const snapshot = readConfigSnapshot(this.deps.getConfigPath);
@@ -463,7 +449,7 @@ var GatewayControllerImpl = class {
463
449
  ok: true,
464
450
  note: params.note ?? null,
465
451
  path: this.deps.getConfigPath(),
466
- config: redactConfig(validated),
452
+ config: redactValue(validated),
467
453
  restart: { scheduled: true, delayMs }
468
454
  };
469
455
  }
@@ -498,67 +484,14 @@ var GatewayControllerImpl = class {
498
484
  ok: true,
499
485
  note: params.note ?? null,
500
486
  path: this.deps.getConfigPath(),
501
- config: redactConfig(validated),
487
+ config: redactValue(validated),
502
488
  restart: { scheduled: true, delayMs }
503
489
  };
504
490
  }
505
491
  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 };
492
+ const result = runSelfUpdate({ timeoutMs: params.timeoutMs });
493
+ if (!result.ok) {
494
+ return { ok: false, error: result.error ?? "update failed", steps: result.steps };
562
495
  }
563
496
  const delayMs = params.restartDelayMs ?? 0;
564
497
  scheduleRestart(delayMs, "update.run");
@@ -566,7 +499,8 @@ var GatewayControllerImpl = class {
566
499
  ok: true,
567
500
  note: params.note ?? null,
568
501
  restart: { scheduled: true, delayMs },
569
- steps
502
+ strategy: result.strategy,
503
+ steps: result.steps
570
504
  };
571
505
  }
572
506
  };
@@ -574,7 +508,7 @@ var GatewayControllerImpl = class {
574
508
  // src/cli/skills/clawhub.ts
575
509
  import { spawnSync as spawnSync2 } from "child_process";
576
510
  import { existsSync as existsSync3 } from "fs";
577
- import { isAbsolute, join as join3, resolve as resolve3 } from "path";
511
+ import { isAbsolute, join as join2, resolve as resolve3 } from "path";
578
512
  async function installClawHubSkill(options) {
579
513
  const slug = options.slug.trim();
580
514
  if (!slug) {
@@ -586,7 +520,7 @@ async function installClawHubSkill(options) {
586
520
  }
587
521
  const dirName = options.dir?.trim() || "skills";
588
522
  const destinationDir = isAbsolute(dirName) ? resolve3(dirName, slug) : resolve3(workdir, dirName, slug);
589
- const skillFile = join3(destinationDir, "SKILL.md");
523
+ const skillFile = join2(destinationDir, "SKILL.md");
590
524
  if (!options.force && existsSync3(destinationDir)) {
591
525
  if (existsSync3(skillFile)) {
592
526
  return {
@@ -776,7 +710,7 @@ var CliRuntime = class {
776
710
  }
777
711
  const config = loadConfig();
778
712
  const workspaceSetting = config.agents.defaults.workspace;
779
- const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ? join4(getDataDir2(), DEFAULT_WORKSPACE_DIR) : expandHome(workspaceSetting);
713
+ const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ? join3(getDataDir2(), DEFAULT_WORKSPACE_DIR) : expandHome(workspaceSetting);
780
714
  const workspaceExisted = existsSync4(workspacePath);
781
715
  mkdirSync2(workspacePath, { recursive: true });
782
716
  const templateResult = this.createWorkspaceTemplates(workspacePath, { force });
@@ -941,7 +875,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
941
875
  }
942
876
  console.log(`${this.logo} Interactive mode (type exit or Ctrl+C to quit)
943
877
  `);
944
- const historyFile = join4(getDataDir2(), "history", "cli_history");
878
+ const historyFile = join3(getDataDir2(), "history", "cli_history");
945
879
  const historyDir = resolve4(historyFile, "..");
946
880
  mkdirSync2(historyDir, { recursive: true });
947
881
  const history = existsSync4(historyFile) ? readFileSync3(historyFile, "utf-8").split("\n").filter(Boolean) : [];
@@ -970,6 +904,41 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
970
904
  printAgentResponse(response);
971
905
  }
972
906
  }
907
+ async update(opts) {
908
+ let timeoutMs;
909
+ if (opts.timeout !== void 0) {
910
+ const parsed = Number(opts.timeout);
911
+ if (!Number.isFinite(parsed) || parsed <= 0) {
912
+ console.error("Invalid --timeout value. Provide milliseconds (e.g. 1200000).");
913
+ process.exit(1);
914
+ }
915
+ timeoutMs = parsed;
916
+ }
917
+ const result = runSelfUpdate({ timeoutMs, cwd: process.cwd() });
918
+ const printSteps = () => {
919
+ for (const step of result.steps) {
920
+ console.log(`- ${step.cmd} ${step.args.join(" ")} (code ${step.code ?? "?"})`);
921
+ if (step.stderr) {
922
+ console.log(` stderr: ${step.stderr}`);
923
+ }
924
+ if (step.stdout) {
925
+ console.log(` stdout: ${step.stdout}`);
926
+ }
927
+ }
928
+ };
929
+ if (!result.ok) {
930
+ console.error(`Update failed: ${result.error ?? "unknown error"}`);
931
+ if (result.steps.length > 0) {
932
+ printSteps();
933
+ }
934
+ process.exit(1);
935
+ }
936
+ console.log(`\u2713 Update complete (${result.strategy})`);
937
+ const state = readServiceState();
938
+ if (state && isProcessRunning(state.pid)) {
939
+ console.log(`Tip: restart ${APP_NAME} to apply the update.`);
940
+ }
941
+ }
973
942
  async skillsInstall(options) {
974
943
  const workdir = options.workdir ? expandHome(options.workdir) : getWorkspacePath();
975
944
  const result = await installClawHubSkill({
@@ -1012,7 +981,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1012
981
  }
1013
982
  }
1014
983
  cronList(opts) {
1015
- const storePath = join4(getDataDir2(), "cron", "jobs.json");
984
+ const storePath = join3(getDataDir2(), "cron", "jobs.json");
1016
985
  const service = new CronService(storePath);
1017
986
  const jobs = service.listJobs(Boolean(opts.all));
1018
987
  if (!jobs.length) {
@@ -1032,7 +1001,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1032
1001
  }
1033
1002
  }
1034
1003
  cronAdd(opts) {
1035
- const storePath = join4(getDataDir2(), "cron", "jobs.json");
1004
+ const storePath = join3(getDataDir2(), "cron", "jobs.json");
1036
1005
  const service = new CronService(storePath);
1037
1006
  let schedule = null;
1038
1007
  if (opts.every) {
@@ -1057,7 +1026,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1057
1026
  console.log(`\u2713 Added job '${job.name}' (${job.id})`);
1058
1027
  }
1059
1028
  cronRemove(jobId) {
1060
- const storePath = join4(getDataDir2(), "cron", "jobs.json");
1029
+ const storePath = join3(getDataDir2(), "cron", "jobs.json");
1061
1030
  const service = new CronService(storePath);
1062
1031
  if (service.removeJob(jobId)) {
1063
1032
  console.log(`\u2713 Removed job ${jobId}`);
@@ -1066,7 +1035,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1066
1035
  }
1067
1036
  }
1068
1037
  cronEnable(jobId, opts) {
1069
- const storePath = join4(getDataDir2(), "cron", "jobs.json");
1038
+ const storePath = join3(getDataDir2(), "cron", "jobs.json");
1070
1039
  const service = new CronService(storePath);
1071
1040
  const job = service.enableJob(jobId, !opts.disable);
1072
1041
  if (job) {
@@ -1076,7 +1045,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1076
1045
  }
1077
1046
  }
1078
1047
  async cronRun(jobId, opts) {
1079
- const storePath = join4(getDataDir2(), "cron", "jobs.json");
1048
+ const storePath = join3(getDataDir2(), "cron", "jobs.json");
1080
1049
  const service = new CronService(storePath);
1081
1050
  const ok = await service.runJob(jobId, Boolean(opts.force));
1082
1051
  console.log(ok ? "\u2713 Job executed" : `Failed to run job ${jobId}`);
@@ -1108,7 +1077,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1108
1077
  const provider = options.allowMissingProvider === true ? this.makeProvider(config, { allowMissing: true }) : this.makeProvider(config);
1109
1078
  const providerManager = provider ? new ProviderManager(provider) : null;
1110
1079
  const sessionManager = new SessionManager(getWorkspacePath(config.agents.defaults.workspace));
1111
- const cronStorePath = join4(getDataDir2(), "cron", "jobs.json");
1080
+ const cronStorePath = join3(getDataDir2(), "cron", "jobs.json");
1112
1081
  const cron2 = new CronService(cronStorePath);
1113
1082
  const uiConfig = resolveUiConfig(config, options.uiOverrides);
1114
1083
  const uiStaticDir = options.uiStaticDir === void 0 ? resolveUiStaticDir() : options.uiStaticDir;
@@ -1382,11 +1351,11 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1382
1351
  { source: "memory/MEMORY.md", target: "memory/MEMORY.md" }
1383
1352
  ];
1384
1353
  for (const entry of templateFiles) {
1385
- const filePath = join4(workspace, entry.target);
1354
+ const filePath = join3(workspace, entry.target);
1386
1355
  if (!force && existsSync4(filePath)) {
1387
1356
  continue;
1388
1357
  }
1389
- const templatePath = join4(templateDir, entry.source);
1358
+ const templatePath = join3(templateDir, entry.source);
1390
1359
  if (!existsSync4(templatePath)) {
1391
1360
  console.warn(`Warning: Template file missing: ${templatePath}`);
1392
1361
  continue;
@@ -1397,15 +1366,15 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1397
1366
  writeFileSync2(filePath, content);
1398
1367
  created.push(entry.target);
1399
1368
  }
1400
- const memoryDir = join4(workspace, "memory");
1369
+ const memoryDir = join3(workspace, "memory");
1401
1370
  if (!existsSync4(memoryDir)) {
1402
1371
  mkdirSync2(memoryDir, { recursive: true });
1403
- created.push(join4("memory", ""));
1372
+ created.push(join3("memory", ""));
1404
1373
  }
1405
- const skillsDir = join4(workspace, "skills");
1374
+ const skillsDir = join3(workspace, "skills");
1406
1375
  if (!existsSync4(skillsDir)) {
1407
1376
  mkdirSync2(skillsDir, { recursive: true });
1408
- created.push(join4("skills", ""));
1377
+ created.push(join3("skills", ""));
1409
1378
  }
1410
1379
  const seeded = this.seedBuiltinSkills(skillsDir, { force });
1411
1380
  if (seeded > 0) {
@@ -1428,11 +1397,11 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1428
1397
  if (!entry.isDirectory()) {
1429
1398
  continue;
1430
1399
  }
1431
- const src = join4(sourceDir, entry.name);
1432
- if (!existsSync4(join4(src, "SKILL.md"))) {
1400
+ const src = join3(sourceDir, entry.name);
1401
+ if (!existsSync4(join3(src, "SKILL.md"))) {
1433
1402
  continue;
1434
1403
  }
1435
- const dest = join4(targetDir, entry.name);
1404
+ const dest = join3(targetDir, entry.name);
1436
1405
  if (!force && existsSync4(dest)) {
1437
1406
  continue;
1438
1407
  }
@@ -1446,11 +1415,11 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1446
1415
  const require2 = createRequire(import.meta.url);
1447
1416
  const entry = require2.resolve("nextclaw-core");
1448
1417
  const pkgRoot = resolve4(dirname(entry), "..");
1449
- const distSkills = join4(pkgRoot, "dist", "skills");
1418
+ const distSkills = join3(pkgRoot, "dist", "skills");
1450
1419
  if (existsSync4(distSkills)) {
1451
1420
  return distSkills;
1452
1421
  }
1453
- const srcSkills = join4(pkgRoot, "src", "agent", "skills");
1422
+ const srcSkills = join3(pkgRoot, "src", "agent", "skills");
1454
1423
  if (existsSync4(srcSkills)) {
1455
1424
  return srcSkills;
1456
1425
  }
@@ -1464,9 +1433,9 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1464
1433
  if (override) {
1465
1434
  return override;
1466
1435
  }
1467
- const cliDir = resolve4(fileURLToPath3(new URL(".", import.meta.url)));
1436
+ const cliDir = resolve4(fileURLToPath2(new URL(".", import.meta.url)));
1468
1437
  const pkgRoot = resolve4(cliDir, "..", "..");
1469
- const candidates = [join4(pkgRoot, "templates")];
1438
+ const candidates = [join3(pkgRoot, "templates")];
1470
1439
  for (const candidate of candidates) {
1471
1440
  if (existsSync4(candidate)) {
1472
1441
  return candidate;
@@ -1475,22 +1444,22 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1475
1444
  return null;
1476
1445
  }
1477
1446
  getBridgeDir() {
1478
- const userBridge = join4(getDataDir2(), "bridge");
1479
- if (existsSync4(join4(userBridge, "dist", "index.js"))) {
1447
+ const userBridge = join3(getDataDir2(), "bridge");
1448
+ if (existsSync4(join3(userBridge, "dist", "index.js"))) {
1480
1449
  return userBridge;
1481
1450
  }
1482
1451
  if (!which("npm")) {
1483
1452
  console.error("npm not found. Please install Node.js >= 18.");
1484
1453
  process.exit(1);
1485
1454
  }
1486
- const cliDir = resolve4(fileURLToPath3(new URL(".", import.meta.url)));
1455
+ const cliDir = resolve4(fileURLToPath2(new URL(".", import.meta.url)));
1487
1456
  const pkgRoot = resolve4(cliDir, "..", "..");
1488
- const pkgBridge = join4(pkgRoot, "bridge");
1489
- const srcBridge = join4(pkgRoot, "..", "..", "bridge");
1457
+ const pkgBridge = join3(pkgRoot, "bridge");
1458
+ const srcBridge = join3(pkgRoot, "..", "..", "bridge");
1490
1459
  let source = null;
1491
- if (existsSync4(join4(pkgBridge, "package.json"))) {
1460
+ if (existsSync4(join3(pkgBridge, "package.json"))) {
1492
1461
  source = pkgBridge;
1493
- } else if (existsSync4(join4(srcBridge, "package.json"))) {
1462
+ } else if (existsSync4(join3(srcBridge, "package.json"))) {
1494
1463
  source = srcBridge;
1495
1464
  }
1496
1465
  if (!source) {
@@ -1539,6 +1508,7 @@ program.command("start").description(`Start the ${APP_NAME2} gateway + UI in the
1539
1508
  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
1509
  program.command("stop").description(`Stop the ${APP_NAME2} background service`).action(async () => runtime.stop());
1541
1510
  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));
1511
+ program.command("update").description(`Update ${APP_NAME2}`).option("--timeout <ms>", "Update command timeout in milliseconds").action(async (opts) => runtime.update(opts));
1542
1512
  var registerClawHubInstall = (cmd) => {
1543
1513
  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
1514
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nextclaw",
3
- "version": "0.4.5",
3
+ "version": "0.4.7",
4
4
  "description": "Lightweight personal AI assistant with CLI, multi-provider routing, and channel integrations.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -38,8 +38,8 @@
38
38
  "dependencies": {
39
39
  "chokidar": "^3.6.0",
40
40
  "commander": "^12.1.0",
41
- "nextclaw-core": "^0.4.4",
42
- "nextclaw-server": "^0.3.2"
41
+ "nextclaw-core": "^0.4.5",
42
+ "nextclaw-server": "^0.3.3"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/node": "^20.17.6",
@@ -54,7 +54,8 @@
54
54
  "vitest": "^2.1.2"
55
55
  },
56
56
  "scripts": {
57
- "dev": "pnpm -C ../nextclaw-core build && pnpm -C ../nextclaw-server build && tsx src/cli/index.ts",
57
+ "dev": "tsx watch --tsconfig tsconfig.json src/cli/index.ts",
58
+ "dev:build": "pnpm -C ../nextclaw-core build && pnpm -C ../nextclaw-server build && tsx src/cli/index.ts",
58
59
  "build": "pnpm -C ../nextclaw-core build && pnpm -C ../nextclaw-server build && tsup src/index.ts src/cli/index.ts --format esm --dts --out-dir dist && node scripts/copy-ui-dist.mjs",
59
60
  "start": "node dist/cli.js",
60
61
  "lint": "eslint .",