@nextclaw/server 0.12.12 → 0.12.13-beta.1

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
@@ -4,6 +4,7 @@ import { Config, ConfigActionExecuteRequest as ConfigActionExecuteRequest$1, Con
4
4
  import { NcpHttpAgentStreamProvider } from "@nextclaw/ncp-http-agent-server";
5
5
  import { PluginChannelBinding, PluginUiMetadata } from "@nextclaw/openclaw-compat";
6
6
  import { NcpAgentClientEndpoint, NcpMessage, NcpSessionApi, NcpSessionStatus, NcpSessionSummary } from "@nextclaw/ncp";
7
+ import { UpdatePreferences, UpdateSnapshot } from "@nextclaw/kernel/update-contract";
7
8
  import { IncomingMessage } from "node:http";
8
9
 
9
10
  //#region src/ui/ncp-attachment.types.d.ts
@@ -191,6 +192,7 @@ type MarketplaceMcpInstallRequest = {
191
192
  inputs?: Record<string, string>;
192
193
  template?: MarketplaceMcpInstallSpec;
193
194
  };
195
+ type MarketplaceInstallRequest = MarketplacePluginInstallRequest | MarketplaceSkillInstallRequest | MarketplaceMcpInstallRequest;
194
196
  type MarketplacePluginInstallResult = {
195
197
  type: "plugin";
196
198
  spec: string;
@@ -210,9 +212,11 @@ type MarketplaceMcpInstallResult = {
210
212
  message: string;
211
213
  output?: string;
212
214
  };
215
+ type MarketplaceInstallResult = MarketplacePluginInstallResult | MarketplaceSkillInstallResult | MarketplaceMcpInstallResult;
213
216
  type MarketplacePluginManageAction = "enable" | "disable" | "uninstall";
214
217
  type MarketplaceSkillManageAction = "uninstall";
215
218
  type MarketplaceMcpManageAction = "enable" | "disable" | "remove";
219
+ type MarketplaceManageAction = MarketplacePluginManageAction | MarketplaceSkillManageAction | MarketplaceMcpManageAction;
216
220
  type MarketplacePluginManageRequest = {
217
221
  type?: "plugin";
218
222
  action: MarketplacePluginManageAction;
@@ -231,6 +235,7 @@ type MarketplaceMcpManageRequest = {
231
235
  id?: string;
232
236
  spec?: string;
233
237
  };
238
+ type MarketplaceManageRequest = MarketplacePluginManageRequest | MarketplaceSkillManageRequest | MarketplaceMcpManageRequest;
234
239
  type MarketplacePluginManageResult = {
235
240
  type: "plugin";
236
241
  action: MarketplacePluginManageAction;
@@ -252,6 +257,7 @@ type MarketplaceMcpManageResult = {
252
257
  message: string;
253
258
  output?: string;
254
259
  };
260
+ type MarketplaceManageResult = MarketplacePluginManageResult | MarketplaceSkillManageResult | MarketplaceMcpManageResult;
255
261
  type MarketplaceMcpDoctorResult = {
256
262
  name: string;
257
263
  enabled: boolean;
@@ -401,6 +407,7 @@ type UiRouterOptions = {
401
407
  authService?: UiAuthService;
402
408
  remoteAccess?: UiRemoteAccessHost;
403
409
  runtimeControl?: UiRuntimeControlHost;
410
+ runtimeUpdate?: UiRuntimeUpdateHost;
404
411
  getBootstrapStatus?: () => BootstrapStatusView;
405
412
  getPluginChannelBindings?: () => PluginChannelBinding[];
406
413
  getPluginUiMetadata?: () => PluginUiMetadata[];
@@ -422,6 +429,14 @@ type UiRuntimeControlHost = {
422
429
  restartService: () => Promise<RuntimeControlActionResult> | RuntimeControlActionResult;
423
430
  stopService: () => Promise<RuntimeControlActionResult> | RuntimeControlActionResult;
424
431
  };
432
+ type UiRuntimeUpdateHost = {
433
+ getState: () => Promise<UpdateSnapshot> | UpdateSnapshot;
434
+ checkForUpdates: () => Promise<UpdateSnapshot> | UpdateSnapshot;
435
+ downloadUpdate: () => Promise<UpdateSnapshot> | UpdateSnapshot;
436
+ applyDownloadedUpdate: () => Promise<UpdateSnapshot> | UpdateSnapshot;
437
+ updatePreferences: (preferences: Partial<UpdatePreferences>) => Promise<UpdateSnapshot> | UpdateSnapshot;
438
+ updateChannel: (channel: UpdateSnapshot["channel"]) => Promise<UpdateSnapshot> | UpdateSnapshot;
439
+ };
425
440
  //#endregion
426
441
  //#region src/ui/chat-session-type.types.d.ts
427
442
  type ChatSessionTypeIconView = {
@@ -989,6 +1004,9 @@ type CronActionResult = {
989
1004
  executed?: boolean;
990
1005
  };
991
1006
  type RuntimeConfigUpdate = {
1007
+ companion?: {
1008
+ enabled?: boolean;
1009
+ };
992
1010
  agents?: {
993
1011
  defaults?: {
994
1012
  contextTokens?: number;
@@ -1077,6 +1095,9 @@ type UiNcpAgent = {
1077
1095
  basePath?: string;
1078
1096
  };
1079
1097
  type ConfigView = {
1098
+ companion?: {
1099
+ enabled?: boolean;
1100
+ };
1080
1101
  agents: {
1081
1102
  defaults: {
1082
1103
  model: string;
@@ -1094,7 +1115,6 @@ type ConfigView = {
1094
1115
  bootstrap?: {
1095
1116
  files?: string[];
1096
1117
  minimalFiles?: string[];
1097
- heartbeatFiles?: string[];
1098
1118
  perFileChars?: number;
1099
1119
  totalChars?: number;
1100
1120
  };
@@ -1318,8 +1338,9 @@ type UiServerHandle = {
1318
1338
  //#region src/ui/server.d.ts
1319
1339
  type UiServerStartOptions = UiServerOptions & {
1320
1340
  applyLiveConfigReload?: () => Promise<void>;
1341
+ runtimeUpdate?: UiRuntimeUpdateHost;
1321
1342
  };
1322
- declare function startUiServer(options: UiServerStartOptions): UiServerHandle;
1343
+ declare function startUiServer(options: UiServerStartOptions): Promise<UiServerHandle>;
1323
1344
  //#endregion
1324
1345
  //#region src/ui/router.d.ts
1325
1346
  declare function createUiRouter(options: UiRouterOptions): Hono;
@@ -1375,7 +1396,7 @@ declare function patchSession(configPath: string, key: string, patch: SessionPat
1375
1396
  availableSessionTypes?: string[];
1376
1397
  }): SessionHistoryView | null;
1377
1398
  declare function deleteSession(configPath: string, key: string): boolean;
1378
- declare function updateRuntime(configPath: string, patch: RuntimeConfigUpdate): Pick<ConfigView, "agents" | "bindings" | "session">;
1399
+ declare function updateRuntime(configPath: string, patch: RuntimeConfigUpdate): Pick<ConfigView, "companion" | "agents" | "bindings" | "session">;
1379
1400
  declare function updateSecrets(configPath: string, patch: SecretsConfigUpdate): SecretsView;
1380
1401
  //#endregion
1381
1402
  //#region src/ui/auth-bridge.d.ts
@@ -1383,4 +1404,4 @@ declare function getUiBridgeSecretPath(): string;
1383
1404
  declare function readUiBridgeSecret(): string | null;
1384
1405
  declare function ensureUiBridgeSecret(): string;
1385
1406
  //#endregion
1386
- export { AgentBindingView, AgentCreateRequest, AgentDeleteResult, AgentProfileView, AgentUpdateRequest, ApiError, ApiResponse, AppMetaView, AuthEnabledUpdateRequest, AuthLoginRequest, AuthPasswordUpdateRequest, AuthSetupRequest, AuthStatusView, BindingPeerView, BochaFreshnessValue, BootstrapPhase, BootstrapRemoteState, BootstrapStageState, BootstrapStatusView, ChannelAuthPollRequest, ChannelAuthPollResult, ChannelAuthStartRequest, ChannelAuthStartResult, ChannelSpecView, type ChatSessionTypeCtaView, type ChatSessionTypeOptionView, type ChatSessionTypesView, ConfigActionExecuteRequest, ConfigActionExecuteResult, ConfigActionManifest, ConfigActionType, ConfigMetaView, ConfigSchemaResponse, ConfigUiHint, ConfigUiHints, ConfigView, CronActionResult, CronCreateRequest, CronCreateResult, CronEnableRequest, CronJobStateView, CronJobView, CronListView, CronPayloadView, CronRunRequest, CronScheduleView, DEFAULT_SESSION_TYPE, MarketplaceApiConfig, MarketplaceInstallKind, MarketplaceInstallSkillParams, MarketplaceInstallSpec, MarketplaceInstalledRecord, MarketplaceInstalledView, MarketplaceInstaller, MarketplaceItemSummary, MarketplaceItemType, MarketplaceItemView, MarketplaceListView, MarketplaceLocalizedTextMap, MarketplaceMcpContentView, MarketplaceMcpDoctorResult, MarketplaceMcpInstallKind, MarketplaceMcpInstallRequest, MarketplaceMcpInstallResult, MarketplaceMcpInstallSpec, MarketplaceMcpManageAction, MarketplaceMcpManageRequest, MarketplaceMcpManageResult, MarketplaceMcpTemplateInput, MarketplacePluginContentView, MarketplacePluginInstallKind, MarketplacePluginInstallRequest, MarketplacePluginInstallResult, MarketplacePluginManageAction, MarketplacePluginManageRequest, MarketplacePluginManageResult, MarketplaceRecommendationView, MarketplaceSkillContentView, MarketplaceSkillInstallKind, MarketplaceSkillInstallRequest, MarketplaceSkillInstallResult, MarketplaceSkillManageAction, MarketplaceSkillManageRequest, MarketplaceSkillManageResult, MarketplaceSort, NcpSessionSkillsView, ProviderAuthImportResult, ProviderAuthPollRequest, ProviderAuthPollResult, ProviderAuthStartRequest, ProviderAuthStartResult, ProviderConfigUpdate, ProviderConfigView, ProviderConnectionTestRequest, ProviderConnectionTestResult, ProviderCreateRequest, ProviderCreateResult, ProviderDeleteResult, ProviderSpecView, RemoteAccessView, RemoteAccountProfileUpdateRequest, RemoteAccountView, RemoteBrowserAuthPollRequest, RemoteBrowserAuthPollResult, RemoteBrowserAuthStartRequest, RemoteBrowserAuthStartResult, RemoteDoctorCheckView, RemoteDoctorView, RemoteLoginRequest, RemoteRuntimeView, RemoteServiceAction, RemoteServiceActionResult, RemoteServiceView, RemoteSettingsUpdateRequest, RemoteSettingsView, RuntimeActionCapability, RuntimeActionImpact, RuntimeConfigUpdate, RuntimeControlAction, RuntimeControlActionResult, RuntimeControlEnvironment, RuntimeControlView, RuntimeEntryView, RuntimeLifecycleState, RuntimePendingRestart, RuntimeServiceState, SearchConfigUpdate, SearchConfigView, SearchProviderConfigView, SearchProviderName, SearchProviderSpecView, SecretProviderEnvView, SecretProviderExecView, SecretProviderFileView, SecretProviderView, SecretRefView, SecretSourceView, SecretsConfigUpdate, SecretsView, ServerPathBreadcrumbView, ServerPathBrowseView, ServerPathEntryView, ServerPathReadView, SessionConfigView, SessionEntryView, SessionEventView, SessionHistoryView, SessionMessageView, SessionPatchUpdate, SessionPatchValidationError, SessionSkillEntryView, SessionTypeDescribeParams, SessionsListView, TavilySearchDepthValue, UiNcpAgent, UiNcpAssetPutView, UiNcpAssetView, UiNcpSessionListView, UiNcpSessionMessagesView, UiNcpSessionService, UiNcpStoredAssetRecord, type UiRemoteAccessHost, type UiRuntimeControlHost, UiServerEvent, UiServerHandle, UiServerOptions, buildConfigMeta, buildConfigSchemaView, buildConfigView, createCustomProvider, createUiRouter, deleteCustomProvider, deleteSession, ensureUiBridgeSecret, executeConfigAction, getSessionHistory, getUiBridgeSecretPath, listSessions, loadConfigOrDefault, patchSession, readUiBridgeSecret, startUiServer, testProviderConnection, updateChannel, updateModel, updateProvider, updateRuntime, updateSearch, updateSecrets };
1407
+ export { AgentBindingView, AgentCreateRequest, AgentDeleteResult, AgentProfileView, AgentUpdateRequest, ApiError, ApiResponse, AppMetaView, AuthEnabledUpdateRequest, AuthLoginRequest, AuthPasswordUpdateRequest, AuthSetupRequest, AuthStatusView, BindingPeerView, BochaFreshnessValue, BootstrapPhase, BootstrapRemoteState, BootstrapStageState, BootstrapStatusView, ChannelAuthPollRequest, ChannelAuthPollResult, ChannelAuthStartRequest, ChannelAuthStartResult, ChannelSpecView, type ChatSessionTypeCtaView, type ChatSessionTypeOptionView, type ChatSessionTypesView, ConfigActionExecuteRequest, ConfigActionExecuteResult, ConfigActionManifest, ConfigActionType, ConfigMetaView, ConfigSchemaResponse, ConfigUiHint, ConfigUiHints, ConfigView, CronActionResult, CronCreateRequest, CronCreateResult, CronEnableRequest, CronJobStateView, CronJobView, CronListView, CronPayloadView, CronRunRequest, CronScheduleView, DEFAULT_SESSION_TYPE, MarketplaceApiConfig, MarketplaceInstallKind, MarketplaceInstallRequest, MarketplaceInstallResult, MarketplaceInstallSkillParams, MarketplaceInstallSpec, MarketplaceInstalledRecord, MarketplaceInstalledView, MarketplaceInstaller, MarketplaceItemSummary, MarketplaceItemType, MarketplaceItemView, MarketplaceListView, MarketplaceLocalizedTextMap, MarketplaceManageAction, MarketplaceManageRequest, MarketplaceManageResult, MarketplaceMcpContentView, MarketplaceMcpDoctorResult, MarketplaceMcpInstallKind, MarketplaceMcpInstallRequest, MarketplaceMcpInstallResult, MarketplaceMcpInstallSpec, MarketplaceMcpManageAction, MarketplaceMcpManageRequest, MarketplaceMcpManageResult, MarketplaceMcpTemplateInput, MarketplacePluginContentView, MarketplacePluginInstallKind, MarketplacePluginInstallRequest, MarketplacePluginInstallResult, MarketplacePluginManageAction, MarketplacePluginManageRequest, MarketplacePluginManageResult, MarketplaceRecommendationView, MarketplaceSkillContentView, MarketplaceSkillInstallKind, MarketplaceSkillInstallRequest, MarketplaceSkillInstallResult, MarketplaceSkillManageAction, MarketplaceSkillManageRequest, MarketplaceSkillManageResult, MarketplaceSort, NcpSessionSkillsView, ProviderAuthImportResult, ProviderAuthPollRequest, ProviderAuthPollResult, ProviderAuthStartRequest, ProviderAuthStartResult, ProviderConfigUpdate, ProviderConfigView, ProviderConnectionTestRequest, ProviderConnectionTestResult, ProviderCreateRequest, ProviderCreateResult, ProviderDeleteResult, ProviderSpecView, RemoteAccessView, RemoteAccountProfileUpdateRequest, RemoteAccountView, RemoteBrowserAuthPollRequest, RemoteBrowserAuthPollResult, RemoteBrowserAuthStartRequest, RemoteBrowserAuthStartResult, RemoteDoctorCheckView, RemoteDoctorView, RemoteLoginRequest, RemoteRuntimeView, RemoteServiceAction, RemoteServiceActionResult, RemoteServiceView, RemoteSettingsUpdateRequest, RemoteSettingsView, RuntimeActionCapability, RuntimeActionImpact, RuntimeConfigUpdate, RuntimeControlAction, RuntimeControlActionResult, RuntimeControlEnvironment, RuntimeControlView, RuntimeEntryView, RuntimeLifecycleState, RuntimePendingRestart, RuntimeServiceState, SearchConfigUpdate, SearchConfigView, SearchProviderConfigView, SearchProviderName, SearchProviderSpecView, SecretProviderEnvView, SecretProviderExecView, SecretProviderFileView, SecretProviderView, SecretRefView, SecretSourceView, SecretsConfigUpdate, SecretsView, ServerPathBreadcrumbView, ServerPathBrowseView, ServerPathEntryView, ServerPathReadView, SessionConfigView, SessionEntryView, SessionEventView, SessionHistoryView, SessionMessageView, SessionPatchUpdate, SessionPatchValidationError, SessionSkillEntryView, SessionTypeDescribeParams, SessionsListView, TavilySearchDepthValue, UiNcpAgent, UiNcpAssetPutView, UiNcpAssetView, UiNcpSessionListView, UiNcpSessionMessagesView, UiNcpSessionService, UiNcpStoredAssetRecord, type UiRemoteAccessHost, type UiRuntimeControlHost, type UiRuntimeUpdateHost, UiServerEvent, UiServerHandle, UiServerOptions, buildConfigMeta, buildConfigSchemaView, buildConfigView, createCustomProvider, createUiRouter, deleteCustomProvider, deleteSession, ensureUiBridgeSecret, executeConfigAction, getSessionHistory, getUiBridgeSecretPath, listSessions, loadConfigOrDefault, patchSession, readUiBridgeSecret, startUiServer, testProviderConnection, updateChannel, updateModel, updateProvider, updateRuntime, updateSearch, updateSecrets };
package/dist/index.js CHANGED
@@ -1332,6 +1332,7 @@ function buildConfigView(config, options) {
1332
1332
  const providers = {};
1333
1333
  for (const [name, provider] of Object.entries(config.providers)) providers[name] = toProviderView(config, provider, name, uiHints, findServerBuiltinProviderByName(name));
1334
1334
  return {
1335
+ companion: sanitizePublicConfigValue(config.companion, "companion", uiHints),
1335
1336
  agents: sanitizePublicConfigValue(config.agents, "agents", uiHints),
1336
1337
  providers,
1337
1338
  search: buildSearchView(config),
@@ -1907,18 +1908,22 @@ function deleteSession(configPath, key) {
1907
1908
  if (!normalizedKey) return false;
1908
1909
  return createSessionManager(loadConfigOrDefault(configPath)).delete(normalizedKey);
1909
1910
  }
1910
- function updateRuntime(configPath, patch) {
1911
- const config = loadConfigOrDefault(configPath);
1912
- const defaultsPatch = patch.agents?.defaults;
1913
- if (defaultsPatch && Object.prototype.hasOwnProperty.call(defaultsPatch, "contextTokens")) {
1911
+ function applyRuntimeAgentDefaultsPatch(config, defaultsPatch) {
1912
+ if (!defaultsPatch) return;
1913
+ if (Object.prototype.hasOwnProperty.call(defaultsPatch, "contextTokens")) {
1914
1914
  const nextContextTokens = defaultsPatch.contextTokens;
1915
1915
  if (typeof nextContextTokens === "number" && Number.isFinite(nextContextTokens)) config.agents.defaults.contextTokens = Math.max(1e3, Math.trunc(nextContextTokens));
1916
1916
  }
1917
- if (defaultsPatch && Object.prototype.hasOwnProperty.call(defaultsPatch, "engine")) config.agents.defaults.engine = normalizeOptionalString(defaultsPatch.engine) ?? "native";
1918
- if (defaultsPatch && Object.prototype.hasOwnProperty.call(defaultsPatch, "engineConfig")) {
1917
+ if (Object.prototype.hasOwnProperty.call(defaultsPatch, "engine")) config.agents.defaults.engine = normalizeOptionalString(defaultsPatch.engine) ?? "native";
1918
+ if (Object.prototype.hasOwnProperty.call(defaultsPatch, "engineConfig")) {
1919
1919
  const nextEngineConfig = defaultsPatch.engineConfig;
1920
1920
  if (nextEngineConfig && typeof nextEngineConfig === "object" && !Array.isArray(nextEngineConfig)) config.agents.defaults.engineConfig = { ...nextEngineConfig };
1921
1921
  }
1922
+ }
1923
+ function updateRuntime(configPath, patch) {
1924
+ const config = loadConfigOrDefault(configPath);
1925
+ if (patch.companion && Object.prototype.hasOwnProperty.call(patch.companion, "enabled")) config.companion.enabled = Boolean(patch.companion.enabled);
1926
+ applyRuntimeAgentDefaultsPatch(config, patch.agents?.defaults);
1922
1927
  if (patch.agents && Object.prototype.hasOwnProperty.call(patch.agents, "list")) config.agents.list = (patch.agents.list ?? []).map((entry) => {
1923
1928
  const normalizedEngine = normalizeOptionalString(entry.engine);
1924
1929
  const hasEngineConfig = entry.engineConfig && typeof entry.engineConfig === "object" && !Array.isArray(entry.engineConfig);
@@ -1939,6 +1944,7 @@ function updateRuntime(configPath, patch) {
1939
1944
  saveConfig(next, configPath);
1940
1945
  const view = buildConfigView(next);
1941
1946
  return {
1947
+ companion: view.companion,
1942
1948
  agents: view.agents,
1943
1949
  bindings: view.bindings ?? [],
1944
1950
  session: view.session ?? {}
@@ -2707,6 +2713,7 @@ var ConfigRoutesController = class {
2707
2713
  if (body.data.agents?.defaults && Object.prototype.hasOwnProperty.call(body.data.agents.defaults, "engine")) changedPaths.push("agents.defaults.engine");
2708
2714
  if (body.data.agents?.defaults && Object.prototype.hasOwnProperty.call(body.data.agents.defaults, "engineConfig")) changedPaths.push("agents.defaults.engineConfig");
2709
2715
  if (body.data.agents?.runtimes && Object.prototype.hasOwnProperty.call(body.data.agents.runtimes, "entries")) changedPaths.push("agents.runtimes.entries");
2716
+ if (body.data.companion && Object.prototype.hasOwnProperty.call(body.data.companion, "enabled")) changedPaths.push("companion.enabled");
2710
2717
  changedPaths.push("agents.list", "bindings", "session");
2711
2718
  await this.publishConfigUpdates(changedPaths);
2712
2719
  return c.json(ok(result));
@@ -3207,6 +3214,7 @@ var NcpSessionRoutesController = class {
3207
3214
  sessionId,
3208
3215
  status: session.status ?? "idle",
3209
3216
  messages,
3217
+ ...session.contextWindow ? { contextWindow: session.contextWindow } : {},
3210
3218
  total: messages.length
3211
3219
  };
3212
3220
  return c.json(ok(payload));
@@ -4530,6 +4538,64 @@ var RuntimeControlRoutesController = class {
4530
4538
  };
4531
4539
  };
4532
4540
  //#endregion
4541
+ //#region src/ui/ui-routes/runtime-update.controller.ts
4542
+ var RuntimeUpdateRoutesController = class {
4543
+ constructor(host) {
4544
+ this.host = host;
4545
+ }
4546
+ getState = async (c) => {
4547
+ try {
4548
+ return c.json(ok(await this.host.getState()));
4549
+ } catch (error) {
4550
+ return c.json(err("RUNTIME_UPDATE_STATE_FAILED", formatUserFacingError(error)), 500);
4551
+ }
4552
+ };
4553
+ checkForUpdates = async (c) => {
4554
+ try {
4555
+ return c.json(ok(await this.host.checkForUpdates()));
4556
+ } catch (error) {
4557
+ return c.json(err("RUNTIME_UPDATE_CHECK_FAILED", formatUserFacingError(error)), 400);
4558
+ }
4559
+ };
4560
+ downloadUpdate = async (c) => {
4561
+ try {
4562
+ return c.json(ok(await this.host.downloadUpdate()));
4563
+ } catch (error) {
4564
+ return c.json(err("RUNTIME_UPDATE_DOWNLOAD_FAILED", formatUserFacingError(error)), 400);
4565
+ }
4566
+ };
4567
+ applyDownloadedUpdate = async (c) => {
4568
+ try {
4569
+ return c.json(ok(await this.host.applyDownloadedUpdate()));
4570
+ } catch (error) {
4571
+ return c.json(err("RUNTIME_UPDATE_APPLY_FAILED", formatUserFacingError(error)), 400);
4572
+ }
4573
+ };
4574
+ updatePreferences = async (c) => {
4575
+ const body = await readJson(c.req.raw);
4576
+ if (!body.ok) return c.json(err("INVALID_BODY", "invalid json body"), 400);
4577
+ try {
4578
+ return c.json(ok(await this.host.updatePreferences({
4579
+ ...typeof body.data.automaticChecks === "boolean" ? { automaticChecks: body.data.automaticChecks } : {},
4580
+ ...typeof body.data.autoDownload === "boolean" ? { autoDownload: body.data.autoDownload } : {}
4581
+ })));
4582
+ } catch (error) {
4583
+ return c.json(err("RUNTIME_UPDATE_PREFERENCES_FAILED", formatUserFacingError(error)), 400);
4584
+ }
4585
+ };
4586
+ updateChannel = async (c) => {
4587
+ const body = await readJson(c.req.raw);
4588
+ if (!body.ok) return c.json(err("INVALID_BODY", "invalid json body"), 400);
4589
+ const channel = body.data.channel;
4590
+ if (channel !== "stable" && channel !== "beta") return c.json(err("INVALID_BODY", "channel must be stable or beta"), 400);
4591
+ try {
4592
+ return c.json(ok(await this.host.updateChannel(channel)));
4593
+ } catch (error) {
4594
+ return c.json(err("RUNTIME_UPDATE_CHANNEL_FAILED", formatUserFacingError(error)), 400);
4595
+ }
4596
+ };
4597
+ };
4598
+ //#endregion
4533
4599
  //#region src/ui/server-path/server-path-browse.utils.ts
4534
4600
  var ServerPathBrowseError = class extends Error {
4535
4601
  constructor(code, message) {
@@ -4866,7 +4932,17 @@ function registerRuntimeControlRoutes(app, runtimeControlController) {
4866
4932
  app.post("/api/runtime/control/restart-service", runtimeControlController.restartService);
4867
4933
  app.post("/api/runtime/control/stop-service", runtimeControlController.stopService);
4868
4934
  }
4935
+ function registerRuntimeUpdateRoutes(app, runtimeUpdateController) {
4936
+ if (!runtimeUpdateController) return;
4937
+ app.get("/api/runtime/update", runtimeUpdateController.getState);
4938
+ app.post("/api/runtime/update/check", runtimeUpdateController.checkForUpdates);
4939
+ app.post("/api/runtime/update/download", runtimeUpdateController.downloadUpdate);
4940
+ app.post("/api/runtime/update/apply", runtimeUpdateController.applyDownloadedUpdate);
4941
+ app.put("/api/runtime/update/preferences", runtimeUpdateController.updatePreferences);
4942
+ app.put("/api/runtime/update/channel", runtimeUpdateController.updateChannel);
4943
+ }
4869
4944
  function createUiRouteControllers(options, authService, marketplaceBaseUrl) {
4945
+ const { remoteAccess, runtimeControl, runtimeUpdate } = options;
4870
4946
  return {
4871
4947
  app: new AppRoutesController(options),
4872
4948
  agents: new AgentsRoutesController(options),
@@ -4876,8 +4952,9 @@ function createUiRouteControllers(options, authService, marketplaceBaseUrl) {
4876
4952
  ncpSession: new NcpSessionRoutesController(options),
4877
4953
  ncpAsset: new NcpAssetRoutesController(options),
4878
4954
  serverPath: new ServerPathRoutesController(),
4879
- remote: options.remoteAccess ? new RemoteRoutesController(options.remoteAccess) : null,
4880
- runtimeControl: options.runtimeControl ? new RuntimeControlRoutesController(options.runtimeControl) : null,
4955
+ remote: remoteAccess ? new RemoteRoutesController(remoteAccess) : null,
4956
+ runtimeControl: runtimeControl ? new RuntimeControlRoutesController(runtimeControl) : null,
4957
+ runtimeUpdate: runtimeUpdate ? new RuntimeUpdateRoutesController(runtimeUpdate) : null,
4881
4958
  pluginMarketplace: new PluginMarketplaceController(options, marketplaceBaseUrl),
4882
4959
  skillMarketplace: new SkillMarketplaceController(options, marketplaceBaseUrl),
4883
4960
  mcpMarketplace: new McpMarketplaceController(options, marketplaceBaseUrl)
@@ -4914,6 +4991,7 @@ function createUiRouter(options) {
4914
4991
  registerCronRoutes(app, controllers.cron);
4915
4992
  registerRemoteRoutes(app, controllers.remote);
4916
4993
  registerRuntimeControlRoutes(app, controllers.runtimeControl);
4994
+ registerRuntimeUpdateRoutes(app, controllers.runtimeUpdate);
4917
4995
  mountMarketplaceRoutes(app, {
4918
4996
  plugin: controllers.pluginMarketplace,
4919
4997
  skill: controllers.skillMarketplace,
@@ -4956,11 +5034,66 @@ function applyCorsHeaders(params) {
4956
5034
  appendVaryHeader(params.headers, "Origin");
4957
5035
  appendVaryHeader(params.headers, "Access-Control-Request-Headers");
4958
5036
  }
4959
- function startUiServer(options) {
5037
+ function createUiEventPublisher(clients) {
5038
+ return (event) => {
5039
+ const payload = JSON.stringify(event);
5040
+ for (const client of clients) if (client.readyState === WebSocket.OPEN) client.send(payload);
5041
+ };
5042
+ }
5043
+ function mountUiStaticAssets(app, staticDir) {
5044
+ if (!existsSync(join(staticDir, "index.html"))) return;
5045
+ const indexHtml = readFileSync(join(staticDir, "index.html"), "utf-8");
5046
+ app.use("/*", serveStatic({
5047
+ root: staticDir,
5048
+ join,
5049
+ getContent: async (path) => {
5050
+ try {
5051
+ return await readFile(path);
5052
+ } catch {
5053
+ return null;
5054
+ }
5055
+ },
5056
+ isDir: async (path) => {
5057
+ try {
5058
+ return (await stat(path)).isDirectory();
5059
+ } catch {
5060
+ return false;
5061
+ }
5062
+ }
5063
+ }));
5064
+ app.get("*", (c) => {
5065
+ const path = c.req.path;
5066
+ if (path.startsWith("/api") || path.startsWith("/ws") || path.startsWith("/_remote")) return c.notFound();
5067
+ return c.html(indexHtml);
5068
+ });
5069
+ }
5070
+ function attachUiSocketServer(httpServer, authService, clients) {
5071
+ const wss = new WebSocketServer({ noServer: true });
5072
+ httpServer.on("upgrade", (request, socket, head) => {
5073
+ const host = request.headers.host ?? "127.0.0.1";
5074
+ const url = request.url ?? "/";
5075
+ if (new URL(url, `http://${host}`).pathname !== "/ws") return;
5076
+ if (!authService.isSocketAuthenticated(request)) {
5077
+ socket.write("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n");
5078
+ socket.destroy();
5079
+ return;
5080
+ }
5081
+ wss.handleUpgrade(request, socket, head, (ws) => {
5082
+ wss.emit("connection", ws, request);
5083
+ });
5084
+ });
5085
+ wss.on("connection", (socket) => {
5086
+ clients.add(socket);
5087
+ socket.on("close", () => clients.delete(socket));
5088
+ });
5089
+ return wss;
5090
+ }
5091
+ async function startUiServer(options) {
5092
+ const { applyLiveConfigReload, configPath, corsOrigins, cronService, getBootstrapStatus, getPluginChannelBindings, getPluginUiMetadata, host, initializeAgentHomeDirectory, marketplace, ncpAgent, ncpSessionService, port, productVersion, remoteAccess, runtimeControl, runtimeUpdate, staticDir } = options;
4960
5093
  const app = new Hono();
4961
5094
  app.use("/*", compress());
4962
- const corsPolicy = options.corsOrigins ?? DEFAULT_CORS_ORIGINS;
4963
- const authService = new UiAuthService(options.configPath);
5095
+ const corsPolicy = corsOrigins ?? DEFAULT_CORS_ORIGINS;
5096
+ const authService = new UiAuthService(configPath);
4964
5097
  app.use("/api/*", async (c, next) => {
4965
5098
  const allowOrigin = resolveAllowedCorsOrigin(readRequestHeader(c.req.raw, "origin"), corsPolicy);
4966
5099
  const allowHeaders = readRequestHeader(c.req.raw, "access-control-request-headers");
@@ -4987,86 +5120,43 @@ function startUiServer(options) {
4987
5120
  });
4988
5121
  });
4989
5122
  const clients = /* @__PURE__ */ new Set();
4990
- const publish = (event) => {
4991
- const payload = JSON.stringify(event);
4992
- for (const client of clients) if (client.readyState === WebSocket.OPEN) client.send(payload);
4993
- };
5123
+ const publish = createUiEventPublisher(clients);
4994
5124
  app.route("/", createUiRouter({
4995
- configPath: options.configPath,
4996
- productVersion: options.productVersion,
5125
+ configPath,
5126
+ productVersion,
4997
5127
  publish,
4998
- applyLiveConfigReload: options.applyLiveConfigReload,
4999
- initializeAgentHomeDirectory: options.initializeAgentHomeDirectory,
5000
- marketplace: options.marketplace,
5001
- cronService: options.cronService,
5002
- ncpAgent: options.ncpAgent,
5003
- ncpSessionService: options.ncpSessionService,
5128
+ applyLiveConfigReload,
5129
+ initializeAgentHomeDirectory,
5130
+ marketplace,
5131
+ cronService,
5132
+ ncpAgent,
5133
+ ncpSessionService,
5004
5134
  authService,
5005
- remoteAccess: options.remoteAccess,
5006
- runtimeControl: options.runtimeControl,
5007
- getBootstrapStatus: options.getBootstrapStatus,
5008
- getPluginChannelBindings: options.getPluginChannelBindings,
5009
- getPluginUiMetadata: options.getPluginUiMetadata
5135
+ remoteAccess,
5136
+ runtimeControl,
5137
+ runtimeUpdate,
5138
+ getBootstrapStatus,
5139
+ getPluginChannelBindings,
5140
+ getPluginUiMetadata
5010
5141
  }));
5011
- const staticDir = options.staticDir;
5012
- if (staticDir && existsSync(join(staticDir, "index.html"))) {
5013
- const indexHtml = readFileSync(join(staticDir, "index.html"), "utf-8");
5014
- app.use("/*", serveStatic({
5015
- root: staticDir,
5016
- join,
5017
- getContent: async (path) => {
5018
- try {
5019
- return await readFile(path);
5020
- } catch {
5021
- return null;
5022
- }
5023
- },
5024
- isDir: async (path) => {
5025
- try {
5026
- return (await stat(path)).isDirectory();
5027
- } catch {
5028
- return false;
5029
- }
5030
- }
5031
- }));
5032
- app.get("*", (c) => {
5033
- const path = c.req.path;
5034
- if (path.startsWith("/api") || path.startsWith("/ws") || path.startsWith("/_remote")) return c.notFound();
5035
- return c.html(indexHtml);
5036
- });
5037
- }
5038
- const server = serve({
5039
- fetch: app.fetch,
5040
- port: options.port,
5041
- hostname: options.host
5042
- });
5043
- const httpServer = server;
5044
- const wss = new WebSocketServer({ noServer: true });
5045
- httpServer.on("upgrade", (request, socket, head) => {
5046
- const host = request.headers.host ?? "127.0.0.1";
5047
- const url = request.url ?? "/";
5048
- if (new URL(url, `http://${host}`).pathname !== "/ws") return;
5049
- if (!authService.isSocketAuthenticated(request)) {
5050
- socket.write("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n");
5051
- socket.destroy();
5052
- return;
5053
- }
5054
- wss.handleUpgrade(request, socket, head, (ws) => {
5055
- wss.emit("connection", ws, request);
5056
- });
5057
- });
5058
- wss.on("connection", (socket) => {
5059
- clients.add(socket);
5060
- socket.on("close", () => clients.delete(socket));
5142
+ if (staticDir) mountUiStaticAssets(app, staticDir);
5143
+ const server = await new Promise((resolve, reject) => {
5144
+ const httpServer = serve({
5145
+ fetch: app.fetch,
5146
+ port,
5147
+ hostname: host
5148
+ }, () => resolve(httpServer));
5149
+ httpServer.once("error", reject);
5061
5150
  });
5151
+ const wss = attachUiSocketServer(server, authService, clients);
5062
5152
  return {
5063
- host: options.host,
5064
- port: options.port,
5153
+ host,
5154
+ port,
5065
5155
  publish,
5066
5156
  close: () => new Promise((resolve) => {
5067
5157
  wss.close(() => {
5068
5158
  server.close(() => {
5069
- Promise.resolve(options.ncpAgent?.agentClientEndpoint.stop()).catch(() => void 0).finally(() => resolve());
5159
+ Promise.resolve(ncpAgent?.agentClientEndpoint.stop()).catch(() => void 0).finally(() => resolve());
5070
5160
  });
5071
5161
  });
5072
5162
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/server",
3
- "version": "0.12.12",
3
+ "version": "0.12.13-beta.1",
4
4
  "private": false,
5
5
  "description": "Nextclaw UI/API server.",
6
6
  "type": "module",
@@ -18,12 +18,13 @@
18
18
  "@hono/node-server": "^1.13.3",
19
19
  "hono": "^4.6.2",
20
20
  "ws": "^8.18.0",
21
- "@nextclaw/core": "0.12.12",
22
- "@nextclaw/mcp": "0.1.77",
23
- "@nextclaw/ncp": "0.5.5",
24
- "@nextclaw/ncp-http-agent-server": "0.3.17",
25
- "@nextclaw/openclaw-compat": "1.0.12",
26
- "@nextclaw/runtime": "0.2.44"
21
+ "@nextclaw/core": "0.12.13-beta.1",
22
+ "@nextclaw/kernel": "0.1.2-beta.2",
23
+ "@nextclaw/mcp": "0.1.78-beta.1",
24
+ "@nextclaw/ncp": "0.5.6-beta.0",
25
+ "@nextclaw/openclaw-compat": "1.0.13-beta.1",
26
+ "@nextclaw/ncp-http-agent-server": "0.3.18-beta.1",
27
+ "@nextclaw/runtime": "0.2.45-beta.0"
27
28
  },
28
29
  "devDependencies": {
29
30
  "@types/node": "^20.17.6",