@nextclaw/server 0.6.8 → 0.6.10

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
@@ -54,6 +54,48 @@ type ProviderConnectionTestResult = {
54
54
  latencyMs: number;
55
55
  message: string;
56
56
  };
57
+ type SearchProviderName = "bocha" | "brave";
58
+ type BochaFreshnessValue = "noLimit" | "oneDay" | "oneWeek" | "oneMonth" | "oneYear" | string;
59
+ type SearchProviderConfigView = {
60
+ enabled: boolean;
61
+ apiKeySet: boolean;
62
+ apiKeyMasked?: string;
63
+ baseUrl: string;
64
+ docsUrl?: string;
65
+ summary?: boolean;
66
+ freshness?: BochaFreshnessValue;
67
+ };
68
+ type SearchConfigView = {
69
+ provider: SearchProviderName;
70
+ enabledProviders: SearchProviderName[];
71
+ defaults: {
72
+ maxResults: number;
73
+ };
74
+ providers: {
75
+ bocha: SearchProviderConfigView;
76
+ brave: SearchProviderConfigView;
77
+ };
78
+ };
79
+ type SearchConfigUpdate = {
80
+ provider?: SearchProviderName;
81
+ enabledProviders?: SearchProviderName[];
82
+ defaults?: {
83
+ maxResults?: number;
84
+ };
85
+ providers?: {
86
+ bocha?: {
87
+ apiKey?: string | null;
88
+ baseUrl?: string | null;
89
+ docsUrl?: string | null;
90
+ summary?: boolean;
91
+ freshness?: BochaFreshnessValue | null;
92
+ };
93
+ brave?: {
94
+ apiKey?: string | null;
95
+ baseUrl?: string | null;
96
+ };
97
+ };
98
+ };
57
99
  type ProviderAuthStartResult = {
58
100
  provider: string;
59
101
  kind: "device_code";
@@ -405,6 +447,7 @@ type ConfigView = {
405
447
  };
406
448
  };
407
449
  providers: Record<string, ProviderConfigView>;
450
+ search: SearchConfigView;
408
451
  channels: Record<string, Record<string, unknown>>;
409
452
  bindings?: AgentBindingView[];
410
453
  session?: SessionConfigView;
@@ -465,8 +508,17 @@ type ChannelSpecView = {
465
508
  zh?: string;
466
509
  };
467
510
  };
511
+ type SearchProviderSpecView = {
512
+ name: SearchProviderName;
513
+ displayName: string;
514
+ description: string;
515
+ docsUrl?: string;
516
+ isDefault?: boolean;
517
+ supportsSummary?: boolean;
518
+ };
468
519
  type ConfigMetaView = {
469
520
  providers: ProviderSpecView[];
521
+ search: SearchProviderSpecView[];
470
522
  channels: ChannelSpecView[];
471
523
  };
472
524
  type ConfigUiHint = {
@@ -787,6 +839,7 @@ declare function loadConfigOrDefault(configPath: string): Config;
787
839
  declare function updateModel(configPath: string, patch: {
788
840
  model?: string;
789
841
  }): ConfigView;
842
+ declare function updateSearch(configPath: string, patch: SearchConfigUpdate): ConfigView["search"];
790
843
  declare function updateProvider(configPath: string, providerName: string, patch: ProviderConfigUpdate): ProviderConfigView | null;
791
844
  declare function createCustomProvider(configPath: string, patch?: ProviderConfigUpdate): {
792
845
  name: string;
@@ -813,4 +866,4 @@ declare function deleteSession(configPath: string, key: string): boolean;
813
866
  declare function updateRuntime(configPath: string, patch: RuntimeConfigUpdate): Pick<ConfigView, "agents" | "bindings" | "session">;
814
867
  declare function updateSecrets(configPath: string, patch: SecretsConfigUpdate): SecretsView;
815
868
 
816
- export { type AgentBindingView, type AgentProfileView, type ApiError, type ApiResponse, type AppMetaView, type BindingPeerView, type ChannelSpecView, type ChatCapabilitiesView, type ChatCommandOptionView, type ChatCommandView, type ChatCommandsView, type ChatRunListView, type ChatRunState, type ChatRunView, type ChatSessionTypeOptionView, type ChatSessionTypesView, type ChatTurnRequest, type ChatTurnResult, type ChatTurnStopRequest, type ChatTurnStopResult, type ChatTurnStreamEvent, type ChatTurnView, type ConfigActionExecuteRequest, type ConfigActionExecuteResult, type ConfigActionManifest, type ConfigActionType, type ConfigMetaView, type ConfigSchemaResponse, type ConfigUiHint, type ConfigUiHints, type ConfigView, type CronActionResult, type CronEnableRequest, type CronJobStateView, type CronJobView, type CronListView, type CronPayloadView, type CronRunRequest, type CronScheduleView, DEFAULT_SESSION_TYPE, type MarketplaceApiConfig, type MarketplaceInstallKind, type MarketplaceInstallSkillParams, type MarketplaceInstallSpec, type MarketplaceInstalledRecord, type MarketplaceInstalledView, type MarketplaceInstaller, type MarketplaceItemSummary, type MarketplaceItemType, type MarketplaceItemView, type MarketplaceListView, type MarketplaceLocalizedTextMap, type MarketplacePluginContentView, type MarketplacePluginInstallKind, type MarketplacePluginInstallRequest, type MarketplacePluginInstallResult, type MarketplacePluginManageAction, type MarketplacePluginManageRequest, type MarketplacePluginManageResult, type MarketplaceRecommendationView, type MarketplaceSkillContentView, type MarketplaceSkillInstallKind, type MarketplaceSkillInstallRequest, type MarketplaceSkillInstallResult, type MarketplaceSkillManageAction, type MarketplaceSkillManageRequest, type MarketplaceSkillManageResult, type MarketplaceSort, type ProviderAuthImportResult, type ProviderAuthPollRequest, type ProviderAuthPollResult, type ProviderAuthStartRequest, type ProviderAuthStartResult, type ProviderConfigUpdate, type ProviderConfigView, type ProviderConnectionTestRequest, type ProviderConnectionTestResult, type ProviderCreateRequest, type ProviderCreateResult, type ProviderDeleteResult, type ProviderSpecView, type RuntimeConfigUpdate, type SecretProviderEnvView, type SecretProviderExecView, type SecretProviderFileView, type SecretProviderView, type SecretRefView, type SecretSourceView, type SecretsConfigUpdate, type SecretsView, type SessionConfigView, type SessionEntryView, type SessionEventView, type SessionHistoryView, type SessionMessageView, type SessionPatchUpdate, SessionPatchValidationError, type SessionsListView, type UiChatRuntime, type UiServerEvent, type UiServerHandle, type UiServerOptions, buildConfigMeta, buildConfigSchemaView, buildConfigView, createCustomProvider, createUiRouter, deleteCustomProvider, deleteSession, executeConfigAction, getSessionHistory, listSessions, loadConfigOrDefault, patchSession, startUiServer, testProviderConnection, updateChannel, updateModel, updateProvider, updateRuntime, updateSecrets };
869
+ export { type AgentBindingView, type AgentProfileView, type ApiError, type ApiResponse, type AppMetaView, type BindingPeerView, type BochaFreshnessValue, type ChannelSpecView, type ChatCapabilitiesView, type ChatCommandOptionView, type ChatCommandView, type ChatCommandsView, type ChatRunListView, type ChatRunState, type ChatRunView, type ChatSessionTypeOptionView, type ChatSessionTypesView, type ChatTurnRequest, type ChatTurnResult, type ChatTurnStopRequest, type ChatTurnStopResult, type ChatTurnStreamEvent, type ChatTurnView, type ConfigActionExecuteRequest, type ConfigActionExecuteResult, type ConfigActionManifest, type ConfigActionType, type ConfigMetaView, type ConfigSchemaResponse, type ConfigUiHint, type ConfigUiHints, type ConfigView, type CronActionResult, type CronEnableRequest, type CronJobStateView, type CronJobView, type CronListView, type CronPayloadView, type CronRunRequest, type CronScheduleView, DEFAULT_SESSION_TYPE, type MarketplaceApiConfig, type MarketplaceInstallKind, type MarketplaceInstallSkillParams, type MarketplaceInstallSpec, type MarketplaceInstalledRecord, type MarketplaceInstalledView, type MarketplaceInstaller, type MarketplaceItemSummary, type MarketplaceItemType, type MarketplaceItemView, type MarketplaceListView, type MarketplaceLocalizedTextMap, type MarketplacePluginContentView, type MarketplacePluginInstallKind, type MarketplacePluginInstallRequest, type MarketplacePluginInstallResult, type MarketplacePluginManageAction, type MarketplacePluginManageRequest, type MarketplacePluginManageResult, type MarketplaceRecommendationView, type MarketplaceSkillContentView, type MarketplaceSkillInstallKind, type MarketplaceSkillInstallRequest, type MarketplaceSkillInstallResult, type MarketplaceSkillManageAction, type MarketplaceSkillManageRequest, type MarketplaceSkillManageResult, type MarketplaceSort, type ProviderAuthImportResult, type ProviderAuthPollRequest, type ProviderAuthPollResult, type ProviderAuthStartRequest, type ProviderAuthStartResult, type ProviderConfigUpdate, type ProviderConfigView, type ProviderConnectionTestRequest, type ProviderConnectionTestResult, type ProviderCreateRequest, type ProviderCreateResult, type ProviderDeleteResult, type ProviderSpecView, type RuntimeConfigUpdate, type SearchConfigUpdate, type SearchConfigView, type SearchProviderConfigView, type SearchProviderName, type SearchProviderSpecView, type SecretProviderEnvView, type SecretProviderExecView, type SecretProviderFileView, type SecretProviderView, type SecretRefView, type SecretSourceView, type SecretsConfigUpdate, type SecretsView, type SessionConfigView, type SessionEntryView, type SessionEventView, type SessionHistoryView, type SessionMessageView, type SessionPatchUpdate, SessionPatchValidationError, type SessionsListView, type UiChatRuntime, type UiServerEvent, type UiServerHandle, type UiServerOptions, buildConfigMeta, buildConfigSchemaView, buildConfigView, createCustomProvider, createUiRouter, deleteCustomProvider, deleteSession, executeConfigAction, getSessionHistory, listSessions, loadConfigOrDefault, patchSession, startUiServer, testProviderConnection, updateChannel, updateModel, updateProvider, updateRuntime, updateSearch, updateSecrets };
package/dist/index.js CHANGED
@@ -212,6 +212,7 @@ function clearSecretRefsByPrefix(config, pathPrefix) {
212
212
  }
213
213
  }
214
214
  var DOCS_BASE_URL = "https://docs.nextclaw.io";
215
+ var BOCHA_OPEN_URL = "https://open.bocha.cn";
215
216
  var CHANNEL_TUTORIAL_URLS = {
216
217
  feishu: {
217
218
  default: `${DOCS_BASE_URL}/guide/tutorials/feishu`,
@@ -219,6 +220,22 @@ var CHANNEL_TUTORIAL_URLS = {
219
220
  zh: `${DOCS_BASE_URL}/zh/guide/tutorials/feishu`
220
221
  }
221
222
  };
223
+ var SEARCH_PROVIDER_META = [
224
+ {
225
+ name: "bocha",
226
+ displayName: "Bocha Search",
227
+ description: "China-friendly web search with AI-ready summaries.",
228
+ docsUrl: BOCHA_OPEN_URL,
229
+ isDefault: true,
230
+ supportsSummary: true
231
+ },
232
+ {
233
+ name: "brave",
234
+ displayName: "Brave Search",
235
+ description: "Brave web search API kept as an optional provider.",
236
+ supportsSummary: false
237
+ }
238
+ ];
222
239
  function matchesExtraSensitivePath(path) {
223
240
  if (path === "session" || path.startsWith("session.")) {
224
241
  return false;
@@ -470,6 +487,7 @@ function buildConfigView(config) {
470
487
  return {
471
488
  agents: config.agents,
472
489
  providers,
490
+ search: buildSearchView(config),
473
491
  channels: sanitizePublicConfigValue(
474
492
  config.channels,
475
493
  "channels",
@@ -488,6 +506,40 @@ function buildConfigView(config) {
488
506
  }
489
507
  };
490
508
  }
509
+ function toSearchProviderView(config, providerName, provider) {
510
+ const apiKeyPath = `search.providers.${providerName}.apiKey`;
511
+ const apiKeyRefSet = hasSecretRef(config, apiKeyPath);
512
+ const masked = maskApiKey(provider.apiKey);
513
+ const base = {
514
+ enabled: config.search.enabledProviders.includes(providerName),
515
+ apiKeySet: masked.apiKeySet || apiKeyRefSet,
516
+ apiKeyMasked: masked.apiKeyMasked ?? (apiKeyRefSet ? "****" : void 0),
517
+ baseUrl: provider.baseUrl
518
+ };
519
+ if ("docsUrl" in provider) {
520
+ base.docsUrl = provider.docsUrl;
521
+ }
522
+ if ("summary" in provider) {
523
+ base.summary = provider.summary;
524
+ }
525
+ if ("freshness" in provider) {
526
+ base.freshness = provider.freshness;
527
+ }
528
+ return base;
529
+ }
530
+ function buildSearchView(config) {
531
+ return {
532
+ provider: config.search.provider,
533
+ enabledProviders: [...config.search.enabledProviders],
534
+ defaults: {
535
+ maxResults: config.search.defaults.maxResults
536
+ },
537
+ providers: {
538
+ bocha: toSearchProviderView(config, "bocha", config.search.providers.bocha),
539
+ brave: toSearchProviderView(config, "brave", config.search.providers.brave)
540
+ }
541
+ };
542
+ }
491
543
  function clearSecretRef(config, path) {
492
544
  if (config.secrets.refs[path]) {
493
545
  delete config.secrets.refs[path];
@@ -574,7 +626,7 @@ function buildConfigMeta(config) {
574
626
  tutorialUrls
575
627
  };
576
628
  });
577
- return { providers, channels };
629
+ return { providers, search: SEARCH_PROVIDER_META, channels };
578
630
  }
579
631
  function buildConfigSchemaView(_config) {
580
632
  return buildConfigSchema({ version: getPackageVersion() });
@@ -643,6 +695,60 @@ function updateModel(configPath, patch) {
643
695
  saveConfig(next, configPath);
644
696
  return buildConfigView(next);
645
697
  }
698
+ function updateSearch(configPath, patch) {
699
+ const config = loadConfigOrDefault(configPath);
700
+ if (patch.provider === "bocha" || patch.provider === "brave") {
701
+ config.search.provider = patch.provider;
702
+ }
703
+ if (Array.isArray(patch.enabledProviders)) {
704
+ config.search.enabledProviders = Array.from(new Set(
705
+ patch.enabledProviders.filter((value) => value === "bocha" || value === "brave")
706
+ ));
707
+ }
708
+ if (patch.defaults && Object.prototype.hasOwnProperty.call(patch.defaults, "maxResults")) {
709
+ const nextMaxResults = patch.defaults.maxResults;
710
+ if (typeof nextMaxResults === "number" && Number.isFinite(nextMaxResults)) {
711
+ config.search.defaults.maxResults = Math.max(1, Math.min(50, Math.trunc(nextMaxResults)));
712
+ }
713
+ }
714
+ const bochaPatch = patch.providers?.bocha;
715
+ if (bochaPatch) {
716
+ if (Object.prototype.hasOwnProperty.call(bochaPatch, "apiKey")) {
717
+ config.search.providers.bocha.apiKey = bochaPatch.apiKey ?? "";
718
+ clearSecretRef(config, "search.providers.bocha.apiKey");
719
+ }
720
+ if (Object.prototype.hasOwnProperty.call(bochaPatch, "baseUrl")) {
721
+ config.search.providers.bocha.baseUrl = normalizeOptionalString(bochaPatch.baseUrl) ?? "https://api.bocha.cn/v1/web-search";
722
+ }
723
+ if (Object.prototype.hasOwnProperty.call(bochaPatch, "docsUrl")) {
724
+ config.search.providers.bocha.docsUrl = normalizeOptionalString(bochaPatch.docsUrl) ?? BOCHA_OPEN_URL;
725
+ }
726
+ if (Object.prototype.hasOwnProperty.call(bochaPatch, "summary")) {
727
+ config.search.providers.bocha.summary = Boolean(bochaPatch.summary);
728
+ }
729
+ if (Object.prototype.hasOwnProperty.call(bochaPatch, "freshness")) {
730
+ const freshness = normalizeOptionalString(bochaPatch.freshness);
731
+ if (freshness === "noLimit" || freshness === "oneDay" || freshness === "oneWeek" || freshness === "oneMonth" || freshness === "oneYear") {
732
+ config.search.providers.bocha.freshness = freshness;
733
+ } else {
734
+ config.search.providers.bocha.freshness = "noLimit";
735
+ }
736
+ }
737
+ }
738
+ const bravePatch = patch.providers?.brave;
739
+ if (bravePatch) {
740
+ if (Object.prototype.hasOwnProperty.call(bravePatch, "apiKey")) {
741
+ config.search.providers.brave.apiKey = bravePatch.apiKey ?? "";
742
+ clearSecretRef(config, "search.providers.brave.apiKey");
743
+ }
744
+ if (Object.prototype.hasOwnProperty.call(bravePatch, "baseUrl")) {
745
+ config.search.providers.brave.baseUrl = normalizeOptionalString(bravePatch.baseUrl) ?? "https://api.search.brave.com/res/v1/web/search";
746
+ }
747
+ }
748
+ const next = ConfigSchema.parse(config);
749
+ saveConfig(next, configPath);
750
+ return buildSearchView(next);
751
+ }
646
752
  function updateProvider(configPath, providerName, patch) {
647
753
  const config = loadConfigOrDefault(configPath);
648
754
  const provider = ensureProviderConfig(config, providerName);
@@ -1921,6 +2027,17 @@ function readNonEmptyString(value) {
1921
2027
  const trimmed = value.trim();
1922
2028
  return trimmed || void 0;
1923
2029
  }
2030
+ function formatUserFacingError(error, maxChars = 320) {
2031
+ const raw = error instanceof Error ? error.message || error.name || "Unknown error" : String(error ?? "Unknown error");
2032
+ const normalized = raw.replace(/\s+/g, " ").trim();
2033
+ if (!normalized) {
2034
+ return "Unknown error";
2035
+ }
2036
+ if (normalized.length <= maxChars) {
2037
+ return normalized;
2038
+ }
2039
+ return `${normalized.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
2040
+ }
1924
2041
  function normalizeSessionType2(value) {
1925
2042
  return readNonEmptyString(value)?.toLowerCase();
1926
2043
  }
@@ -3026,6 +3143,15 @@ function createUiRouter(options) {
3026
3143
  model: view.agents.defaults.model
3027
3144
  }));
3028
3145
  });
3146
+ app.put("/api/config/search", async (c) => {
3147
+ const body = await readJson(c.req.raw);
3148
+ if (!body.ok) {
3149
+ return c.json(err("INVALID_BODY", "invalid json body"), 400);
3150
+ }
3151
+ const result = updateSearch(options.configPath, body.data);
3152
+ options.publish({ type: "config.updated", payload: { path: "search" } });
3153
+ return c.json(ok(result));
3154
+ });
3029
3155
  app.put("/api/config/providers/:provider", async (c) => {
3030
3156
  const provider = c.req.param("provider");
3031
3157
  const body = await readJson(c.req.raw);
@@ -3256,7 +3382,7 @@ function createUiRouter(options) {
3256
3382
  options.publish({ type: "config.updated", payload: { path: "session" } });
3257
3383
  return c.json(ok(response));
3258
3384
  } catch (error) {
3259
- return c.json(err("CHAT_TURN_FAILED", String(error)), 500);
3385
+ return c.json(err("CHAT_TURN_FAILED", formatUserFacingError(error)), 500);
3260
3386
  }
3261
3387
  });
3262
3388
  app.post("/api/chat/turn/stop", async (c) => {
@@ -3334,7 +3460,7 @@ function createUiRouter(options) {
3334
3460
  try {
3335
3461
  managedRun = await chatRuntime.startTurnRun(request);
3336
3462
  } catch (error) {
3337
- return c.json(err("CHAT_TURN_FAILED", String(error)), 500);
3463
+ return c.json(err("CHAT_TURN_FAILED", formatUserFacingError(error)), 500);
3338
3464
  }
3339
3465
  if (readNonEmptyString(managedRun.runId)) {
3340
3466
  runId = readNonEmptyString(managedRun.runId);
@@ -3396,7 +3522,7 @@ function createUiRouter(options) {
3396
3522
  if (typed.type === "error") {
3397
3523
  push("error", {
3398
3524
  code: "CHAT_TURN_FAILED",
3399
- message: typed.error
3525
+ message: formatUserFacingError(typed.error)
3400
3526
  });
3401
3527
  return;
3402
3528
  }
@@ -3457,7 +3583,7 @@ function createUiRouter(options) {
3457
3583
  if (typed.type === "error") {
3458
3584
  push("error", {
3459
3585
  code: "CHAT_TURN_FAILED",
3460
- message: typed.error
3586
+ message: formatUserFacingError(typed.error)
3461
3587
  });
3462
3588
  return;
3463
3589
  }
@@ -3473,7 +3599,7 @@ function createUiRouter(options) {
3473
3599
  } catch (error) {
3474
3600
  push("error", {
3475
3601
  code: "CHAT_TURN_FAILED",
3476
- message: String(error)
3602
+ message: formatUserFacingError(error)
3477
3603
  });
3478
3604
  } finally {
3479
3605
  controller.close();
@@ -3599,7 +3725,7 @@ function createUiRouter(options) {
3599
3725
  if (typed.type === "error") {
3600
3726
  push("error", {
3601
3727
  code: "CHAT_TURN_FAILED",
3602
- message: typed.error
3728
+ message: formatUserFacingError(typed.error)
3603
3729
  });
3604
3730
  return;
3605
3731
  }
@@ -3609,7 +3735,7 @@ function createUiRouter(options) {
3609
3735
  if (latestRun?.state === "failed") {
3610
3736
  push("error", {
3611
3737
  code: "CHAT_TURN_FAILED",
3612
- message: latestRun.error ?? "chat run failed"
3738
+ message: formatUserFacingError(latestRun.error ?? "chat run failed")
3613
3739
  });
3614
3740
  return;
3615
3741
  }
@@ -3618,7 +3744,7 @@ function createUiRouter(options) {
3618
3744
  } catch (error) {
3619
3745
  push("error", {
3620
3746
  code: "CHAT_TURN_FAILED",
3621
- message: String(error)
3747
+ message: formatUserFacingError(error)
3622
3748
  });
3623
3749
  } finally {
3624
3750
  controller.close();
@@ -3906,5 +4032,6 @@ export {
3906
4032
  updateModel,
3907
4033
  updateProvider,
3908
4034
  updateRuntime,
4035
+ updateSearch,
3909
4036
  updateSecrets
3910
4037
  };
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@nextclaw/server",
3
- "version": "0.6.8",
3
+ "version": "0.6.10",
4
4
  "private": false,
5
5
  "description": "Nextclaw UI/API server.",
6
6
  "type": "module",
7
7
  "exports": {
8
8
  ".": {
9
+ "development": "./src/index.ts",
9
10
  "types": "./dist/index.d.ts",
10
11
  "default": "./dist/index.js"
11
12
  }
@@ -17,9 +18,9 @@
17
18
  "@hono/node-server": "^1.13.3",
18
19
  "hono": "^4.6.2",
19
20
  "ws": "^8.18.0",
20
- "@nextclaw/openclaw-compat": "0.2.3",
21
- "@nextclaw/core": "0.7.4",
22
- "@nextclaw/runtime": "0.1.3"
21
+ "@nextclaw/openclaw-compat": "0.2.5",
22
+ "@nextclaw/runtime": "0.1.5",
23
+ "@nextclaw/core": "0.7.6"
23
24
  },
24
25
  "devDependencies": {
25
26
  "@types/node": "^20.17.6",