@nextclaw/server 0.12.6 → 0.12.7

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
@@ -798,6 +798,12 @@ type AgentBindingView = {
798
798
  type SessionConfigView = {
799
799
  dmScope?: "main" | "per-peer" | "per-channel-peer" | "per-account-channel-peer";
800
800
  };
801
+ type RuntimeEntryView = {
802
+ enabled?: boolean;
803
+ label?: string;
804
+ type: string;
805
+ config?: Record<string, unknown>;
806
+ };
801
807
  type SessionEntryView = {
802
808
  key: string;
803
809
  createdAt: string;
@@ -882,6 +888,15 @@ type ServerPathBrowseView = {
882
888
  breadcrumbs: ServerPathBreadcrumbView[];
883
889
  entries: ServerPathEntryView[];
884
890
  };
891
+ type ServerPathReadView = {
892
+ requestedPath: string;
893
+ resolvedPath: string;
894
+ kind: "text" | "markdown" | "binary";
895
+ sizeBytes: number;
896
+ truncated: boolean;
897
+ text?: string;
898
+ languageHint?: string | null;
899
+ };
885
900
  type CronScheduleView = {
886
901
  kind: "at";
887
902
  atMs?: number | null;
@@ -952,6 +967,9 @@ type RuntimeConfigUpdate = {
952
967
  engine?: string;
953
968
  engineConfig?: Record<string, unknown>;
954
969
  };
970
+ runtimes?: {
971
+ entries?: Record<string, RuntimeEntryView> | null;
972
+ };
955
973
  list?: AgentProfileView[];
956
974
  };
957
975
  bindings?: AgentBindingView[];
@@ -1040,6 +1058,9 @@ type ConfigView = {
1040
1058
  contextTokens?: number;
1041
1059
  maxToolIterations?: number;
1042
1060
  };
1061
+ runtimes?: {
1062
+ entries?: Record<string, RuntimeEntryView>;
1063
+ };
1043
1064
  list?: AgentProfileView[];
1044
1065
  context?: {
1045
1066
  bootstrap?: {
@@ -1327,4 +1348,4 @@ declare function getUiBridgeSecretPath(): string;
1327
1348
  declare function readUiBridgeSecret(): string | null;
1328
1349
  declare function ensureUiBridgeSecret(): string;
1329
1350
  //#endregion
1330
- 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, RuntimeLifecycleState, RuntimeServiceState, SearchConfigUpdate, SearchConfigView, SearchProviderConfigView, SearchProviderName, SearchProviderSpecView, SecretProviderEnvView, SecretProviderExecView, SecretProviderFileView, SecretProviderView, SecretRefView, SecretSourceView, SecretsConfigUpdate, SecretsView, ServerPathBreadcrumbView, ServerPathBrowseView, ServerPathEntryView, 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 };
1351
+ 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, 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 };
package/dist/index.js CHANGED
@@ -3,8 +3,8 @@ import { compress } from "hono/compress";
3
3
  import { serve } from "@hono/node-server";
4
4
  import { WebSocket, WebSocketServer } from "ws";
5
5
  import fs, { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
6
- import { readFile, readdir, realpath, stat } from "node:fs/promises";
7
- import path, { dirname, isAbsolute, join, parse, resolve } from "node:path";
6
+ import { open, readFile, readdir, realpath, stat } from "node:fs/promises";
7
+ import path, { dirname, extname, isAbsolute, join, parse, resolve } from "node:path";
8
8
  import * as NextclawCore from "@nextclaw/core";
9
9
  import { ConfigSchema, DEFAULT_WORKSPACE_PATH, LiteLLMProvider, SessionManager, buildConfigSchema, createAgentProfile, expandHome, findEffectiveAgentProfile, getDataDir, getPackageVersion, getProviderName, getWorkspacePathFromConfig, hasSecretRef, isSensitiveConfigPath, loadConfig, normalizeThinkingLevels, parseThinkingLevel, probeFeishu, readAgentAvatarContent, removeAgentProfile, resolveEffectiveAgentProfiles, saveConfig, updateAgentProfile } from "@nextclaw/core";
10
10
  import { createHash, randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
@@ -1331,7 +1331,7 @@ function buildConfigView(config, options) {
1331
1331
  const providers = {};
1332
1332
  for (const [name, provider] of Object.entries(config.providers)) providers[name] = toProviderView(config, provider, name, uiHints, findServerBuiltinProviderByName(name));
1333
1333
  return {
1334
- agents: config.agents,
1334
+ agents: sanitizePublicConfigValue(config.agents, "agents", uiHints),
1335
1335
  providers,
1336
1336
  search: buildSearchView(config),
1337
1337
  channels: sanitizePublicConfigValue(projectedChannels, "channels", uiHints),
@@ -1348,6 +1348,40 @@ function buildConfigView(config, options) {
1348
1348
  }
1349
1349
  };
1350
1350
  }
1351
+ function normalizeRuntimeEntries(entries) {
1352
+ if (!entries || typeof entries !== "object" || Array.isArray(entries)) return {};
1353
+ const normalized = {};
1354
+ for (const [rawId, rawEntry] of Object.entries(entries)) {
1355
+ const id = rawId.trim();
1356
+ if (!id || !rawEntry || typeof rawEntry !== "object" || Array.isArray(rawEntry)) continue;
1357
+ const entry = rawEntry;
1358
+ const type = normalizeOptionalString(entry.type);
1359
+ if (!type) continue;
1360
+ normalized[id] = {
1361
+ enabled: typeof entry.enabled === "boolean" ? entry.enabled : true,
1362
+ ...normalizeOptionalString(entry.label) ? { label: normalizeOptionalString(entry.label) ?? void 0 } : {},
1363
+ type,
1364
+ config: normalizeRuntimeEntryConfig(type, entry.config && typeof entry.config === "object" && !Array.isArray(entry.config) ? entry.config : {})
1365
+ };
1366
+ }
1367
+ return normalized;
1368
+ }
1369
+ function normalizeRuntimeEntryConfig(type, config) {
1370
+ if (type !== "narp-stdio") return { ...config };
1371
+ const command = normalizeOptionalString(config.command);
1372
+ const cwd = normalizeOptionalString(config.cwd);
1373
+ return {
1374
+ wireDialect: normalizeOptionalString(config.wireDialect) ?? "acp",
1375
+ processScope: normalizeOptionalString(config.processScope) ?? "per-session",
1376
+ ...command ? { command } : {},
1377
+ ...normalizeStringArray(config.args) ? { args: normalizeStringArray(config.args) } : {},
1378
+ env: normalizeUnknownStringRecord(config.env) ?? {},
1379
+ ...cwd ? { cwd } : {},
1380
+ startupTimeoutMs: normalizePositiveInteger(config.startupTimeoutMs) ?? 8e3,
1381
+ probeTimeoutMs: normalizePositiveInteger(config.probeTimeoutMs) ?? 3e3,
1382
+ requestTimeoutMs: normalizePositiveInteger(config.requestTimeoutMs) ?? 12e4
1383
+ };
1384
+ }
1351
1385
  function clearSecretRef(config, path) {
1352
1386
  if (config.secrets.refs[path]) delete config.secrets.refs[path];
1353
1387
  }
@@ -1545,6 +1579,22 @@ function normalizeOptionalString(value) {
1545
1579
  const trimmed = value.trim();
1546
1580
  return trimmed.length > 0 ? trimmed : null;
1547
1581
  }
1582
+ function normalizePositiveInteger(value) {
1583
+ const parsed = typeof value === "number" ? value : typeof value === "string" ? Number.parseInt(value, 10) : NaN;
1584
+ if (!Number.isFinite(parsed)) return null;
1585
+ const normalized = Math.trunc(parsed);
1586
+ return normalized > 0 ? normalized : null;
1587
+ }
1588
+ function normalizeStringArray(value) {
1589
+ if (!Array.isArray(value)) return null;
1590
+ const entries = value.map((entry) => normalizeOptionalString(entry)).filter((entry) => Boolean(entry));
1591
+ return entries.length > 0 ? entries : null;
1592
+ }
1593
+ function normalizeUnknownStringRecord(value) {
1594
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
1595
+ const entries = Object.entries(value).map(([key, entryValue]) => [key.trim(), normalizeOptionalString(entryValue)]).filter(([key, entryValue]) => key.length > 0 && Boolean(entryValue));
1596
+ return entries.length > 0 ? Object.fromEntries(entries) : null;
1597
+ }
1548
1598
  function normalizeHeaders(input) {
1549
1599
  if (!input) return null;
1550
1600
  const entries = Object.entries(input).map(([key, value]) => [key.trim(), String(value ?? "").trim()]).filter(([key, value]) => key.length > 0 && value.length > 0);
@@ -1858,6 +1908,7 @@ function updateRuntime(configPath, patch) {
1858
1908
  ...hasEngineConfig ? { engineConfig: { ...entry.engineConfig } } : {}
1859
1909
  };
1860
1910
  });
1911
+ if (patch.agents?.runtimes && Object.prototype.hasOwnProperty.call(patch.agents.runtimes, "entries")) config.agents.runtimes.entries = normalizeRuntimeEntries(patch.agents.runtimes.entries);
1861
1912
  if (Object.prototype.hasOwnProperty.call(patch, "bindings")) config.bindings = patch.bindings ?? [];
1862
1913
  if (patch.session) config.session = {
1863
1914
  ...config.session,
@@ -2591,6 +2642,7 @@ var ConfigRoutesController = class {
2591
2642
  if (body.data.agents?.defaults && Object.prototype.hasOwnProperty.call(body.data.agents.defaults, "contextTokens")) changedPaths.push("agents.defaults.contextTokens");
2592
2643
  if (body.data.agents?.defaults && Object.prototype.hasOwnProperty.call(body.data.agents.defaults, "engine")) changedPaths.push("agents.defaults.engine");
2593
2644
  if (body.data.agents?.defaults && Object.prototype.hasOwnProperty.call(body.data.agents.defaults, "engineConfig")) changedPaths.push("agents.defaults.engineConfig");
2645
+ if (body.data.agents?.runtimes && Object.prototype.hasOwnProperty.call(body.data.agents.runtimes, "entries")) changedPaths.push("agents.runtimes.entries");
2594
2646
  changedPaths.push("agents.list", "bindings", "session");
2595
2647
  await this.publishConfigUpdates(changedPaths);
2596
2648
  return c.json(ok(result));
@@ -4476,6 +4528,139 @@ function isServerPathBrowseError(error) {
4476
4528
  return error instanceof ServerPathBrowseError;
4477
4529
  }
4478
4530
  //#endregion
4531
+ //#region src/ui/server-path/server-path-read.utils.ts
4532
+ const DEFAULT_PREVIEW_MAX_BYTES = 2e5;
4533
+ const MARKDOWN_EXTENSIONS = new Set([
4534
+ ".md",
4535
+ ".mdx",
4536
+ ".markdown"
4537
+ ]);
4538
+ const TEXT_EXTENSIONS = new Set([
4539
+ ".c",
4540
+ ".cc",
4541
+ ".conf",
4542
+ ".cpp",
4543
+ ".css",
4544
+ ".csv",
4545
+ ".env",
4546
+ ".go",
4547
+ ".graphql",
4548
+ ".h",
4549
+ ".hpp",
4550
+ ".html",
4551
+ ".ini",
4552
+ ".java",
4553
+ ".js",
4554
+ ".json",
4555
+ ".jsx",
4556
+ ".log",
4557
+ ".mjs",
4558
+ ".py",
4559
+ ".rb",
4560
+ ".rs",
4561
+ ".sh",
4562
+ ".sql",
4563
+ ".svg",
4564
+ ".toml",
4565
+ ".ts",
4566
+ ".tsx",
4567
+ ".txt",
4568
+ ".xml",
4569
+ ".yaml",
4570
+ ".yml"
4571
+ ]);
4572
+ var ServerPathReadError = class extends Error {
4573
+ constructor(code, message) {
4574
+ super(message);
4575
+ this.code = code;
4576
+ this.name = "ServerPathReadError";
4577
+ }
4578
+ };
4579
+ function normalizeReadPath(params) {
4580
+ const { basePath, path } = params;
4581
+ const rawPath = typeof path === "string" ? path.trim() : "";
4582
+ const normalizedBasePath = typeof basePath === "string" ? basePath.trim() : "";
4583
+ if (!rawPath) return resolve(expandHome(normalizedBasePath || homedir()));
4584
+ const expandedPath = expandHome(rawPath);
4585
+ if (expandedPath.startsWith("/")) return resolve(expandedPath);
4586
+ if (!normalizedBasePath) throw new ServerPathReadError("SERVER_PATH_BASE_REQUIRED", "relative server path requires a base path");
4587
+ return resolve(expandHome(normalizedBasePath), expandedPath);
4588
+ }
4589
+ function inferPreviewKind(path) {
4590
+ const extension = extname(path).toLowerCase();
4591
+ if (MARKDOWN_EXTENSIONS.has(extension)) return "markdown";
4592
+ if (TEXT_EXTENSIONS.has(extension)) return "text";
4593
+ return "binary";
4594
+ }
4595
+ function isLikelyTextBuffer(buffer) {
4596
+ for (const byte of buffer) if (byte === 0) return false;
4597
+ return true;
4598
+ }
4599
+ function readLanguageHint(path) {
4600
+ const extension = extname(path).toLowerCase();
4601
+ if (!extension) return null;
4602
+ if (MARKDOWN_EXTENSIONS.has(extension)) return "markdown";
4603
+ return extension.slice(1) || null;
4604
+ }
4605
+ async function readFilePreviewBytes(params) {
4606
+ const { maxBytes, resolvedPath, sizeBytes } = params;
4607
+ const bytesToRead = Math.min(sizeBytes, maxBytes);
4608
+ const fileHandle = await open(resolvedPath, "r");
4609
+ try {
4610
+ const buffer = Buffer.alloc(bytesToRead);
4611
+ const { bytesRead } = await fileHandle.read(buffer, 0, bytesToRead, 0);
4612
+ return {
4613
+ buffer: buffer.subarray(0, bytesRead),
4614
+ truncated: sizeBytes > maxBytes
4615
+ };
4616
+ } finally {
4617
+ await fileHandle.close();
4618
+ }
4619
+ }
4620
+ async function readServerPath(options = {}) {
4621
+ const requestedPath = typeof options.path === "string" ? options.path.trim() : "";
4622
+ const resolvedPath = normalizeReadPath(options);
4623
+ let resolvedStats;
4624
+ try {
4625
+ resolvedStats = await stat(resolvedPath);
4626
+ } catch {
4627
+ throw new ServerPathReadError("SERVER_PATH_NOT_FOUND", "server path does not exist");
4628
+ }
4629
+ if (!resolvedStats.isFile()) throw new ServerPathReadError("SERVER_PATH_NOT_FILE", "server path must point to a file");
4630
+ let previewBytes;
4631
+ try {
4632
+ previewBytes = await readFilePreviewBytes({
4633
+ resolvedPath,
4634
+ sizeBytes: resolvedStats.size,
4635
+ maxBytes: options.maxBytes ?? DEFAULT_PREVIEW_MAX_BYTES
4636
+ });
4637
+ } catch {
4638
+ throw new ServerPathReadError("SERVER_PATH_NOT_READABLE", "server path is not readable");
4639
+ }
4640
+ const inferredKind = inferPreviewKind(resolvedPath);
4641
+ const resolvedKind = inferredKind === "binary" && isLikelyTextBuffer(previewBytes.buffer) ? "text" : inferredKind;
4642
+ if (resolvedKind === "binary") return {
4643
+ requestedPath: requestedPath || resolvedPath,
4644
+ resolvedPath,
4645
+ kind: "binary",
4646
+ sizeBytes: resolvedStats.size,
4647
+ truncated: previewBytes.truncated,
4648
+ languageHint: readLanguageHint(resolvedPath)
4649
+ };
4650
+ return {
4651
+ requestedPath: requestedPath || resolvedPath,
4652
+ resolvedPath,
4653
+ kind: resolvedKind,
4654
+ sizeBytes: resolvedStats.size,
4655
+ truncated: previewBytes.truncated,
4656
+ text: previewBytes.buffer.toString("utf8"),
4657
+ languageHint: readLanguageHint(resolvedPath)
4658
+ };
4659
+ }
4660
+ function isServerPathReadError(error) {
4661
+ return error instanceof ServerPathReadError;
4662
+ }
4663
+ //#endregion
4479
4664
  //#region src/ui/ui-routes/server-path.controller.ts
4480
4665
  function readIncludeFilesFlag(value) {
4481
4666
  return value === "1" || value === "true";
@@ -4493,6 +4678,18 @@ var ServerPathRoutesController = class {
4493
4678
  throw error;
4494
4679
  }
4495
4680
  };
4681
+ read = async (c) => {
4682
+ try {
4683
+ const payload = await readServerPath({
4684
+ path: c.req.query("path"),
4685
+ basePath: c.req.query("basePath")
4686
+ });
4687
+ return c.json(ok(payload));
4688
+ } catch (error) {
4689
+ if (isServerPathReadError(error)) return c.json(err(error.code, error.message), 400);
4690
+ throw error;
4691
+ }
4692
+ };
4496
4693
  };
4497
4694
  //#endregion
4498
4695
  //#region src/ui/router.ts
@@ -4543,6 +4740,7 @@ function registerNcpSessionRoutes(app, ncpSessionController) {
4543
4740
  }
4544
4741
  function registerServerPathRoutes(app, serverPathController) {
4545
4742
  app.get("/api/server-paths/browse", serverPathController.browse);
4743
+ app.get("/api/server-paths/read", serverPathController.read);
4546
4744
  }
4547
4745
  function registerNcpRuntimeRoutes(app, options, ncpAssetController) {
4548
4746
  if (!options.ncpAgent) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/server",
3
- "version": "0.12.6",
3
+ "version": "0.12.7",
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/core": "0.12.6",
22
- "@nextclaw/mcp": "0.1.71",
23
- "@nextclaw/ncp-http-agent-server": "0.3.13",
24
- "@nextclaw/openclaw-compat": "1.0.6",
25
- "@nextclaw/ncp": "0.5.1",
26
- "@nextclaw/runtime": "0.2.38"
21
+ "@nextclaw/core": "0.12.7",
22
+ "@nextclaw/openclaw-compat": "1.0.7",
23
+ "@nextclaw/ncp": "0.5.2",
24
+ "@nextclaw/ncp-http-agent-server": "0.3.14",
25
+ "@nextclaw/mcp": "0.1.72",
26
+ "@nextclaw/runtime": "0.2.39"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/node": "^20.17.6",