@nextclaw/server 0.12.11 → 0.12.13-beta.0

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
@@ -401,6 +402,7 @@ type UiRouterOptions = {
401
402
  authService?: UiAuthService;
402
403
  remoteAccess?: UiRemoteAccessHost;
403
404
  runtimeControl?: UiRuntimeControlHost;
405
+ runtimeUpdate?: UiRuntimeUpdateHost;
404
406
  getBootstrapStatus?: () => BootstrapStatusView;
405
407
  getPluginChannelBindings?: () => PluginChannelBinding[];
406
408
  getPluginUiMetadata?: () => PluginUiMetadata[];
@@ -422,6 +424,14 @@ type UiRuntimeControlHost = {
422
424
  restartService: () => Promise<RuntimeControlActionResult> | RuntimeControlActionResult;
423
425
  stopService: () => Promise<RuntimeControlActionResult> | RuntimeControlActionResult;
424
426
  };
427
+ type UiRuntimeUpdateHost = {
428
+ getState: () => Promise<UpdateSnapshot> | UpdateSnapshot;
429
+ checkForUpdates: () => Promise<UpdateSnapshot> | UpdateSnapshot;
430
+ downloadUpdate: () => Promise<UpdateSnapshot> | UpdateSnapshot;
431
+ applyDownloadedUpdate: () => Promise<UpdateSnapshot> | UpdateSnapshot;
432
+ updatePreferences: (preferences: Partial<UpdatePreferences>) => Promise<UpdateSnapshot> | UpdateSnapshot;
433
+ updateChannel: (channel: UpdateSnapshot["channel"]) => Promise<UpdateSnapshot> | UpdateSnapshot;
434
+ };
425
435
  //#endregion
426
436
  //#region src/ui/chat-session-type.types.d.ts
427
437
  type ChatSessionTypeIconView = {
@@ -935,9 +945,12 @@ type CronScheduleView = {
935
945
  type CronPayloadView = {
936
946
  kind?: "system_event" | "agent_turn";
937
947
  message: string;
948
+ agentId?: string | null;
949
+ sessionId?: string | null;
938
950
  deliver?: boolean;
939
951
  channel?: string | null;
940
952
  to?: string | null;
953
+ accountId?: string | null;
941
954
  };
942
955
  type CronJobStateView = {
943
956
  nextRunAt?: string | null;
@@ -965,6 +978,7 @@ type CronCreateRequest = {
965
978
  message: string;
966
979
  schedule: CronScheduleView;
967
980
  agentId?: string | null;
981
+ sessionId?: string | null;
968
982
  deliver?: boolean;
969
983
  channel?: string | null;
970
984
  to?: string | null;
@@ -985,6 +999,9 @@ type CronActionResult = {
985
999
  executed?: boolean;
986
1000
  };
987
1001
  type RuntimeConfigUpdate = {
1002
+ companion?: {
1003
+ enabled?: boolean;
1004
+ };
988
1005
  agents?: {
989
1006
  defaults?: {
990
1007
  contextTokens?: number;
@@ -1073,6 +1090,9 @@ type UiNcpAgent = {
1073
1090
  basePath?: string;
1074
1091
  };
1075
1092
  type ConfigView = {
1093
+ companion?: {
1094
+ enabled?: boolean;
1095
+ };
1076
1096
  agents: {
1077
1097
  defaults: {
1078
1098
  model: string;
@@ -1090,7 +1110,6 @@ type ConfigView = {
1090
1110
  bootstrap?: {
1091
1111
  files?: string[];
1092
1112
  minimalFiles?: string[];
1093
- heartbeatFiles?: string[];
1094
1113
  perFileChars?: number;
1095
1114
  totalChars?: number;
1096
1115
  };
@@ -1314,6 +1333,7 @@ type UiServerHandle = {
1314
1333
  //#region src/ui/server.d.ts
1315
1334
  type UiServerStartOptions = UiServerOptions & {
1316
1335
  applyLiveConfigReload?: () => Promise<void>;
1336
+ runtimeUpdate?: UiRuntimeUpdateHost;
1317
1337
  };
1318
1338
  declare function startUiServer(options: UiServerStartOptions): UiServerHandle;
1319
1339
  //#endregion
@@ -1371,7 +1391,7 @@ declare function patchSession(configPath: string, key: string, patch: SessionPat
1371
1391
  availableSessionTypes?: string[];
1372
1392
  }): SessionHistoryView | null;
1373
1393
  declare function deleteSession(configPath: string, key: string): boolean;
1374
- declare function updateRuntime(configPath: string, patch: RuntimeConfigUpdate): Pick<ConfigView, "agents" | "bindings" | "session">;
1394
+ declare function updateRuntime(configPath: string, patch: RuntimeConfigUpdate): Pick<ConfigView, "companion" | "agents" | "bindings" | "session">;
1375
1395
  declare function updateSecrets(configPath: string, patch: SecretsConfigUpdate): SecretsView;
1376
1396
  //#endregion
1377
1397
  //#region src/ui/auth-bridge.d.ts
@@ -1379,4 +1399,4 @@ declare function getUiBridgeSecretPath(): string;
1379
1399
  declare function readUiBridgeSecret(): string | null;
1380
1400
  declare function ensureUiBridgeSecret(): string;
1381
1401
  //#endregion
1382
- 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 };
1402
+ 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, 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));
@@ -2792,6 +2799,7 @@ function readCronCreateParams(input) {
2792
2799
  message,
2793
2800
  schedule,
2794
2801
  agentId: readNonEmptyString(input.agentId),
2802
+ sessionId: readNonEmptyString(input.sessionId),
2795
2803
  deliver: input.deliver === true,
2796
2804
  channel: readNonEmptyString(input.channel),
2797
2805
  to: readNonEmptyString(input.to),
@@ -3206,6 +3214,7 @@ var NcpSessionRoutesController = class {
3206
3214
  sessionId,
3207
3215
  status: session.status ?? "idle",
3208
3216
  messages,
3217
+ ...session.contextWindow ? { contextWindow: session.contextWindow } : {},
3209
3218
  total: messages.length
3210
3219
  };
3211
3220
  return c.json(ok(payload));
@@ -4529,6 +4538,64 @@ var RuntimeControlRoutesController = class {
4529
4538
  };
4530
4539
  };
4531
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
4532
4599
  //#region src/ui/server-path/server-path-browse.utils.ts
4533
4600
  var ServerPathBrowseError = class extends Error {
4534
4601
  constructor(code, message) {
@@ -4865,7 +4932,17 @@ function registerRuntimeControlRoutes(app, runtimeControlController) {
4865
4932
  app.post("/api/runtime/control/restart-service", runtimeControlController.restartService);
4866
4933
  app.post("/api/runtime/control/stop-service", runtimeControlController.stopService);
4867
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
+ }
4868
4944
  function createUiRouteControllers(options, authService, marketplaceBaseUrl) {
4945
+ const { remoteAccess, runtimeControl, runtimeUpdate } = options;
4869
4946
  return {
4870
4947
  app: new AppRoutesController(options),
4871
4948
  agents: new AgentsRoutesController(options),
@@ -4875,8 +4952,9 @@ function createUiRouteControllers(options, authService, marketplaceBaseUrl) {
4875
4952
  ncpSession: new NcpSessionRoutesController(options),
4876
4953
  ncpAsset: new NcpAssetRoutesController(options),
4877
4954
  serverPath: new ServerPathRoutesController(),
4878
- remote: options.remoteAccess ? new RemoteRoutesController(options.remoteAccess) : null,
4879
- 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,
4880
4958
  pluginMarketplace: new PluginMarketplaceController(options, marketplaceBaseUrl),
4881
4959
  skillMarketplace: new SkillMarketplaceController(options, marketplaceBaseUrl),
4882
4960
  mcpMarketplace: new McpMarketplaceController(options, marketplaceBaseUrl)
@@ -4913,6 +4991,7 @@ function createUiRouter(options) {
4913
4991
  registerCronRoutes(app, controllers.cron);
4914
4992
  registerRemoteRoutes(app, controllers.remote);
4915
4993
  registerRuntimeControlRoutes(app, controllers.runtimeControl);
4994
+ registerRuntimeUpdateRoutes(app, controllers.runtimeUpdate);
4916
4995
  mountMarketplaceRoutes(app, {
4917
4996
  plugin: controllers.pluginMarketplace,
4918
4997
  skill: controllers.skillMarketplace,
@@ -4955,11 +5034,66 @@ function applyCorsHeaders(params) {
4955
5034
  appendVaryHeader(params.headers, "Origin");
4956
5035
  appendVaryHeader(params.headers, "Access-Control-Request-Headers");
4957
5036
  }
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
+ }
4958
5091
  function startUiServer(options) {
5092
+ const { applyLiveConfigReload, configPath, corsOrigins, cronService, getBootstrapStatus, getPluginChannelBindings, getPluginUiMetadata, host, initializeAgentHomeDirectory, marketplace, ncpAgent, ncpSessionService, port, productVersion, remoteAccess, runtimeControl, runtimeUpdate, staticDir } = options;
4959
5093
  const app = new Hono();
4960
5094
  app.use("/*", compress());
4961
- const corsPolicy = options.corsOrigins ?? DEFAULT_CORS_ORIGINS;
4962
- const authService = new UiAuthService(options.configPath);
5095
+ const corsPolicy = corsOrigins ?? DEFAULT_CORS_ORIGINS;
5096
+ const authService = new UiAuthService(configPath);
4963
5097
  app.use("/api/*", async (c, next) => {
4964
5098
  const allowOrigin = resolveAllowedCorsOrigin(readRequestHeader(c.req.raw, "origin"), corsPolicy);
4965
5099
  const allowHeaders = readRequestHeader(c.req.raw, "access-control-request-headers");
@@ -4986,86 +5120,40 @@ function startUiServer(options) {
4986
5120
  });
4987
5121
  });
4988
5122
  const clients = /* @__PURE__ */ new Set();
4989
- const publish = (event) => {
4990
- const payload = JSON.stringify(event);
4991
- for (const client of clients) if (client.readyState === WebSocket.OPEN) client.send(payload);
4992
- };
5123
+ const publish = createUiEventPublisher(clients);
4993
5124
  app.route("/", createUiRouter({
4994
- configPath: options.configPath,
4995
- productVersion: options.productVersion,
5125
+ configPath,
5126
+ productVersion,
4996
5127
  publish,
4997
- applyLiveConfigReload: options.applyLiveConfigReload,
4998
- initializeAgentHomeDirectory: options.initializeAgentHomeDirectory,
4999
- marketplace: options.marketplace,
5000
- cronService: options.cronService,
5001
- ncpAgent: options.ncpAgent,
5002
- ncpSessionService: options.ncpSessionService,
5128
+ applyLiveConfigReload,
5129
+ initializeAgentHomeDirectory,
5130
+ marketplace,
5131
+ cronService,
5132
+ ncpAgent,
5133
+ ncpSessionService,
5003
5134
  authService,
5004
- remoteAccess: options.remoteAccess,
5005
- runtimeControl: options.runtimeControl,
5006
- getBootstrapStatus: options.getBootstrapStatus,
5007
- getPluginChannelBindings: options.getPluginChannelBindings,
5008
- getPluginUiMetadata: options.getPluginUiMetadata
5135
+ remoteAccess,
5136
+ runtimeControl,
5137
+ runtimeUpdate,
5138
+ getBootstrapStatus,
5139
+ getPluginChannelBindings,
5140
+ getPluginUiMetadata
5009
5141
  }));
5010
- const staticDir = options.staticDir;
5011
- if (staticDir && existsSync(join(staticDir, "index.html"))) {
5012
- const indexHtml = readFileSync(join(staticDir, "index.html"), "utf-8");
5013
- app.use("/*", serveStatic({
5014
- root: staticDir,
5015
- join,
5016
- getContent: async (path) => {
5017
- try {
5018
- return await readFile(path);
5019
- } catch {
5020
- return null;
5021
- }
5022
- },
5023
- isDir: async (path) => {
5024
- try {
5025
- return (await stat(path)).isDirectory();
5026
- } catch {
5027
- return false;
5028
- }
5029
- }
5030
- }));
5031
- app.get("*", (c) => {
5032
- const path = c.req.path;
5033
- if (path.startsWith("/api") || path.startsWith("/ws") || path.startsWith("/_remote")) return c.notFound();
5034
- return c.html(indexHtml);
5035
- });
5036
- }
5142
+ if (staticDir) mountUiStaticAssets(app, staticDir);
5037
5143
  const server = serve({
5038
5144
  fetch: app.fetch,
5039
- port: options.port,
5040
- hostname: options.host
5041
- });
5042
- const httpServer = server;
5043
- const wss = new WebSocketServer({ noServer: true });
5044
- httpServer.on("upgrade", (request, socket, head) => {
5045
- const host = request.headers.host ?? "127.0.0.1";
5046
- const url = request.url ?? "/";
5047
- if (new URL(url, `http://${host}`).pathname !== "/ws") return;
5048
- if (!authService.isSocketAuthenticated(request)) {
5049
- socket.write("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n");
5050
- socket.destroy();
5051
- return;
5052
- }
5053
- wss.handleUpgrade(request, socket, head, (ws) => {
5054
- wss.emit("connection", ws, request);
5055
- });
5056
- });
5057
- wss.on("connection", (socket) => {
5058
- clients.add(socket);
5059
- socket.on("close", () => clients.delete(socket));
5145
+ port,
5146
+ hostname: host
5060
5147
  });
5148
+ const wss = attachUiSocketServer(server, authService, clients);
5061
5149
  return {
5062
- host: options.host,
5063
- port: options.port,
5150
+ host,
5151
+ port,
5064
5152
  publish,
5065
5153
  close: () => new Promise((resolve) => {
5066
5154
  wss.close(() => {
5067
5155
  server.close(() => {
5068
- Promise.resolve(options.ncpAgent?.agentClientEndpoint.stop()).catch(() => void 0).finally(() => resolve());
5156
+ Promise.resolve(ncpAgent?.agentClientEndpoint.stop()).catch(() => void 0).finally(() => resolve());
5069
5157
  });
5070
5158
  });
5071
5159
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/server",
3
- "version": "0.12.11",
3
+ "version": "0.12.13-beta.0",
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.11",
22
- "@nextclaw/mcp": "0.1.76",
23
- "@nextclaw/ncp": "0.5.5",
24
- "@nextclaw/ncp-http-agent-server": "0.3.17",
25
- "@nextclaw/openclaw-compat": "1.0.11",
26
- "@nextclaw/runtime": "0.2.43"
21
+ "@nextclaw/core": "0.12.13-beta.1",
22
+ "@nextclaw/mcp": "0.1.78-beta.0",
23
+ "@nextclaw/kernel": "0.1.2-beta.1",
24
+ "@nextclaw/ncp": "0.5.6-beta.0",
25
+ "@nextclaw/runtime": "0.2.45-beta.0",
26
+ "@nextclaw/ncp-http-agent-server": "0.3.18-beta.0",
27
+ "@nextclaw/openclaw-compat": "1.0.13-beta.0"
27
28
  },
28
29
  "devDependencies": {
29
30
  "@types/node": "^20.17.6",