nextclaw 0.6.34 → 0.6.36

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 CHANGED
@@ -20,11 +20,15 @@ import {
20
20
  DEFAULT_WORKSPACE_DIR,
21
21
  DEFAULT_WORKSPACE_PATH
22
22
  } from "@nextclaw/core";
23
- import { resolvePluginChannelMessageToolHints as resolvePluginChannelMessageToolHints2 } from "@nextclaw/openclaw-compat";
23
+ import {
24
+ getPluginChannelBindings as getPluginChannelBindings3,
25
+ resolvePluginChannelMessageToolHints as resolvePluginChannelMessageToolHints2,
26
+ setPluginRuntimeBridge as setPluginRuntimeBridge2
27
+ } from "@nextclaw/openclaw-compat";
24
28
  import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
25
29
  import { join as join6, resolve as resolve9 } from "path";
26
30
  import { createInterface as createInterface2 } from "readline";
27
- import { fileURLToPath as fileURLToPath3 } from "url";
31
+ import { fileURLToPath as fileURLToPath4 } from "url";
28
32
  import { spawn as spawn3 } from "child_process";
29
33
 
30
34
  // src/cli/restart-coordinator.ts
@@ -1861,6 +1865,7 @@ import { startUiServer } from "@nextclaw/server";
1861
1865
  import { closeSync, mkdirSync as mkdirSync3, openSync } from "fs";
1862
1866
  import { join as join4, resolve as resolve7 } from "path";
1863
1867
  import { spawn as spawn2 } from "child_process";
1868
+ import { fileURLToPath as fileURLToPath2 } from "url";
1864
1869
  import chokidar from "chokidar";
1865
1870
 
1866
1871
  // src/cli/gateway/controller.ts
@@ -3046,7 +3051,14 @@ var ServiceCommands = class {
3046
3051
  host: uiConfig.host,
3047
3052
  port: uiConfig.port,
3048
3053
  configPath: getConfigPath2(),
3049
- staticDir: uiStaticDir ?? void 0
3054
+ staticDir: uiStaticDir ?? void 0,
3055
+ marketplace: {
3056
+ apiBaseUrl: process.env.NEXTCLAW_MARKETPLACE_API_BASE,
3057
+ installer: {
3058
+ installPlugin: (spec) => this.installMarketplacePlugin(spec),
3059
+ installSkill: (params) => this.installMarketplaceSkill(params)
3060
+ }
3061
+ }
3050
3062
  });
3051
3063
  const uiUrl = `http://${uiServer.host}:${uiServer.port}`;
3052
3064
  console.log(`\u2713 UI API: ${uiUrl}/api`);
@@ -3058,13 +3070,75 @@ var ServiceCommands = class {
3058
3070
  openBrowser(uiUrl);
3059
3071
  }
3060
3072
  }
3073
+ async installMarketplacePlugin(spec) {
3074
+ const output = await this.runCliSubcommand(["plugins", "install", spec]);
3075
+ const summary = this.pickLastOutputLine(output) ?? `Installed plugin: ${spec}`;
3076
+ return { message: summary, output };
3077
+ }
3078
+ async installMarketplaceSkill(params) {
3079
+ const args = ["skills", "install", params.slug];
3080
+ if (params.version) {
3081
+ args.push("--version", params.version);
3082
+ }
3083
+ if (params.registry) {
3084
+ args.push("--registry", params.registry);
3085
+ }
3086
+ if (params.force) {
3087
+ args.push("--force");
3088
+ }
3089
+ const output = await this.runCliSubcommand(args);
3090
+ const summary = this.pickLastOutputLine(output) ?? `Installed skill: ${params.slug}`;
3091
+ return { message: summary, output };
3092
+ }
3093
+ runCliSubcommand(args, timeoutMs = 18e4) {
3094
+ const cliEntry = fileURLToPath2(new URL("../index.js", import.meta.url));
3095
+ return new Promise((resolvePromise, rejectPromise) => {
3096
+ const child = spawn2(process.execPath, [...process.execArgv, cliEntry, ...args], {
3097
+ cwd: process.cwd(),
3098
+ env: process.env,
3099
+ stdio: ["ignore", "pipe", "pipe"]
3100
+ });
3101
+ let stdout = "";
3102
+ let stderr = "";
3103
+ child.stdout?.setEncoding("utf-8");
3104
+ child.stderr?.setEncoding("utf-8");
3105
+ child.stdout?.on("data", (chunk) => {
3106
+ stdout += chunk;
3107
+ });
3108
+ child.stderr?.on("data", (chunk) => {
3109
+ stderr += chunk;
3110
+ });
3111
+ const timer = setTimeout(() => {
3112
+ child.kill("SIGTERM");
3113
+ rejectPromise(new Error(`CLI command timed out after ${timeoutMs}ms`));
3114
+ }, timeoutMs);
3115
+ child.on("error", (error) => {
3116
+ clearTimeout(timer);
3117
+ rejectPromise(new Error(`failed to start CLI command: ${String(error)}`));
3118
+ });
3119
+ child.on("close", (code) => {
3120
+ clearTimeout(timer);
3121
+ const output = `${stdout}
3122
+ ${stderr}`.trim();
3123
+ if (code === 0) {
3124
+ resolvePromise(output);
3125
+ return;
3126
+ }
3127
+ rejectPromise(new Error(output || `CLI command failed with code ${code ?? 1}`));
3128
+ });
3129
+ });
3130
+ }
3131
+ pickLastOutputLine(output) {
3132
+ const lines = output.split("\n").map((line) => line.trim()).filter(Boolean);
3133
+ return lines.length > 0 ? lines[lines.length - 1] : null;
3134
+ }
3061
3135
  };
3062
3136
 
3063
3137
  // src/cli/workspace.ts
3064
3138
  import { cpSync, existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync5, readdirSync, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
3065
3139
  import { createRequire } from "module";
3066
3140
  import { dirname, join as join5, resolve as resolve8 } from "path";
3067
- import { fileURLToPath as fileURLToPath2 } from "url";
3141
+ import { fileURLToPath as fileURLToPath3 } from "url";
3068
3142
  import { APP_NAME as APP_NAME3, getDataDir as getDataDir6 } from "@nextclaw/core";
3069
3143
  import { spawnSync as spawnSync4 } from "child_process";
3070
3144
  var WorkspaceManager = class {
@@ -3171,7 +3245,7 @@ var WorkspaceManager = class {
3171
3245
  if (override) {
3172
3246
  return override;
3173
3247
  }
3174
- const cliDir = resolve8(fileURLToPath2(new URL(".", import.meta.url)));
3248
+ const cliDir = resolve8(fileURLToPath3(new URL(".", import.meta.url)));
3175
3249
  const pkgRoot = resolve8(cliDir, "..", "..");
3176
3250
  const candidates = [join5(pkgRoot, "templates")];
3177
3251
  for (const candidate of candidates) {
@@ -3190,7 +3264,7 @@ var WorkspaceManager = class {
3190
3264
  console.error("npm not found. Please install Node.js >= 18.");
3191
3265
  process.exit(1);
3192
3266
  }
3193
- const cliDir = resolve8(fileURLToPath2(new URL(".", import.meta.url)));
3267
+ const cliDir = resolve8(fileURLToPath3(new URL(".", import.meta.url)));
3194
3268
  const pkgRoot = resolve8(cliDir, "..", "..");
3195
3269
  const pkgBridge = join5(pkgRoot, "bridge");
3196
3270
  const srcBridge = join5(pkgRoot, "..", "..", "bridge");
@@ -3295,7 +3369,9 @@ var CliRuntime = class {
3295
3369
  }
3296
3370
  const uiHost = FORCED_PUBLIC_UI_HOST;
3297
3371
  const uiPort = typeof state.uiPort === "number" && Number.isFinite(state.uiPort) ? state.uiPort : 18791;
3298
- console.log(`Applying changes (${reason}): restarting ${APP_NAME4} background service...`);
3372
+ console.log(
3373
+ `Applying changes (${reason}): restarting ${APP_NAME4} background service...`
3374
+ );
3299
3375
  await this.serviceCommands.stopService();
3300
3376
  await this.serviceCommands.startService({
3301
3377
  uiOverrides: {
@@ -3327,7 +3403,7 @@ var CliRuntime = class {
3327
3403
  }
3328
3404
  const uiPort = typeof state.uiPort === "number" && Number.isFinite(state.uiPort) ? state.uiPort : 18791;
3329
3405
  const delayMs = typeof params.delayMs === "number" && Number.isFinite(params.delayMs) ? Math.max(0, Math.floor(params.delayMs)) : 100;
3330
- const cliPath = process.env.NEXTCLAW_SELF_RELAUNCH_CLI?.trim() || fileURLToPath3(new URL("./index.js", import.meta.url));
3406
+ const cliPath = process.env.NEXTCLAW_SELF_RELAUNCH_CLI?.trim() || fileURLToPath4(new URL("./index.js", import.meta.url));
3331
3407
  const startArgs = [cliPath, "start", "--ui-port", String(uiPort)];
3332
3408
  const serviceStatePath = resolve9(getDataDir7(), "run", "service.json");
3333
3409
  const helperScript = [
@@ -3442,11 +3518,15 @@ var CliRuntime = class {
3442
3518
  }
3443
3519
  });
3444
3520
  } catch (error) {
3445
- console.warn(`Warning: failed to write restart sentinel from exec context: ${String(error)}`);
3521
+ console.warn(
3522
+ `Warning: failed to write restart sentinel from exec context: ${String(error)}`
3523
+ );
3446
3524
  }
3447
3525
  }
3448
3526
  async onboard() {
3449
- console.warn(`Warning: ${APP_NAME4} onboard is deprecated. Use "${APP_NAME4} init" instead.`);
3527
+ console.warn(
3528
+ `Warning: ${APP_NAME4} onboard is deprecated. Use "${APP_NAME4} init" instead.`
3529
+ );
3450
3530
  await this.init({ source: "onboard" });
3451
3531
  }
3452
3532
  async init(options = {}) {
@@ -3465,7 +3545,10 @@ var CliRuntime = class {
3465
3545
  const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ? join6(getDataDir7(), DEFAULT_WORKSPACE_DIR) : expandHome2(workspaceSetting);
3466
3546
  const workspaceExisted = existsSync8(workspacePath);
3467
3547
  mkdirSync5(workspacePath, { recursive: true });
3468
- const templateResult = this.workspaceManager.createWorkspaceTemplates(workspacePath, { force });
3548
+ const templateResult = this.workspaceManager.createWorkspaceTemplates(
3549
+ workspacePath,
3550
+ { force }
3551
+ );
3469
3552
  if (createdConfig) {
3470
3553
  console.log(`\u2713 ${prefix}: created config at ${configPath}`);
3471
3554
  }
@@ -3485,7 +3568,9 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
3485
3568
  console.log(` 1. Add your API key to ${configPath}`);
3486
3569
  console.log(` 2. Chat: ${APP_NAME4} agent -m "Hello!"`);
3487
3570
  } else {
3488
- console.log(`Tip: Run "${APP_NAME4} init${force ? " --force" : ""}" to re-run initialization if needed.`);
3571
+ console.log(
3572
+ `Tip: Run "${APP_NAME4} init${force ? " --force" : ""}" to re-run initialization if needed.`
3573
+ );
3489
3574
  }
3490
3575
  }
3491
3576
  async gateway(opts) {
@@ -3512,7 +3597,10 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
3512
3597
  if (opts.port) {
3513
3598
  uiOverrides.port = Number(opts.port);
3514
3599
  }
3515
- await this.serviceCommands.startGateway({ uiOverrides, allowMissingProvider: true });
3600
+ await this.serviceCommands.startGateway({
3601
+ uiOverrides,
3602
+ allowMissingProvider: true
3603
+ });
3516
3604
  }
3517
3605
  async start(opts) {
3518
3606
  await this.init({ source: "start", auto: true });
@@ -3566,74 +3654,103 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
3566
3654
  const pluginRegistry = loadPluginRegistry(config2, workspace);
3567
3655
  const extensionRegistry = toExtensionRegistry(pluginRegistry);
3568
3656
  logPluginDiagnostics(pluginRegistry);
3569
- const bus = new MessageBus2();
3570
- const provider = this.serviceCommands.createProvider(config2) ?? this.serviceCommands.createMissingProvider(config2);
3571
- const providerManager = new ProviderManager2({
3572
- defaultProvider: provider,
3573
- config: config2
3574
- });
3575
- const agentLoop = new AgentLoop2({
3576
- bus,
3577
- providerManager,
3578
- workspace,
3579
- model: config2.agents.defaults.model,
3580
- maxIterations: config2.agents.defaults.maxToolIterations,
3581
- maxTokens: config2.agents.defaults.maxTokens,
3582
- contextTokens: config2.agents.defaults.contextTokens,
3583
- braveApiKey: config2.tools.web.search.apiKey || void 0,
3584
- execConfig: config2.tools.exec,
3585
- restrictToWorkspace: config2.tools.restrictToWorkspace,
3586
- contextConfig: config2.agents.context,
3587
- config: config2,
3588
- extensionRegistry,
3589
- resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints2({
3590
- registry: pluginRegistry,
3591
- channel,
3592
- cfg: loadConfig6(),
3593
- accountId
3594
- })
3657
+ const pluginChannelBindings = getPluginChannelBindings3(pluginRegistry);
3658
+ setPluginRuntimeBridge2({
3659
+ loadConfig: () => toPluginConfigView(loadConfig6(), pluginChannelBindings),
3660
+ writeConfigFile: async (nextConfigView) => {
3661
+ if (!nextConfigView || typeof nextConfigView !== "object" || Array.isArray(nextConfigView)) {
3662
+ throw new Error(
3663
+ "plugin runtime writeConfigFile expects an object config"
3664
+ );
3665
+ }
3666
+ const current = loadConfig6();
3667
+ const next = mergePluginConfigView(
3668
+ current,
3669
+ nextConfigView,
3670
+ pluginChannelBindings
3671
+ );
3672
+ saveConfig5(next);
3673
+ }
3595
3674
  });
3596
- if (opts.message) {
3597
- const response = await agentLoop.processDirect({
3598
- content: opts.message,
3599
- sessionKey: opts.session ?? "cli:default",
3600
- channel: "cli",
3601
- chatId: "direct",
3602
- metadata: typeof opts.model === "string" && opts.model.trim() ? { model: opts.model.trim() } : {}
3675
+ try {
3676
+ const bus = new MessageBus2();
3677
+ const provider = this.serviceCommands.createProvider(config2) ?? this.serviceCommands.createMissingProvider(config2);
3678
+ const providerManager = new ProviderManager2({
3679
+ defaultProvider: provider,
3680
+ config: config2
3603
3681
  });
3604
- printAgentResponse(response);
3605
- return;
3606
- }
3607
- console.log(`${this.logo} Interactive mode (type exit or Ctrl+C to quit)
3608
- `);
3609
- const historyFile = join6(getDataDir7(), "history", "cli_history");
3610
- const historyDir = resolve9(historyFile, "..");
3611
- mkdirSync5(historyDir, { recursive: true });
3612
- const history = existsSync8(historyFile) ? readFileSync6(historyFile, "utf-8").split("\n").filter(Boolean) : [];
3613
- const rl = createInterface2({ input: process.stdin, output: process.stdout });
3614
- rl.on("close", () => {
3615
- const merged = history.concat(rl.history ?? []);
3616
- writeFileSync4(historyFile, merged.join("\n"));
3617
- process.exit(0);
3618
- });
3619
- let running = true;
3620
- while (running) {
3621
- const line = await prompt(rl, "You: ");
3622
- const trimmed = line.trim();
3623
- if (!trimmed) {
3624
- continue;
3625
- }
3626
- if (EXIT_COMMANDS.has(trimmed.toLowerCase())) {
3627
- rl.close();
3628
- running = false;
3629
- break;
3682
+ const agentLoop = new AgentLoop2({
3683
+ bus,
3684
+ providerManager,
3685
+ workspace,
3686
+ model: config2.agents.defaults.model,
3687
+ maxIterations: config2.agents.defaults.maxToolIterations,
3688
+ maxTokens: config2.agents.defaults.maxTokens,
3689
+ contextTokens: config2.agents.defaults.contextTokens,
3690
+ braveApiKey: config2.tools.web.search.apiKey || void 0,
3691
+ execConfig: config2.tools.exec,
3692
+ restrictToWorkspace: config2.tools.restrictToWorkspace,
3693
+ contextConfig: config2.agents.context,
3694
+ config: config2,
3695
+ extensionRegistry,
3696
+ resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints2({
3697
+ registry: pluginRegistry,
3698
+ channel,
3699
+ cfg: loadConfig6(),
3700
+ accountId
3701
+ })
3702
+ });
3703
+ if (opts.message) {
3704
+ const response = await agentLoop.processDirect({
3705
+ content: opts.message,
3706
+ sessionKey: opts.session ?? "cli:default",
3707
+ channel: "cli",
3708
+ chatId: "direct",
3709
+ metadata: typeof opts.model === "string" && opts.model.trim() ? { model: opts.model.trim() } : {}
3710
+ });
3711
+ printAgentResponse(response);
3712
+ return;
3630
3713
  }
3631
- const response = await agentLoop.processDirect({
3632
- content: trimmed,
3633
- sessionKey: opts.session ?? "cli:default",
3634
- metadata: typeof opts.model === "string" && opts.model.trim() ? { model: opts.model.trim() } : {}
3714
+ console.log(
3715
+ `${this.logo} Interactive mode (type exit or Ctrl+C to quit)
3716
+ `
3717
+ );
3718
+ const historyFile = join6(getDataDir7(), "history", "cli_history");
3719
+ const historyDir = resolve9(historyFile, "..");
3720
+ mkdirSync5(historyDir, { recursive: true });
3721
+ const history = existsSync8(historyFile) ? readFileSync6(historyFile, "utf-8").split("\n").filter(Boolean) : [];
3722
+ const rl = createInterface2({
3723
+ input: process.stdin,
3724
+ output: process.stdout
3635
3725
  });
3636
- printAgentResponse(response);
3726
+ rl.on("close", () => {
3727
+ const merged = history.concat(
3728
+ rl.history ?? []
3729
+ );
3730
+ writeFileSync4(historyFile, merged.join("\n"));
3731
+ process.exit(0);
3732
+ });
3733
+ let running = true;
3734
+ while (running) {
3735
+ const line = await prompt(rl, "You: ");
3736
+ const trimmed = line.trim();
3737
+ if (!trimmed) {
3738
+ continue;
3739
+ }
3740
+ if (EXIT_COMMANDS.has(trimmed.toLowerCase())) {
3741
+ rl.close();
3742
+ running = false;
3743
+ break;
3744
+ }
3745
+ const response = await agentLoop.processDirect({
3746
+ content: trimmed,
3747
+ sessionKey: opts.session ?? "cli:default",
3748
+ metadata: typeof opts.model === "string" && opts.model.trim() ? { model: opts.model.trim() } : {}
3749
+ });
3750
+ printAgentResponse(response);
3751
+ }
3752
+ } finally {
3753
+ setPluginRuntimeBridge2(null);
3637
3754
  }
3638
3755
  }
3639
3756
  async update(opts) {
@@ -3641,7 +3758,9 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
3641
3758
  if (opts.timeout !== void 0) {
3642
3759
  const parsed = Number(opts.timeout);
3643
3760
  if (!Number.isFinite(parsed) || parsed <= 0) {
3644
- console.error("Invalid --timeout value. Provide milliseconds (e.g. 1200000).");
3761
+ console.error(
3762
+ "Invalid --timeout value. Provide milliseconds (e.g. 1200000)."
3763
+ );
3645
3764
  process.exit(1);
3646
3765
  }
3647
3766
  timeoutMs = parsed;
@@ -3651,7 +3770,9 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
3651
3770
  const result = runSelfUpdate({ timeoutMs, cwd: process.cwd() });
3652
3771
  const printSteps = () => {
3653
3772
  for (const step of result.steps) {
3654
- console.log(`- ${step.cmd} ${step.args.join(" ")} (code ${step.code ?? "?"})`);
3773
+ console.log(
3774
+ `- ${step.cmd} ${step.args.join(" ")} (code ${step.code ?? "?"})`
3775
+ );
3655
3776
  if (step.stderr) {
3656
3777
  console.log(` stderr: ${step.stderr}`);
3657
3778
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nextclaw",
3
- "version": "0.6.34",
3
+ "version": "0.6.36",
4
4
  "description": "Lightweight personal AI assistant with CLI, multi-provider routing, and channel integrations.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -39,7 +39,7 @@
39
39
  "chokidar": "^3.6.0",
40
40
  "commander": "^12.1.0",
41
41
  "@nextclaw/core": "^0.6.27",
42
- "@nextclaw/server": "^0.4.16",
42
+ "@nextclaw/server": "^0.4.17",
43
43
  "@nextclaw/openclaw-compat": "^0.1.20"
44
44
  },
45
45
  "devDependencies": {