@nextclaw/server 0.10.28 → 0.10.29

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/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as NextclawCore from '@nextclaw/core';
2
2
  import { ThinkingLevel, CronService, Config, ConfigActionExecuteRequest as ConfigActionExecuteRequest$1, ConfigActionExecuteResult as ConfigActionExecuteResult$1 } from '@nextclaw/core';
3
+ import { PluginChannelBinding, PluginUiMetadata } from '@nextclaw/openclaw-compat';
3
4
  import { NcpAgentClientEndpoint, NcpSessionApi, NcpSessionSummary, NcpMessage } from '@nextclaw/ncp';
4
5
  import { NcpHttpAgentStreamProvider } from '@nextclaw/ncp-http-agent-server';
5
6
  import { IncomingMessage } from 'node:http';
@@ -332,6 +333,8 @@ type UiRouterOptions = {
332
333
  ncpAgent?: UiNcpAgent;
333
334
  authService?: UiAuthService;
334
335
  remoteAccess?: UiRemoteAccessHost;
336
+ getPluginChannelBindings?: () => PluginChannelBinding[];
337
+ getPluginUiMetadata?: () => PluginUiMetadata[];
335
338
  };
336
339
  type UiRemoteAccessHost = {
337
340
  getStatus: () => Promise<RemoteAccessView> | RemoteAccessView;
@@ -1118,6 +1121,8 @@ type UiServerOptions = {
1118
1121
  chatRuntime?: UiChatRuntime;
1119
1122
  ncpAgent?: UiNcpAgent;
1120
1123
  remoteAccess?: UiRemoteAccessHost;
1124
+ getPluginChannelBindings?: () => PluginChannelBinding[];
1125
+ getPluginUiMetadata?: () => PluginUiMetadata[];
1121
1126
  };
1122
1127
  type UiServerHandle = {
1123
1128
  host: string;
@@ -1130,6 +1135,11 @@ declare function startUiServer(options: UiServerOptions): UiServerHandle;
1130
1135
 
1131
1136
  declare function createUiRouter(options: UiRouterOptions): Hono;
1132
1137
 
1138
+ type PluginConfigProjectionOptions = {
1139
+ pluginChannelBindings?: PluginChannelBinding[];
1140
+ pluginUiMetadata?: PluginUiMetadata[];
1141
+ };
1142
+
1133
1143
  type ExecuteActionResult = {
1134
1144
  ok: true;
1135
1145
  data: ConfigActionExecuteResult$1;
@@ -1139,9 +1149,9 @@ type ExecuteActionResult = {
1139
1149
  message: string;
1140
1150
  details?: Record<string, unknown>;
1141
1151
  };
1142
- declare function buildConfigView(config: Config): ConfigView;
1143
- declare function buildConfigMeta(config: Config): ConfigMetaView;
1144
- declare function buildConfigSchemaView(_config: Config): ConfigSchemaResponse;
1152
+ declare function buildConfigView(config: Config, options?: PluginConfigProjectionOptions): ConfigView;
1153
+ declare function buildConfigMeta(config: Config, options?: PluginConfigProjectionOptions): ConfigMetaView;
1154
+ declare function buildConfigSchemaView(_config: Config, options?: PluginConfigProjectionOptions): ConfigSchemaResponse;
1145
1155
  declare function executeConfigAction(configPath: string, actionId: string, request: ConfigActionExecuteRequest$1): Promise<ExecuteActionResult>;
1146
1156
  declare function loadConfigOrDefault(configPath: string): Config;
1147
1157
  declare function updateModel(configPath: string, patch: {
@@ -1156,7 +1166,7 @@ declare function createCustomProvider(configPath: string, patch?: ProviderConfig
1156
1166
  };
1157
1167
  declare function deleteCustomProvider(configPath: string, providerName: string): boolean | null;
1158
1168
  declare function testProviderConnection(configPath: string, providerName: string, patch: ProviderConnectionTestRequest): Promise<ProviderConnectionTestResult | null>;
1159
- declare function updateChannel(configPath: string, channelName: string, patch: Record<string, unknown>): Record<string, unknown> | null;
1169
+ declare function updateChannel(configPath: string, channelName: string, patch: Record<string, unknown>, options?: PluginConfigProjectionOptions): Record<string, unknown> | null;
1160
1170
  declare const DEFAULT_SESSION_TYPE = "native";
1161
1171
  declare class SessionPatchValidationError extends Error {
1162
1172
  readonly code: "SESSION_TYPE_INVALID" | "SESSION_TYPE_IMMUTABLE" | "SESSION_TYPE_UNAVAILABLE" | "PREFERRED_THINKING_INVALID";
package/dist/index.js CHANGED
@@ -584,6 +584,113 @@ function createDefaultProviderConfig(defaultWireApi = "auto") {
584
584
  };
585
585
  }
586
586
 
587
+ // src/ui/plugin-channel-config.projection.ts
588
+ import { mergePluginConfigView, toPluginConfigView } from "@nextclaw/openclaw-compat";
589
+ var DOCS_BASE_URL = "https://docs.nextclaw.io";
590
+ var CHANNEL_TUTORIAL_URLS = {
591
+ feishu: {
592
+ default: `${DOCS_BASE_URL}/guide/tutorials/feishu`,
593
+ en: `${DOCS_BASE_URL}/en/guide/tutorials/feishu`,
594
+ zh: `${DOCS_BASE_URL}/zh/guide/tutorials/feishu`
595
+ },
596
+ weixin: {
597
+ default: "https://npmx.dev/package/@nextclaw/channel-plugin-weixin"
598
+ }
599
+ };
600
+ function normalizePluginProjectionOptions(options) {
601
+ return {
602
+ pluginChannelBindings: options?.pluginChannelBindings ?? [],
603
+ pluginUiMetadata: options?.pluginUiMetadata ?? []
604
+ };
605
+ }
606
+ function getProjectedConfigView(config, options) {
607
+ const normalized = normalizePluginProjectionOptions(options);
608
+ return toPluginConfigView(config, normalized.pluginChannelBindings);
609
+ }
610
+ function getProjectedChannelMap(config, options) {
611
+ const view = getProjectedConfigView(config, options);
612
+ const channels = view.channels;
613
+ if (!channels || typeof channels !== "object" || Array.isArray(channels)) {
614
+ return {};
615
+ }
616
+ return channels;
617
+ }
618
+ function getProjectedChannelConfig(config, channelName, options) {
619
+ const channel = getProjectedChannelMap(config, options)[channelName];
620
+ if (!channel || typeof channel !== "object" || Array.isArray(channel)) {
621
+ return null;
622
+ }
623
+ return channel;
624
+ }
625
+ function buildPluginChannelUiHints(options) {
626
+ const normalized = normalizePluginProjectionOptions(options);
627
+ if (normalized.pluginChannelBindings.length === 0) {
628
+ return {};
629
+ }
630
+ const hints = {};
631
+ const metadataById = new Map(normalized.pluginUiMetadata.map((item) => [item.id, item]));
632
+ for (const binding of normalized.pluginChannelBindings) {
633
+ const channelScope = `channels.${binding.channelId}`;
634
+ const channelMeta = binding.channel.meta;
635
+ const channelLabel = typeof channelMeta?.selectionLabel === "string" ? channelMeta.selectionLabel : typeof channelMeta?.label === "string" ? channelMeta.label : binding.channelId;
636
+ const channelHelp = typeof channelMeta?.blurb === "string" ? channelMeta.blurb : void 0;
637
+ hints[channelScope] = {
638
+ ...channelLabel ? { label: channelLabel } : {},
639
+ ...channelHelp ? { help: channelHelp } : {}
640
+ };
641
+ const pluginHints = metadataById.get(binding.pluginId)?.configUiHints ?? {};
642
+ for (const [key, hint] of Object.entries(pluginHints)) {
643
+ hints[`${channelScope}.${key}`] = {
644
+ label: hint.label,
645
+ help: hint.help,
646
+ advanced: hint.advanced,
647
+ sensitive: hint.sensitive,
648
+ placeholder: hint.placeholder
649
+ };
650
+ }
651
+ }
652
+ return hints;
653
+ }
654
+ function buildProjectedChannelMeta(config, options) {
655
+ const normalized = normalizePluginProjectionOptions(options);
656
+ const projectedChannelMap = getProjectedChannelMap(config, normalized);
657
+ const bindingByChannelId = new Map(normalized.pluginChannelBindings.map((binding) => [binding.channelId, binding]));
658
+ const channelNames = /* @__PURE__ */ new Set([
659
+ ...Object.keys(config.channels),
660
+ ...Object.keys(projectedChannelMap),
661
+ ...bindingByChannelId.keys()
662
+ ]);
663
+ return [...channelNames].map((name) => {
664
+ const tutorialUrls = CHANNEL_TUTORIAL_URLS[name];
665
+ const tutorialUrl = tutorialUrls?.default ?? tutorialUrls?.en ?? tutorialUrls?.zh;
666
+ const binding = bindingByChannelId.get(name);
667
+ const channelMeta = binding?.channel.meta;
668
+ const displayName = typeof channelMeta?.selectionLabel === "string" ? channelMeta.selectionLabel : typeof channelMeta?.label === "string" ? channelMeta.label : name;
669
+ return {
670
+ name,
671
+ displayName,
672
+ enabled: Boolean(projectedChannelMap[name]?.enabled),
673
+ tutorialUrl,
674
+ tutorialUrls
675
+ };
676
+ });
677
+ }
678
+ function mergeProjectedPluginChannelConfig(config, channelName, mergedChannel, options) {
679
+ const normalized = normalizePluginProjectionOptions(options);
680
+ if (!normalized.pluginChannelBindings.some((binding) => binding.channelId === channelName)) {
681
+ return null;
682
+ }
683
+ const currentView = getProjectedConfigView(config, normalized);
684
+ const nextView = {
685
+ ...currentView,
686
+ channels: {
687
+ ...currentView.channels ?? {},
688
+ [channelName]: mergedChannel
689
+ }
690
+ };
691
+ return mergePluginConfigView(config, nextView, normalized.pluginChannelBindings);
692
+ }
693
+
587
694
  // src/ui/provider-overrides.ts
588
695
  import { findBuiltinProviderByName, listBuiltinProviders } from "@nextclaw/runtime";
589
696
  var MINIMAX_PORTAL_PROVIDER_SPEC = {
@@ -804,15 +911,7 @@ function clearSecretRefsByPrefix(config, pathPrefix) {
804
911
  }
805
912
  }
806
913
  }
807
- var DOCS_BASE_URL = "https://docs.nextclaw.io";
808
914
  var BOCHA_OPEN_URL = "https://open.bocha.cn";
809
- var CHANNEL_TUTORIAL_URLS = {
810
- feishu: {
811
- default: `${DOCS_BASE_URL}/guide/tutorials/feishu`,
812
- en: `${DOCS_BASE_URL}/en/guide/tutorials/feishu`,
813
- zh: `${DOCS_BASE_URL}/zh/guide/tutorials/feishu`
814
- }
815
- };
816
915
  var SEARCH_PROVIDER_META = [
817
916
  {
818
917
  name: "bocha",
@@ -1015,8 +1114,8 @@ async function runFeishuVerifyAction(params) {
1015
1114
  var ACTION_HANDLERS = {
1016
1115
  "channels.feishu.verifyConnection": runFeishuVerifyAction
1017
1116
  };
1018
- function buildUiHints(config) {
1019
- return buildConfigSchemaView(config).uiHints;
1117
+ function buildUiHints(config, options) {
1118
+ return buildConfigSchemaView(config, options).uiHints;
1020
1119
  }
1021
1120
  function maskApiKey(value) {
1022
1121
  if (!value) {
@@ -1095,8 +1194,9 @@ function toProviderView(config, provider, providerName, uiHints, spec) {
1095
1194
  }
1096
1195
  return view;
1097
1196
  }
1098
- function buildConfigView(config) {
1099
- const uiHints = buildUiHints(config);
1197
+ function buildConfigView(config, options) {
1198
+ const uiHints = buildUiHints(config, options);
1199
+ const projectedChannels = getProjectedChannelMap(config, options);
1100
1200
  const providers = {};
1101
1201
  for (const [name, provider] of Object.entries(config.providers)) {
1102
1202
  const spec = findServerBuiltinProviderByName(name);
@@ -1106,11 +1206,7 @@ function buildConfigView(config) {
1106
1206
  agents: config.agents,
1107
1207
  providers,
1108
1208
  search: buildSearchView(config),
1109
- channels: sanitizePublicConfigValue(
1110
- config.channels,
1111
- "channels",
1112
- uiHints
1113
- ),
1209
+ channels: sanitizePublicConfigValue(projectedChannels, "channels", uiHints),
1114
1210
  bindings: sanitizePublicConfigValue(config.bindings, "bindings", uiHints),
1115
1211
  session: sanitizePublicConfigValue(config.session, "session", uiHints),
1116
1212
  tools: sanitizePublicConfigValue(config.tools, "tools", uiHints),
@@ -1163,7 +1259,7 @@ function clearSecretRef(config, path) {
1163
1259
  delete config.secrets.refs[path];
1164
1260
  }
1165
1261
  }
1166
- function buildConfigMeta(config) {
1262
+ function buildConfigMeta(config, options) {
1167
1263
  const configProviders = config.providers;
1168
1264
  const builtinProviders = BUILTIN_PROVIDERS.map((spec) => {
1169
1265
  const providerConfig = configProviders[spec.name];
@@ -1233,21 +1329,16 @@ function buildConfigMeta(config) {
1233
1329
  };
1234
1330
  });
1235
1331
  const providers = [...customProviders, ...builtinProviders];
1236
- const channels = Object.keys(config.channels).map((name) => {
1237
- const tutorialUrls = CHANNEL_TUTORIAL_URLS[name];
1238
- const tutorialUrl = tutorialUrls?.default ?? tutorialUrls?.en ?? tutorialUrls?.zh;
1239
- return {
1240
- name,
1241
- displayName: name,
1242
- enabled: Boolean(config.channels[name]?.enabled),
1243
- tutorialUrl,
1244
- tutorialUrls
1245
- };
1246
- });
1332
+ const channels = buildProjectedChannelMeta(config, options);
1247
1333
  return { providers, search: SEARCH_PROVIDER_META, channels };
1248
1334
  }
1249
- function buildConfigSchemaView(_config) {
1250
- return buildConfigSchema({ version: getPackageVersion() });
1335
+ function buildConfigSchemaView(_config, options) {
1336
+ const base = buildConfigSchema({ version: getPackageVersion() });
1337
+ const pluginUiHints = buildPluginChannelUiHints(options);
1338
+ if (Object.keys(pluginUiHints).length === 0) {
1339
+ return base;
1340
+ }
1341
+ return { ...base, uiHints: { ...base.uiHints, ...pluginUiHints } };
1251
1342
  }
1252
1343
  async function executeConfigAction(configPath, actionId, request) {
1253
1344
  const baseConfig = loadConfigOrDefault(configPath);
@@ -1580,9 +1671,10 @@ async function testProviderConnection(configPath, providerName, patch) {
1580
1671
  };
1581
1672
  }
1582
1673
  }
1583
- function updateChannel(configPath, channelName, patch) {
1674
+ function updateChannel(configPath, channelName, patch, options) {
1584
1675
  const config = loadConfigOrDefault(configPath);
1585
- const channel = config.channels[channelName];
1676
+ const normalizedOptions = normalizePluginProjectionOptions(options);
1677
+ const channel = getProjectedChannelConfig(config, channelName, normalizedOptions);
1586
1678
  if (!channel) {
1587
1679
  return null;
1588
1680
  }
@@ -1592,14 +1684,24 @@ function updateChannel(configPath, channelName, patch) {
1592
1684
  clearSecretRef(config, path);
1593
1685
  }
1594
1686
  }
1595
- config.channels[channelName] = { ...channel, ...patch };
1687
+ const mergedChannel = { ...channel, ...patch };
1688
+ const mergedPluginConfig = mergeProjectedPluginChannelConfig(config, channelName, mergedChannel, normalizedOptions);
1689
+ if (mergedPluginConfig) {
1690
+ const next2 = ConfigSchema2.parse(mergedPluginConfig);
1691
+ saveConfig2(next2, configPath);
1692
+ return sanitizePublicConfigValue(
1693
+ getProjectedChannelConfig(next2, channelName, normalizedOptions) ?? {},
1694
+ `channels.${channelName}`,
1695
+ buildUiHints(next2, normalizedOptions)
1696
+ );
1697
+ }
1698
+ config.channels[channelName] = mergedChannel;
1596
1699
  const next = ConfigSchema2.parse(config);
1597
1700
  saveConfig2(next, configPath);
1598
- const uiHints = buildUiHints(next);
1599
1701
  return sanitizePublicConfigValue(
1600
- next.channels[channelName],
1702
+ getProjectedChannelConfig(next, channelName, normalizedOptions) ?? {},
1601
1703
  `channels.${channelName}`,
1602
- uiHints
1704
+ buildUiHints(next, normalizedOptions)
1603
1705
  );
1604
1706
  }
1605
1707
  function normalizeSessionKey(value) {
@@ -3042,17 +3144,23 @@ var ConfigRoutesController = class {
3042
3144
  constructor(options) {
3043
3145
  this.options = options;
3044
3146
  }
3147
+ getPluginConfigOptions() {
3148
+ return {
3149
+ pluginChannelBindings: this.options.getPluginChannelBindings?.() ?? [],
3150
+ pluginUiMetadata: this.options.getPluginUiMetadata?.() ?? []
3151
+ };
3152
+ }
3045
3153
  getConfig = (c) => {
3046
3154
  const config = loadConfigOrDefault(this.options.configPath);
3047
- return c.json(ok(buildConfigView(config)));
3155
+ return c.json(ok(buildConfigView(config, this.getPluginConfigOptions())));
3048
3156
  };
3049
3157
  getConfigMeta = (c) => {
3050
3158
  const config = loadConfigOrDefault(this.options.configPath);
3051
- return c.json(ok(buildConfigMeta(config)));
3159
+ return c.json(ok(buildConfigMeta(config, this.getPluginConfigOptions())));
3052
3160
  };
3053
3161
  getConfigSchema = (c) => {
3054
3162
  const config = loadConfigOrDefault(this.options.configPath);
3055
- return c.json(ok(buildConfigSchemaView(config)));
3163
+ return c.json(ok(buildConfigSchemaView(config, this.getPluginConfigOptions())));
3056
3164
  };
3057
3165
  updateConfigModel = async (c) => {
3058
3166
  const body = await readJson(c.req.raw);
@@ -3211,7 +3319,7 @@ var ConfigRoutesController = class {
3211
3319
  if (!body.ok) {
3212
3320
  return c.json(err("INVALID_BODY", "invalid json body"), 400);
3213
3321
  }
3214
- const result = updateChannel(this.options.configPath, channel, body.data);
3322
+ const result = updateChannel(this.options.configPath, channel, body.data, this.getPluginConfigOptions());
3215
3323
  if (!result) {
3216
3324
  return c.json(err("NOT_FOUND", `unknown channel: ${channel}`), 404);
3217
3325
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/server",
3
- "version": "0.10.28",
3
+ "version": "0.10.29",
4
4
  "private": false,
5
5
  "description": "Nextclaw UI/API server.",
6
6
  "type": "module",
@@ -14,16 +14,23 @@
14
14
  "files": [
15
15
  "dist"
16
16
  ],
17
+ "scripts": {
18
+ "build": "tsup src/index.ts --format esm --dts --out-dir dist",
19
+ "prepack": "pnpm run build",
20
+ "lint": "eslint .",
21
+ "tsc": "tsc -p tsconfig.json",
22
+ "test": "vitest"
23
+ },
17
24
  "dependencies": {
18
25
  "@hono/node-server": "^1.13.3",
26
+ "@nextclaw/mcp": "workspace:*",
27
+ "@nextclaw/ncp": "workspace:*",
28
+ "@nextclaw/ncp-http-agent-server": "workspace:*",
29
+ "@nextclaw/openclaw-compat": "workspace:*",
30
+ "@nextclaw/runtime": "workspace:*",
19
31
  "hono": "^4.6.2",
20
32
  "ws": "^8.18.0",
21
- "@nextclaw/ncp": "0.3.1",
22
- "@nextclaw/ncp-http-agent-server": "0.3.1",
23
- "@nextclaw/core": "0.9.11",
24
- "@nextclaw/mcp": "0.1.28",
25
- "@nextclaw/openclaw-compat": "0.3.14",
26
- "@nextclaw/runtime": "0.2.11"
33
+ "@nextclaw/core": "workspace:*"
27
34
  },
28
35
  "devDependencies": {
29
36
  "@types/node": "^20.17.6",
@@ -33,11 +40,5 @@
33
40
  "tsx": "^4.19.2",
34
41
  "typescript": "^5.6.3",
35
42
  "vitest": "^2.1.2"
36
- },
37
- "scripts": {
38
- "build": "tsup src/index.ts --format esm --dts --out-dir dist",
39
- "lint": "eslint .",
40
- "tsc": "tsc -p tsconfig.json",
41
- "test": "vitest"
42
43
  }
43
- }
44
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 NextClaw contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.