@nextclaw/server 0.10.55 → 0.10.57

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
@@ -6,6 +6,29 @@ import { NcpHttpAgentStreamProvider } from '@nextclaw/ncp-http-agent-server';
6
6
  import { IncomingMessage } from 'node:http';
7
7
  import { Hono } from 'hono';
8
8
 
9
+ type UiNcpStoredAttachmentRecord = {
10
+ id: string;
11
+ uri: string;
12
+ storageKey: string;
13
+ originalName: string;
14
+ storedName: string;
15
+ mimeType: string;
16
+ sizeBytes: number;
17
+ createdAt: string;
18
+ sha256: string;
19
+ };
20
+ type UiNcpAttachmentView = {
21
+ id: string;
22
+ name: string;
23
+ mimeType: string;
24
+ sizeBytes: number;
25
+ attachmentUri: string;
26
+ url: string;
27
+ };
28
+ type UiNcpAttachmentUploadView = {
29
+ attachments: UiNcpAttachmentView[];
30
+ };
31
+
9
32
  type MarketplaceItemType = "plugin" | "skill" | "mcp";
10
33
  type MarketplaceSort = "relevance" | "updated";
11
34
  type MarketplacePluginInstallKind = "npm";
@@ -327,6 +350,7 @@ type UiRouterOptions = {
327
350
  configPath: string;
328
351
  productVersion?: string;
329
352
  publish: (event: UiServerEvent) => void;
353
+ applyLiveConfigReload?: () => Promise<void>;
330
354
  marketplace?: MarketplaceApiConfig;
331
355
  cronService?: InstanceType<typeof NextclawCore.CronService>;
332
356
  chatRuntime?: UiChatRuntime;
@@ -942,6 +966,16 @@ type UiNcpAgent = {
942
966
  streamProvider?: NcpHttpAgentStreamProvider;
943
967
  sessionApi?: NcpSessionApi;
944
968
  listSessionTypes?: (params?: SessionTypeDescribeParams) => Promise<ChatSessionTypesView> | ChatSessionTypesView;
969
+ attachmentApi?: {
970
+ saveAttachment: (input: {
971
+ fileName: string;
972
+ mimeType?: string | null;
973
+ bytes: Uint8Array;
974
+ createdAt?: Date;
975
+ }) => Promise<UiNcpStoredAttachmentRecord>;
976
+ statAttachment: (uri: string) => Promise<UiNcpStoredAttachmentRecord | null> | UiNcpStoredAttachmentRecord | null;
977
+ resolveContentPath: (uri: string) => string | null;
978
+ };
945
979
  basePath?: string;
946
980
  };
947
981
  type ConfigView = {
@@ -1159,7 +1193,10 @@ type UiServerHandle = {
1159
1193
  publish: (event: UiServerEvent) => void;
1160
1194
  };
1161
1195
 
1162
- declare function startUiServer(options: UiServerOptions): UiServerHandle;
1196
+ type UiServerStartOptions = UiServerOptions & {
1197
+ applyLiveConfigReload?: () => Promise<void>;
1198
+ };
1199
+ declare function startUiServer(options: UiServerStartOptions): UiServerHandle;
1163
1200
 
1164
1201
  declare function createUiRouter(options: UiRouterOptions): Hono;
1165
1202
 
@@ -1217,4 +1254,4 @@ declare function getUiBridgeSecretPath(): string;
1217
1254
  declare function readUiBridgeSecret(): string | null;
1218
1255
  declare function ensureUiBridgeSecret(): string;
1219
1256
 
1220
- export { type AgentBindingView, type AgentProfileView, type ApiError, type ApiResponse, type AppMetaView, type AuthEnabledUpdateRequest, type AuthLoginRequest, type AuthPasswordUpdateRequest, type AuthSetupRequest, type AuthStatusView, type BindingPeerView, type BochaFreshnessValue, type ChannelAuthPollRequest, type ChannelAuthPollResult, type ChannelAuthStartRequest, type ChannelAuthStartResult, type ChannelSpecView, type ChatCapabilitiesView, type ChatCommandOptionView, type ChatCommandView, type ChatCommandsView, type ChatRunListView, type ChatRunState, type ChatRunView, type ChatSessionTypeCtaView, 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 MarketplaceMcpContentView, type MarketplaceMcpDoctorResult, type MarketplaceMcpInstallKind, type MarketplaceMcpInstallRequest, type MarketplaceMcpInstallResult, type MarketplaceMcpInstallSpec, type MarketplaceMcpManageAction, type MarketplaceMcpManageRequest, type MarketplaceMcpManageResult, type MarketplaceMcpTemplateInput, 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 RemoteAccessView, type RemoteAccountView, type RemoteBrowserAuthPollRequest, type RemoteBrowserAuthPollResult, type RemoteBrowserAuthStartRequest, type RemoteBrowserAuthStartResult, type RemoteDoctorCheckView, type RemoteDoctorView, type RemoteLoginRequest, type RemoteRuntimeView, type RemoteServiceAction, type RemoteServiceActionResult, type RemoteServiceView, type RemoteSettingsUpdateRequest, type RemoteSettingsView, 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 SessionTypeDescribeParams, type SessionsListView, type UiChatRuntime, type UiNcpAgent, type UiNcpSessionListView, type UiNcpSessionMessagesView, type UiRemoteAccessHost, type UiServerEvent, type UiServerHandle, type UiServerOptions, buildConfigMeta, buildConfigSchemaView, buildConfigView, createCustomProvider, createUiRouter, deleteCustomProvider, deleteSession, ensureUiBridgeSecret, executeConfigAction, getSessionHistory, getUiBridgeSecretPath, listSessions, loadConfigOrDefault, patchSession, readUiBridgeSecret, startUiServer, testProviderConnection, updateChannel, updateModel, updateProvider, updateRuntime, updateSearch, updateSecrets };
1257
+ export { type AgentBindingView, type AgentProfileView, type ApiError, type ApiResponse, type AppMetaView, type AuthEnabledUpdateRequest, type AuthLoginRequest, type AuthPasswordUpdateRequest, type AuthSetupRequest, type AuthStatusView, type BindingPeerView, type BochaFreshnessValue, type ChannelAuthPollRequest, type ChannelAuthPollResult, type ChannelAuthStartRequest, type ChannelAuthStartResult, type ChannelSpecView, type ChatCapabilitiesView, type ChatCommandOptionView, type ChatCommandView, type ChatCommandsView, type ChatRunListView, type ChatRunState, type ChatRunView, type ChatSessionTypeCtaView, 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 MarketplaceMcpContentView, type MarketplaceMcpDoctorResult, type MarketplaceMcpInstallKind, type MarketplaceMcpInstallRequest, type MarketplaceMcpInstallResult, type MarketplaceMcpInstallSpec, type MarketplaceMcpManageAction, type MarketplaceMcpManageRequest, type MarketplaceMcpManageResult, type MarketplaceMcpTemplateInput, 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 RemoteAccessView, type RemoteAccountView, type RemoteBrowserAuthPollRequest, type RemoteBrowserAuthPollResult, type RemoteBrowserAuthStartRequest, type RemoteBrowserAuthStartResult, type RemoteDoctorCheckView, type RemoteDoctorView, type RemoteLoginRequest, type RemoteRuntimeView, type RemoteServiceAction, type RemoteServiceActionResult, type RemoteServiceView, type RemoteSettingsUpdateRequest, type RemoteSettingsView, 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 SessionTypeDescribeParams, type SessionsListView, type UiChatRuntime, type UiNcpAgent, type UiNcpAttachmentUploadView, type UiNcpAttachmentView, type UiNcpSessionListView, type UiNcpSessionMessagesView, type UiNcpStoredAttachmentRecord, type UiRemoteAccessHost, type UiServerEvent, type UiServerHandle, type 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
@@ -4,7 +4,7 @@ import { compress } from "hono/compress";
4
4
  import { serve } from "@hono/node-server";
5
5
  import { WebSocketServer, WebSocket } from "ws";
6
6
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
7
- import { readFile as readFile2, stat } from "fs/promises";
7
+ import { readFile as readFile3, stat } from "fs/promises";
8
8
  import { join as join2 } from "path";
9
9
 
10
10
  // src/ui/auth.service.ts
@@ -836,6 +836,7 @@ var PREFERRED_PROVIDER_ORDER = [
836
836
  "anthropic",
837
837
  "gemini",
838
838
  "openrouter",
839
+ "dashscope-coding-plan",
839
840
  "dashscope",
840
841
  "deepseek",
841
842
  "minimax",
@@ -3240,6 +3241,12 @@ var ConfigRoutesController = class {
3240
3241
  pluginUiMetadata: this.options.getPluginUiMetadata?.() ?? []
3241
3242
  };
3242
3243
  }
3244
+ async publishConfigUpdates(paths) {
3245
+ for (const path of paths) {
3246
+ this.options.publish({ type: "config.updated", payload: { path } });
3247
+ }
3248
+ await this.options.applyLiveConfigReload?.();
3249
+ }
3243
3250
  getConfig = (c) => {
3244
3251
  const config = loadConfigOrDefault(this.options.configPath);
3245
3252
  return c.json(ok(buildConfigView(config, this.getPluginConfigOptions())));
@@ -3265,12 +3272,14 @@ var ConfigRoutesController = class {
3265
3272
  model: body.data.model,
3266
3273
  workspace: body.data.workspace
3267
3274
  });
3275
+ const changedPaths = [];
3268
3276
  if (hasModel) {
3269
- this.options.publish({ type: "config.updated", payload: { path: "agents.defaults.model" } });
3277
+ changedPaths.push("agents.defaults.model");
3270
3278
  }
3271
3279
  if (typeof body.data.workspace === "string") {
3272
- this.options.publish({ type: "config.updated", payload: { path: "agents.defaults.workspace" } });
3280
+ changedPaths.push("agents.defaults.workspace");
3273
3281
  }
3282
+ await this.publishConfigUpdates(changedPaths);
3274
3283
  return c.json(ok({
3275
3284
  model: view.agents.defaults.model,
3276
3285
  workspace: view.agents.defaults.workspace
@@ -3282,7 +3291,7 @@ var ConfigRoutesController = class {
3282
3291
  return c.json(err("INVALID_BODY", "invalid json body"), 400);
3283
3292
  }
3284
3293
  const result = updateSearch(this.options.configPath, body.data);
3285
- this.options.publish({ type: "config.updated", payload: { path: "search" } });
3294
+ await this.publishConfigUpdates(["search"]);
3286
3295
  return c.json(ok(result));
3287
3296
  };
3288
3297
  updateProvider = async (c) => {
@@ -3295,7 +3304,7 @@ var ConfigRoutesController = class {
3295
3304
  if (!result) {
3296
3305
  return c.json(err("NOT_FOUND", `unknown provider: ${provider}`), 404);
3297
3306
  }
3298
- this.options.publish({ type: "config.updated", payload: { path: `providers.${provider}` } });
3307
+ await this.publishConfigUpdates([`providers.${provider}`]);
3299
3308
  return c.json(ok(result));
3300
3309
  };
3301
3310
  createProvider = async (c) => {
@@ -3307,7 +3316,7 @@ var ConfigRoutesController = class {
3307
3316
  this.options.configPath,
3308
3317
  body.data
3309
3318
  );
3310
- this.options.publish({ type: "config.updated", payload: { path: `providers.${result.name}` } });
3319
+ await this.publishConfigUpdates([`providers.${result.name}`]);
3311
3320
  return c.json(ok({
3312
3321
  name: result.name,
3313
3322
  provider: result.provider
@@ -3319,7 +3328,7 @@ var ConfigRoutesController = class {
3319
3328
  if (result === null) {
3320
3329
  return c.json(err("NOT_FOUND", `custom provider not found: ${provider}`), 404);
3321
3330
  }
3322
- this.options.publish({ type: "config.updated", payload: { path: `providers.${provider}` } });
3331
+ await this.publishConfigUpdates([`providers.${provider}`]);
3323
3332
  return c.json(ok({
3324
3333
  deleted: true,
3325
3334
  provider
@@ -3385,7 +3394,7 @@ var ConfigRoutesController = class {
3385
3394
  return c.json(err("NOT_FOUND", "provider auth session not found"), 404);
3386
3395
  }
3387
3396
  if (result.status === "authorized") {
3388
- this.options.publish({ type: "config.updated", payload: { path: `providers.${provider}` } });
3397
+ await this.publishConfigUpdates([`providers.${provider}`]);
3389
3398
  }
3390
3399
  return c.json(ok(result));
3391
3400
  };
@@ -3396,7 +3405,7 @@ var ConfigRoutesController = class {
3396
3405
  if (!result) {
3397
3406
  return c.json(err("NOT_SUPPORTED", `provider cli auth import is not supported: ${provider}`), 404);
3398
3407
  }
3399
- this.options.publish({ type: "config.updated", payload: { path: `providers.${provider}` } });
3408
+ await this.publishConfigUpdates([`providers.${provider}`]);
3400
3409
  return c.json(ok(result));
3401
3410
  } catch (error) {
3402
3411
  const message = error instanceof Error ? error.message : String(error);
@@ -3413,7 +3422,7 @@ var ConfigRoutesController = class {
3413
3422
  if (!result) {
3414
3423
  return c.json(err("NOT_FOUND", `unknown channel: ${channel}`), 404);
3415
3424
  }
3416
- this.options.publish({ type: "config.updated", payload: { path: `channels.${channel}` } });
3425
+ await this.publishConfigUpdates([`channels.${channel}`]);
3417
3426
  return c.json(ok(result));
3418
3427
  };
3419
3428
  startChannelAuth = async (c) => {
@@ -3466,7 +3475,7 @@ var ConfigRoutesController = class {
3466
3475
  return c.json(err("NOT_FOUND", "channel auth session not found"), 404);
3467
3476
  }
3468
3477
  if (result.status === "authorized") {
3469
- this.options.publish({ type: "config.updated", payload: { path: `channels.${channel}` } });
3478
+ await this.publishConfigUpdates([`channels.${channel}`]);
3470
3479
  }
3471
3480
  return c.json(ok(result));
3472
3481
  };
@@ -3476,7 +3485,7 @@ var ConfigRoutesController = class {
3476
3485
  return c.json(err("INVALID_BODY", "invalid json body"), 400);
3477
3486
  }
3478
3487
  const result = updateSecrets(this.options.configPath, body.data);
3479
- this.options.publish({ type: "config.updated", payload: { path: "secrets" } });
3488
+ await this.publishConfigUpdates(["secrets"]);
3480
3489
  return c.json(ok(result));
3481
3490
  };
3482
3491
  updateRuntime = async (c) => {
@@ -3485,18 +3494,18 @@ var ConfigRoutesController = class {
3485
3494
  return c.json(err("INVALID_BODY", "invalid json body"), 400);
3486
3495
  }
3487
3496
  const result = updateRuntime(this.options.configPath, body.data);
3497
+ const changedPaths = [];
3488
3498
  if (body.data.agents?.defaults && Object.prototype.hasOwnProperty.call(body.data.agents.defaults, "contextTokens")) {
3489
- this.options.publish({ type: "config.updated", payload: { path: "agents.defaults.contextTokens" } });
3499
+ changedPaths.push("agents.defaults.contextTokens");
3490
3500
  }
3491
3501
  if (body.data.agents?.defaults && Object.prototype.hasOwnProperty.call(body.data.agents.defaults, "engine")) {
3492
- this.options.publish({ type: "config.updated", payload: { path: "agents.defaults.engine" } });
3502
+ changedPaths.push("agents.defaults.engine");
3493
3503
  }
3494
3504
  if (body.data.agents?.defaults && Object.prototype.hasOwnProperty.call(body.data.agents.defaults, "engineConfig")) {
3495
- this.options.publish({ type: "config.updated", payload: { path: "agents.defaults.engineConfig" } });
3505
+ changedPaths.push("agents.defaults.engineConfig");
3496
3506
  }
3497
- this.options.publish({ type: "config.updated", payload: { path: "agents.list" } });
3498
- this.options.publish({ type: "config.updated", payload: { path: "bindings" } });
3499
- this.options.publish({ type: "config.updated", payload: { path: "session" } });
3507
+ changedPaths.push("agents.list", "bindings", "session");
3508
+ await this.publishConfigUpdates(changedPaths);
3500
3509
  return c.json(ok(result));
3501
3510
  };
3502
3511
  executeAction = async (c) => {
@@ -3612,6 +3621,87 @@ var CronRoutesController = class {
3612
3621
  };
3613
3622
  };
3614
3623
 
3624
+ // src/ui/router/ncp-attachment.controller.ts
3625
+ import { readFile as readFile2 } from "fs/promises";
3626
+ var ATTACHMENT_CONTENT_BASE_PATH = "/api/ncp/attachments/content";
3627
+ function buildAttachmentContentUrl(attachmentUri) {
3628
+ const query = new URLSearchParams({ uri: attachmentUri });
3629
+ return `${ATTACHMENT_CONTENT_BASE_PATH}?${query.toString()}`;
3630
+ }
3631
+ function encodeContentDispositionFileName(fileName) {
3632
+ return encodeURIComponent(fileName).replace(/\*/g, "%2A");
3633
+ }
3634
+ function toAttachmentView(record) {
3635
+ return {
3636
+ id: record.id,
3637
+ name: record.originalName,
3638
+ mimeType: record.mimeType,
3639
+ sizeBytes: record.sizeBytes,
3640
+ attachmentUri: record.uri,
3641
+ url: buildAttachmentContentUrl(record.uri)
3642
+ };
3643
+ }
3644
+ var NcpAttachmentRoutesController = class {
3645
+ constructor(options) {
3646
+ this.options = options;
3647
+ }
3648
+ uploadAttachments = async (c) => {
3649
+ const attachmentApi = this.options.ncpAgent?.attachmentApi;
3650
+ if (!attachmentApi) {
3651
+ return c.json(err("NOT_AVAILABLE", "ncp attachment api unavailable"), 503);
3652
+ }
3653
+ let formData;
3654
+ try {
3655
+ formData = await c.req.raw.formData();
3656
+ } catch {
3657
+ return c.json(err("INVALID_BODY", "invalid multipart body"), 400);
3658
+ }
3659
+ const files = Array.from(formData.values()).reduce((result, value) => {
3660
+ if (typeof value !== "string") {
3661
+ result.push(value);
3662
+ }
3663
+ return result;
3664
+ }, []);
3665
+ if (files.length === 0) {
3666
+ return c.json(err("INVALID_BODY", "no files provided"), 400);
3667
+ }
3668
+ const attachments = [];
3669
+ for (const file of files) {
3670
+ const record = await attachmentApi.saveAttachment({
3671
+ fileName: file.name,
3672
+ mimeType: file.type || null,
3673
+ bytes: new Uint8Array(await file.arrayBuffer())
3674
+ });
3675
+ attachments.push(toAttachmentView(record));
3676
+ }
3677
+ const payload = { attachments };
3678
+ return c.json(ok(payload));
3679
+ };
3680
+ getAttachmentContent = async (c) => {
3681
+ const attachmentApi = this.options.ncpAgent?.attachmentApi;
3682
+ if (!attachmentApi) {
3683
+ return c.json(err("NOT_AVAILABLE", "ncp attachment api unavailable"), 503);
3684
+ }
3685
+ const uri = c.req.query("uri")?.trim();
3686
+ if (!uri) {
3687
+ return c.json(err("INVALID_URI", "attachment uri is required"), 400);
3688
+ }
3689
+ const record = await attachmentApi.statAttachment(uri);
3690
+ const contentPath = attachmentApi.resolveContentPath(uri);
3691
+ if (!record || !contentPath) {
3692
+ return c.json(err("NOT_FOUND", `attachment not found: ${uri}`), 404);
3693
+ }
3694
+ const body = await readFile2(contentPath);
3695
+ return new Response(body, {
3696
+ headers: {
3697
+ "content-length": String(body.byteLength),
3698
+ "content-type": record.mimeType || "application/octet-stream",
3699
+ "content-disposition": `inline; filename*=UTF-8''${encodeContentDispositionFileName(record.originalName)}`
3700
+ }
3701
+ });
3702
+ };
3703
+ };
3704
+
3615
3705
  // src/ui/router/ncp-session.controller.ts
3616
3706
  function readPositiveInt(value) {
3617
3707
  if (typeof value !== "string") {
@@ -5388,7 +5478,7 @@ function registerSessionRoutes(app, sessionController) {
5388
5478
  app.put("/api/sessions/:key", sessionController.patchSession);
5389
5479
  app.delete("/api/sessions/:key", sessionController.deleteSession);
5390
5480
  }
5391
- function registerNcpRoutes(app, options, ncpSessionController) {
5481
+ function registerNcpRoutes(app, options, ncpSessionController, ncpAttachmentController) {
5392
5482
  if (!options.ncpAgent) {
5393
5483
  return;
5394
5484
  }
@@ -5403,6 +5493,8 @@ function registerNcpRoutes(app, options, ncpSessionController) {
5403
5493
  app.put("/api/ncp/sessions/:sessionId", ncpSessionController.patchSession);
5404
5494
  app.get("/api/ncp/sessions/:sessionId/messages", ncpSessionController.listSessionMessages);
5405
5495
  app.delete("/api/ncp/sessions/:sessionId", ncpSessionController.deleteSession);
5496
+ app.post("/api/ncp/attachments", ncpAttachmentController.uploadAttachments);
5497
+ app.get("/api/ncp/attachments/content", ncpAttachmentController.getAttachmentContent);
5406
5498
  }
5407
5499
  function registerCronRoutes(app, cronController) {
5408
5500
  app.get("/api/cron", cronController.listJobs);
@@ -5434,6 +5526,7 @@ function createUiRouter(options) {
5434
5526
  const sessionController = new SessionRoutesController(options);
5435
5527
  const cronController = new CronRoutesController(options);
5436
5528
  const ncpSessionController = new NcpSessionRoutesController(options);
5529
+ const ncpAttachmentController = new NcpAttachmentRoutesController(options);
5437
5530
  const remoteController = options.remoteAccess ? new RemoteRoutesController(options.remoteAccess) : null;
5438
5531
  const pluginMarketplaceController = new PluginMarketplaceController(options, marketplaceBaseUrl);
5439
5532
  const skillMarketplaceController = new SkillMarketplaceController(options, marketplaceBaseUrl);
@@ -5458,7 +5551,7 @@ function createUiRouter(options) {
5458
5551
  registerConfigRoutes(app, configController);
5459
5552
  registerChatRoutes(app, chatController);
5460
5553
  registerSessionRoutes(app, sessionController);
5461
- registerNcpRoutes(app, options, ncpSessionController);
5554
+ registerNcpRoutes(app, options, ncpSessionController, ncpAttachmentController);
5462
5555
  registerCronRoutes(app, cronController);
5463
5556
  registerRemoteRoutes(app, remoteController);
5464
5557
  mountMarketplaceRoutes(app, {
@@ -5564,6 +5657,7 @@ function startUiServer(options) {
5564
5657
  configPath: options.configPath,
5565
5658
  productVersion: options.productVersion,
5566
5659
  publish,
5660
+ applyLiveConfigReload: options.applyLiveConfigReload,
5567
5661
  marketplace: options.marketplace,
5568
5662
  cronService: options.cronService,
5569
5663
  chatRuntime: options.chatRuntime,
@@ -5584,7 +5678,7 @@ function startUiServer(options) {
5584
5678
  join: join2,
5585
5679
  getContent: async (path) => {
5586
5680
  try {
5587
- return await readFile2(path);
5681
+ return await readFile3(path);
5588
5682
  } catch {
5589
5683
  return null;
5590
5684
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/server",
3
- "version": "0.10.55",
3
+ "version": "0.10.57",
4
4
  "private": false,
5
5
  "description": "Nextclaw UI/API server.",
6
6
  "type": "module",
@@ -18,12 +18,12 @@
18
18
  "@hono/node-server": "^1.13.3",
19
19
  "hono": "^4.6.2",
20
20
  "ws": "^8.18.0",
21
- "@nextclaw/mcp": "0.1.50",
22
- "@nextclaw/ncp": "0.3.2",
23
- "@nextclaw/ncp-http-agent-server": "0.3.2",
24
- "@nextclaw/runtime": "0.2.15",
25
- "@nextclaw/openclaw-compat": "0.3.31",
26
- "@nextclaw/core": "0.11.1"
21
+ "@nextclaw/mcp": "0.1.51",
22
+ "@nextclaw/ncp-http-agent-server": "0.3.3",
23
+ "@nextclaw/ncp": "0.3.3",
24
+ "@nextclaw/runtime": "0.2.16",
25
+ "@nextclaw/core": "0.11.2",
26
+ "@nextclaw/openclaw-compat": "0.3.33"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/node": "^20.17.6",