@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 +22 -1
- package/dist/index.js +201 -3
- package/package.json +7 -7
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.
|
|
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.
|
|
22
|
-
"@nextclaw/
|
|
23
|
-
"@nextclaw/ncp
|
|
24
|
-
"@nextclaw/
|
|
25
|
-
"@nextclaw/
|
|
26
|
-
"@nextclaw/runtime": "0.2.
|
|
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",
|