@nextclaw/server 0.15.23 → 0.15.25

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.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { Hono } from "hono";
2
2
  import { compress } from "hono/compress";
3
3
  import { serve } from "@hono/node-server";
4
- import { AccessManager, PanelAppError, injectUiContentParamsBootstrap, isInboxDeliveryError, isPanelAppError, isPreferenceError, isProjectError, isServiceAppError, isSessionContextCompactionError, isSessionMessageCursorError, isSessionSettingsError } from "@nextclaw/kernel";
4
+ import { AccessManager, PanelAppError, injectUiContentParamsBootstrap, isAppPackageError, isInboxDeliveryError, isPanelAppError, isPreferenceError, isProjectError, isServiceAppError, isSessionContextCompactionError, isSessionMessageCursorError, isSessionSettingsError, isSystemObjectReferenceError } from "@nextclaw/kernel";
5
5
  import { WebSocket, WebSocketServer } from "ws";
6
6
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
7
- import { mkdir, open, readFile, readdir, realpath, stat } from "node:fs/promises";
7
+ import { lstat, mkdir, open, readFile, readdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
8
8
  import { basename, dirname, extname, isAbsolute, join, parse, relative, resolve, sep } from "node:path";
9
9
  import { createHash, randomBytes, randomUUID } from "node:crypto";
10
10
  import * as NextclawCore from "@nextclaw/core";
@@ -702,6 +702,118 @@ var AppRoutesController = class {
702
702
  extensionRuntimeStatus = (c) => c.json(ok(this.options.extensions?.getRuntimeStatus?.() ?? []));
703
703
  };
704
704
  //#endregion
705
+ //#region src/features/app-packages/controllers/app-packages.controller.ts
706
+ var AppPackagesRoutesController = class {
707
+ constructor(manager) {
708
+ this.manager = manager;
709
+ }
710
+ list = async (c) => c.json(ok(await this.manager.listPackages()));
711
+ get = async (c) => {
712
+ try {
713
+ return c.json(ok(await this.manager.getPackage(c.req.param("appId"))));
714
+ } catch (error) {
715
+ return this.handleError(c, error);
716
+ }
717
+ };
718
+ install = async (c) => {
719
+ const body = await readJson(c.req.raw);
720
+ if (!body.ok || !isRecord$2(body.data) || typeof body.data.source !== "string") return c.json(err("INVALID_APP_PACKAGE_INSTALL", "source is required"), 400);
721
+ try {
722
+ return c.json(ok(await this.manager.install(body.data.source, typeof body.data.registryUrl === "string" ? body.data.registryUrl : void 0)));
723
+ } catch (error) {
724
+ return this.handleError(c, error);
725
+ }
726
+ };
727
+ enable = async (c) => {
728
+ try {
729
+ return c.json(ok(await this.manager.enable(c.req.param("appId"))));
730
+ } catch (error) {
731
+ return this.handleError(c, error);
732
+ }
733
+ };
734
+ disable = async (c) => {
735
+ try {
736
+ return c.json(ok(await this.manager.disable(c.req.param("appId"))));
737
+ } catch (error) {
738
+ return this.handleError(c, error);
739
+ }
740
+ };
741
+ update = async (c) => {
742
+ const body = await readJson(c.req.raw);
743
+ if (!body.ok || !isRecord$2(body.data)) return c.json(err("INVALID_APP_PACKAGE_UPDATE", "invalid update request"), 400);
744
+ try {
745
+ return c.json(ok(await this.manager.update(c.req.param("appId"), {
746
+ version: typeof body.data.version === "string" ? body.data.version : void 0,
747
+ registryUrl: typeof body.data.registryUrl === "string" ? body.data.registryUrl : void 0
748
+ })));
749
+ } catch (error) {
750
+ return this.handleError(c, error);
751
+ }
752
+ };
753
+ rollback = async (c) => {
754
+ const body = await readJson(c.req.raw);
755
+ if (!body.ok || !isRecord$2(body.data) || typeof body.data.version !== "string") return c.json(err("INVALID_APP_PACKAGE_ROLLBACK", "version is required"), 400);
756
+ try {
757
+ return c.json(ok(await this.manager.rollback(c.req.param("appId"), body.data.version)));
758
+ } catch (error) {
759
+ return this.handleError(c, error);
760
+ }
761
+ };
762
+ uninstall = async (c) => {
763
+ const body = await readJson(c.req.raw);
764
+ const purgeData = body.ok && isRecord$2(body.data) && body.data.purgeData === true;
765
+ try {
766
+ return c.json(ok(await this.manager.uninstall(c.req.param("appId"), purgeData)));
767
+ } catch (error) {
768
+ return this.handleError(c, error);
769
+ }
770
+ };
771
+ handleError = (c, error) => {
772
+ if (isAppPackageError(error)) {
773
+ const status = error.code === "APP_PACKAGE_NOT_FOUND" ? 404 : error.code === "APP_PACKAGE_CONFLICT" ? 409 : 400;
774
+ return c.json(err(error.code, error.message), status);
775
+ }
776
+ return c.json(err("APP_PACKAGE_OPERATION_FAILED", error instanceof Error ? error.message : String(error)), 400);
777
+ };
778
+ };
779
+ //#endregion
780
+ //#region src/app/controllers/system-object-references.controller.ts
781
+ function readLimit(value) {
782
+ if (value === void 0) return void 0;
783
+ const limit = Number(value);
784
+ return Number.isSafeInteger(limit) && limit >= 1 && limit <= 50 ? limit : null;
785
+ }
786
+ var SystemObjectReferencesRoutesController = class {
787
+ constructor(manager) {
788
+ this.manager = manager;
789
+ }
790
+ list = async (c) => {
791
+ const limit = readLimit(c.req.query("limit"));
792
+ if (limit === null) return c.json(err("INVALID_SYSTEM_OBJECT_QUERY", `limit must be between 1 and 50`), 400);
793
+ try {
794
+ return c.json(ok(await this.manager.listReferences({
795
+ query: c.req.query("query"),
796
+ limit,
797
+ objectType: c.req.query("objectType")
798
+ })));
799
+ } catch (error) {
800
+ if (!isSystemObjectReferenceError(error)) throw error;
801
+ return c.json(err(error.code, error.message), 400);
802
+ }
803
+ };
804
+ resolve = async (c) => {
805
+ const body = await readJson(c.req.raw);
806
+ if (!body.ok || !isRecord$2(body.data) || typeof body.data.uri !== "string" || !body.data.uri.trim()) return c.json(err("INVALID_SYSTEM_OBJECT_REFERENCE", "uri must be a non-empty string"), 400);
807
+ try {
808
+ return c.json(ok(await this.manager.resolveReference(body.data.uri)));
809
+ } catch (error) {
810
+ if (!isSystemObjectReferenceError(error)) throw error;
811
+ const status = error.code === "SYSTEM_OBJECT_NOT_FOUND" ? 404 : 400;
812
+ return c.json(err(error.code, error.message), status);
813
+ }
814
+ };
815
+ };
816
+ //#endregion
705
817
  //#region src/features/config/providers/server-builtin-provider.provider.ts
706
818
  const SERVER_BUILTIN_PROVIDER_OVERRIDES = [{
707
819
  name: "minimax-portal",
@@ -3081,7 +3193,6 @@ var InboxDeliveriesRoutesController = class {
3081
3193
  deliveryId
3082
3194
  }));
3083
3195
  };
3084
- continueInChat = async (c) => await this.handleManagerAction(c, () => this.manager.continueInChat(c.req.param("deliveryId")));
3085
3196
  handleManagerAction = async (c, action) => {
3086
3197
  try {
3087
3198
  return c.json(ok(await action()));
@@ -3734,6 +3845,17 @@ function findUnsupportedSkillInstallKind(items) {
3734
3845
  }
3735
3846
  //#endregion
3736
3847
  //#region src/features/marketplace/controllers/skill-marketplace.controller.ts
3848
+ const MARKETPLACE_SKILL_LOCAL_CHANGES_ERROR_CODE = "MARKETPLACE_SKILL_LOCAL_CHANGES";
3849
+ function readStructuredError(error) {
3850
+ const message = error instanceof Error ? error.message : String(error);
3851
+ if (!error || typeof error !== "object") return { message };
3852
+ const value = error;
3853
+ return {
3854
+ message,
3855
+ code: typeof value.code === "string" ? value.code : void 0,
3856
+ details: value.details && typeof value.details === "object" && !Array.isArray(value.details) ? value.details : void 0
3857
+ };
3858
+ }
3737
3859
  async function installMarketplaceSkill(params) {
3738
3860
  const { body, options } = params;
3739
3861
  const spec = typeof body.spec === "string" ? body.spec.trim() : "";
@@ -3903,9 +4025,11 @@ var SkillMarketplaceController = class {
3903
4025
  });
3904
4026
  return c.json(ok(payload));
3905
4027
  } catch (error) {
3906
- const message = String(error);
4028
+ const structuredError = readStructuredError(error);
4029
+ const message = structuredError.message;
3907
4030
  if (message.startsWith("INVALID_BODY:")) return c.json(err("INVALID_BODY", message.slice(13)), 400);
3908
4031
  if (message.startsWith("NOT_AVAILABLE:")) return c.json(err("NOT_AVAILABLE", message.slice(14)), 503);
4032
+ if (structuredError.code === MARKETPLACE_SKILL_LOCAL_CHANGES_ERROR_CODE) return c.json(err(MARKETPLACE_SKILL_LOCAL_CHANGES_ERROR_CODE, message, structuredError.details), 409);
3909
4033
  return c.json(err("MANAGE_FAILED", message), 400);
3910
4034
  }
3911
4035
  };
@@ -4839,6 +4963,8 @@ var ServiceAppsRoutesController = class {
4839
4963
  };
4840
4964
  //#endregion
4841
4965
  //#region src/app/utils/ncp-session-event-stream.utils.ts
4966
+ const NCP_SESSION_STREAM_HEARTBEAT_INTERVAL_MS = 25e3;
4967
+ const NCP_SESSION_STREAM_HEARTBEAT_FRAME = ": keepalive\n\n";
4842
4968
  function readEventSessionId(event) {
4843
4969
  const payload = "payload" in event ? event.payload : null;
4844
4970
  if (!payload || typeof payload !== "object") return null;
@@ -4852,8 +4978,13 @@ function createNcpSessionEventStreamResponse(eventBus, payload, signal) {
4852
4978
  let controller = null;
4853
4979
  let closed = false;
4854
4980
  let unsubscribe = () => void 0;
4981
+ let heartbeat = null;
4855
4982
  const cleanup = () => {
4856
4983
  unsubscribe();
4984
+ if (heartbeat) {
4985
+ clearInterval(heartbeat);
4986
+ heartbeat = null;
4987
+ }
4857
4988
  signal.removeEventListener("abort", close);
4858
4989
  };
4859
4990
  const close = () => {
@@ -4870,6 +5001,9 @@ function createNcpSessionEventStreamResponse(eventBus, payload, signal) {
4870
5001
  start: (streamController) => {
4871
5002
  controller = streamController;
4872
5003
  unsubscribe = eventBus.on(eventKeys.ncpEvent, push);
5004
+ heartbeat = setInterval(() => {
5005
+ if (!closed && !signal.aborted) controller?.enqueue(encoder.encode(NCP_SESSION_STREAM_HEARTBEAT_FRAME));
5006
+ }, NCP_SESSION_STREAM_HEARTBEAT_INTERVAL_MS);
4873
5007
  signal.addEventListener("abort", close, { once: true });
4874
5008
  if (signal.aborted) close();
4875
5009
  },
@@ -5079,6 +5213,28 @@ function isServerPathBrowseError(error) {
5079
5213
  return error instanceof ServerPathBrowseError;
5080
5214
  }
5081
5215
  //#endregion
5216
+ //#region src/features/server-path/utils/server-path-search.utils.ts
5217
+ function isServerPathInside(basePath, candidatePath) {
5218
+ const relativePath = relative(basePath, candidatePath);
5219
+ return relativePath === "" || relativePath !== ".." && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath);
5220
+ }
5221
+ function normalizeServerPathRelativePath(value) {
5222
+ return value.split(sep).join("/");
5223
+ }
5224
+ function resolveServerPathSearchScore(entry, query) {
5225
+ if (!query) return 1;
5226
+ const normalizedQuery = query.toLocaleLowerCase();
5227
+ const normalizedName = entry.name.toLocaleLowerCase();
5228
+ const normalizedPath = entry.relativePath.toLocaleLowerCase();
5229
+ if (!normalizedQuery.split(/\s+/).filter(Boolean).every((term) => normalizedPath.includes(term))) return 0;
5230
+ if (normalizedName === normalizedQuery) return 1e3;
5231
+ if (normalizedName.startsWith(normalizedQuery)) return 850;
5232
+ if (normalizedName.includes(normalizedQuery)) return 700;
5233
+ if (normalizedPath.startsWith(normalizedQuery)) return 600;
5234
+ if (normalizedPath.split("/").some((segment) => segment.startsWith(normalizedQuery))) return 550;
5235
+ return 400 - Math.min(normalizedPath.length, 300);
5236
+ }
5237
+ //#endregion
5082
5238
  //#region src/features/server-path/utils/server-path-directory.utils.ts
5083
5239
  var ServerPathDirectoryCreateError = class extends Error {
5084
5240
  constructor(code, message) {
@@ -5092,28 +5248,46 @@ function normalizeDirectoryName(value) {
5092
5248
  if (!name || name === "." || name === ".." || name.includes("/") || name.includes("\\")) throw new ServerPathDirectoryCreateError("SERVER_PATH_DIRECTORY_NAME_INVALID", "directory name is invalid");
5093
5249
  return name;
5094
5250
  }
5095
- function readFileErrorCode(error) {
5251
+ function readFileErrorCode$1(error) {
5096
5252
  return typeof error === "object" && error !== null && "code" in error ? String(error.code ?? "") : void 0;
5097
5253
  }
5098
- async function createServerPathDirectory(input) {
5099
- let parentPath;
5254
+ function resolveDirectoryPath(value) {
5100
5255
  try {
5101
- parentPath = resolveServerPath({ path: typeof input.parentPath === "string" ? input.parentPath : null });
5256
+ return resolveServerPath({ path: typeof value === "string" ? value : null });
5102
5257
  } catch (error) {
5103
5258
  if (error instanceof ServerPathResolutionError) throw new ServerPathDirectoryCreateError("SERVER_PATH_PARENT_INVALID", error.message);
5104
5259
  throw error;
5105
5260
  }
5261
+ }
5262
+ async function assertParentInsideBase(baseValue, parentPath) {
5263
+ if (typeof baseValue !== "string" || !baseValue.trim()) return;
5264
+ const basePath = resolveDirectoryPath(baseValue);
5265
+ if (!isServerPathInside(basePath, parentPath)) throw new ServerPathDirectoryCreateError("SERVER_PATH_PARENT_INVALID", "parent path must stay inside the project root");
5266
+ try {
5267
+ const [realBasePath, realParentPath] = await Promise.all([realpath(basePath), realpath(parentPath)]);
5268
+ if (!isServerPathInside(realBasePath, realParentPath)) throw new ServerPathDirectoryCreateError("SERVER_PATH_PARENT_INVALID", "parent path must stay inside the project root");
5269
+ } catch (error) {
5270
+ if (error instanceof ServerPathDirectoryCreateError) throw error;
5271
+ throw new ServerPathDirectoryCreateError("SERVER_PATH_PARENT_INVALID", "project root and parent path must exist");
5272
+ }
5273
+ }
5274
+ function throwDirectoryCreationError(error) {
5275
+ const code = readFileErrorCode$1(error);
5276
+ if (code === "EEXIST") throw new ServerPathDirectoryCreateError("SERVER_PATH_DIRECTORY_EXISTS", "directory already exists");
5277
+ if (code === "ENOENT") throw new ServerPathDirectoryCreateError("SERVER_PATH_PARENT_NOT_FOUND", "parent directory does not exist");
5278
+ if (code === "ENOTDIR") throw new ServerPathDirectoryCreateError("SERVER_PATH_PARENT_NOT_DIRECTORY", "parent path must point to a directory");
5279
+ if (code === "EACCES" || code === "EPERM" || code === "EROFS") throw new ServerPathDirectoryCreateError("SERVER_PATH_PARENT_NOT_WRITABLE", "parent directory is not writable");
5280
+ throw error;
5281
+ }
5282
+ async function createServerPathDirectory(input) {
5283
+ const parentPath = resolveDirectoryPath(input.parentPath);
5284
+ await assertParentInsideBase(input.basePath, parentPath);
5106
5285
  const directoryPath = join(parentPath, normalizeDirectoryName(input.name));
5107
5286
  try {
5108
5287
  await mkdir(directoryPath);
5109
5288
  return { path: directoryPath };
5110
5289
  } catch (error) {
5111
- const code = readFileErrorCode(error);
5112
- if (code === "EEXIST") throw new ServerPathDirectoryCreateError("SERVER_PATH_DIRECTORY_EXISTS", "directory already exists");
5113
- if (code === "ENOENT") throw new ServerPathDirectoryCreateError("SERVER_PATH_PARENT_NOT_FOUND", "parent directory does not exist");
5114
- if (code === "ENOTDIR") throw new ServerPathDirectoryCreateError("SERVER_PATH_PARENT_NOT_DIRECTORY", "parent path must point to a directory");
5115
- if (code === "EACCES" || code === "EPERM" || code === "EROFS") throw new ServerPathDirectoryCreateError("SERVER_PATH_PARENT_NOT_WRITABLE", "parent directory is not writable");
5116
- throw error;
5290
+ throwDirectoryCreationError(error);
5117
5291
  }
5118
5292
  }
5119
5293
  function isServerPathDirectoryCreateError(error) {
@@ -5405,28 +5579,6 @@ function isServerPathContentError(error) {
5405
5579
  return error instanceof ServerPathContentError;
5406
5580
  }
5407
5581
  //#endregion
5408
- //#region src/features/server-path/utils/server-path-search.utils.ts
5409
- function isServerPathInside(basePath, candidatePath) {
5410
- const relativePath = relative(basePath, candidatePath);
5411
- return relativePath === "" || relativePath !== ".." && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath);
5412
- }
5413
- function normalizeServerPathRelativePath(value) {
5414
- return value.split(sep).join("/");
5415
- }
5416
- function resolveServerPathSearchScore(entry, query) {
5417
- if (!query) return 1;
5418
- const normalizedQuery = query.toLocaleLowerCase();
5419
- const normalizedName = entry.name.toLocaleLowerCase();
5420
- const normalizedPath = entry.relativePath.toLocaleLowerCase();
5421
- if (!normalizedQuery.split(/\s+/).filter(Boolean).every((term) => normalizedPath.includes(term))) return 0;
5422
- if (normalizedName === normalizedQuery) return 1e3;
5423
- if (normalizedName.startsWith(normalizedQuery)) return 850;
5424
- if (normalizedName.includes(normalizedQuery)) return 700;
5425
- if (normalizedPath.startsWith(normalizedQuery)) return 600;
5426
- if (normalizedPath.split("/").some((segment) => segment.startsWith(normalizedQuery))) return 550;
5427
- return 400 - Math.min(normalizedPath.length, 300);
5428
- }
5429
- //#endregion
5430
5582
  //#region src/features/server-path/services/server-path-search.service.ts
5431
5583
  const DEFAULT_RESULT_LIMIT = 50;
5432
5584
  const MAX_RESULT_LIMIT = 100;
@@ -5614,6 +5766,199 @@ function isServerPathSearchError(error) {
5614
5766
  return error instanceof ServerPathSearchError;
5615
5767
  }
5616
5768
  //#endregion
5769
+ //#region src/features/server-path/utils/server-path-mutation.utils.ts
5770
+ var ServerPathMutationError = class extends Error {
5771
+ constructor(code, message, details) {
5772
+ super(message);
5773
+ this.code = code;
5774
+ this.details = details;
5775
+ this.name = "ServerPathMutationError";
5776
+ }
5777
+ };
5778
+ function readFileErrorCode(error) {
5779
+ return typeof error === "object" && error !== null && "code" in error ? String(error.code ?? "") : void 0;
5780
+ }
5781
+ function resolveScopedPaths(input) {
5782
+ let basePath;
5783
+ let targetPath;
5784
+ try {
5785
+ basePath = resolveServerPath({ path: typeof input.basePath === "string" ? input.basePath : null });
5786
+ targetPath = resolveServerPath({
5787
+ path: typeof input.targetPath === "string" ? input.targetPath : null,
5788
+ basePath
5789
+ });
5790
+ } catch (error) {
5791
+ if (error instanceof ServerPathResolutionError) throw new ServerPathMutationError("SERVER_PATH_BASE_INVALID", error.message);
5792
+ throw error;
5793
+ }
5794
+ if (!isServerPathInside(basePath, targetPath)) throw new ServerPathMutationError("SERVER_PATH_OUTSIDE_BASE", "target path must stay inside the project root");
5795
+ if (!input.allowRoot && targetPath === basePath) throw new ServerPathMutationError("SERVER_PATH_ROOT_PROTECTED", "the project root cannot be deleted");
5796
+ return {
5797
+ basePath,
5798
+ targetPath
5799
+ };
5800
+ }
5801
+ async function resolveRealBasePath(basePath) {
5802
+ try {
5803
+ const resolved = await realpath(basePath);
5804
+ if (!(await stat(resolved)).isDirectory()) throw new ServerPathMutationError("SERVER_PATH_BASE_INVALID", "project root must point to a directory");
5805
+ return resolved;
5806
+ } catch (error) {
5807
+ if (error instanceof ServerPathMutationError) throw error;
5808
+ throw new ServerPathMutationError("SERVER_PATH_BASE_INVALID", "project root does not exist or is not accessible");
5809
+ }
5810
+ }
5811
+ async function assertExistingPathInsideRealBase(params) {
5812
+ const [realBasePath, realTargetPath] = await Promise.all([resolveRealBasePath(params.basePath), realpath(params.targetPath).catch(() => null)]);
5813
+ if (!realTargetPath) throw new ServerPathMutationError("SERVER_PATH_ENTRY_NOT_FOUND", "target path does not exist");
5814
+ if (!isServerPathInside(realBasePath, realTargetPath)) throw new ServerPathMutationError("SERVER_PATH_OUTSIDE_BASE", "target path must stay inside the project root");
5815
+ }
5816
+ function normalizeEntryName(value) {
5817
+ const name = typeof value === "string" ? value.trim() : "";
5818
+ if (!name || name === "." || name === ".." || name.includes("/") || name.includes("\\") || name.includes("\0") || basename(name) !== name) throw new ServerPathMutationError("SERVER_PATH_FILE_NAME_INVALID", "entry name is invalid");
5819
+ return name;
5820
+ }
5821
+ async function createServerPathFile(input) {
5822
+ const { basePath, targetPath: parentPath } = resolveScopedPaths({
5823
+ basePath: input.basePath,
5824
+ targetPath: input.parentPath,
5825
+ allowRoot: true
5826
+ });
5827
+ await assertExistingPathInsideRealBase({
5828
+ basePath,
5829
+ targetPath: parentPath
5830
+ });
5831
+ if (!(await stat(parentPath).catch(() => null))?.isDirectory()) throw new ServerPathMutationError("SERVER_PATH_TARGET_NOT_DIRECTORY", "file parent must point to a directory");
5832
+ const name = normalizeEntryName(input.name);
5833
+ const path = join(parentPath, name);
5834
+ try {
5835
+ await writeFile(path, "", { flag: "wx" });
5836
+ } catch (error) {
5837
+ const code = readFileErrorCode(error);
5838
+ if (code === "EEXIST") throw new ServerPathMutationError("SERVER_PATH_FILE_EXISTS", "target file already exists");
5839
+ if (code === "EACCES" || code === "EPERM" || code === "EROFS") throw new ServerPathMutationError("SERVER_PATH_TARGET_NOT_WRITABLE", "file parent is not writable");
5840
+ throw error;
5841
+ }
5842
+ return {
5843
+ name,
5844
+ path,
5845
+ kind: "file"
5846
+ };
5847
+ }
5848
+ async function renameServerPathEntry(input) {
5849
+ const { basePath, targetPath } = resolveScopedPaths({
5850
+ basePath: input.basePath,
5851
+ targetPath: input.path
5852
+ });
5853
+ await assertExistingPathInsideRealBase({
5854
+ basePath,
5855
+ targetPath
5856
+ });
5857
+ const entryStats = await lstat(targetPath).catch(() => null);
5858
+ if (!entryStats) throw new ServerPathMutationError("SERVER_PATH_ENTRY_NOT_FOUND", "target path does not exist");
5859
+ const name = normalizeEntryName(input.name);
5860
+ const path = join(resolve(targetPath, ".."), name);
5861
+ if (!isServerPathInside(basePath, path)) throw new ServerPathMutationError("SERVER_PATH_OUTSIDE_BASE", "target path must stay inside the project root");
5862
+ if (await lstat(path).then(() => true).catch(() => false)) throw new ServerPathMutationError("SERVER_PATH_FILE_EXISTS", "target entry already exists");
5863
+ try {
5864
+ await rename(targetPath, path);
5865
+ } catch (error) {
5866
+ const code = readFileErrorCode(error);
5867
+ if (code === "EACCES" || code === "EPERM" || code === "EROFS") throw new ServerPathMutationError("SERVER_PATH_TARGET_NOT_WRITABLE", "target path is not writable");
5868
+ throw error;
5869
+ }
5870
+ return {
5871
+ oldPath: targetPath,
5872
+ path,
5873
+ name,
5874
+ kind: entryStats.isDirectory() ? "directory" : "file"
5875
+ };
5876
+ }
5877
+ async function deleteServerPathEntry(input) {
5878
+ const { basePath, targetPath } = resolveScopedPaths({
5879
+ basePath: input.basePath,
5880
+ targetPath: input.path
5881
+ });
5882
+ await assertExistingPathInsideRealBase({
5883
+ basePath,
5884
+ targetPath
5885
+ });
5886
+ let entryStats;
5887
+ try {
5888
+ entryStats = await lstat(targetPath);
5889
+ } catch {
5890
+ throw new ServerPathMutationError("SERVER_PATH_ENTRY_NOT_FOUND", "target path does not exist");
5891
+ }
5892
+ const kind = entryStats.isDirectory() ? "directory" : "file";
5893
+ try {
5894
+ await rm(targetPath, {
5895
+ recursive: kind === "directory",
5896
+ force: false
5897
+ });
5898
+ } catch (error) {
5899
+ const code = readFileErrorCode(error);
5900
+ if (code === "ENOENT") throw new ServerPathMutationError("SERVER_PATH_ENTRY_NOT_FOUND", "target path does not exist");
5901
+ if (code === "EACCES" || code === "EPERM" || code === "EROFS") throw new ServerPathMutationError("SERVER_PATH_TARGET_NOT_WRITABLE", "target path is not writable");
5902
+ throw error;
5903
+ }
5904
+ return {
5905
+ path: targetPath,
5906
+ kind
5907
+ };
5908
+ }
5909
+ async function uploadServerPathFiles(input) {
5910
+ const { basePath, targetPath } = resolveScopedPaths({
5911
+ basePath: input.basePath,
5912
+ targetPath: input.targetPath,
5913
+ allowRoot: true
5914
+ });
5915
+ await assertExistingPathInsideRealBase({
5916
+ basePath,
5917
+ targetPath
5918
+ });
5919
+ if (!(await stat(targetPath).catch(() => null))?.isDirectory()) throw new ServerPathMutationError("SERVER_PATH_TARGET_NOT_DIRECTORY", "upload target must point to a directory");
5920
+ if (input.files.length === 0) throw new ServerPathMutationError("SERVER_PATH_ENTRY_INVALID", "at least one upload file is required");
5921
+ const normalizedFiles = input.files.map((file) => ({
5922
+ file,
5923
+ name: normalizeEntryName(file.name)
5924
+ }));
5925
+ const uniqueNames = /* @__PURE__ */ new Set();
5926
+ for (const { name } of normalizedFiles) {
5927
+ if (uniqueNames.has(name)) throw new ServerPathMutationError("SERVER_PATH_FILE_NAME_INVALID", "upload file names must be unique");
5928
+ uniqueNames.add(name);
5929
+ }
5930
+ if (!input.overwrite) {
5931
+ const conflicts = (await Promise.all(normalizedFiles.map(async ({ name }) => {
5932
+ return await lstat(join(targetPath, name)).then(() => true).catch(() => false) ? name : null;
5933
+ }))).filter((name) => Boolean(name));
5934
+ if (conflicts.length > 0) throw new ServerPathMutationError("SERVER_PATH_FILE_EXISTS", "one or more upload files already exist", { conflicts });
5935
+ }
5936
+ try {
5937
+ return {
5938
+ files: await Promise.all(normalizedFiles.map(async ({ file, name }) => {
5939
+ const path = resolve(targetPath, name);
5940
+ const content = Buffer.from(await file.arrayBuffer());
5941
+ await writeFile(path, content, { flag: input.overwrite ? "w" : "wx" });
5942
+ return {
5943
+ name,
5944
+ path,
5945
+ sizeBytes: content.byteLength
5946
+ };
5947
+ })),
5948
+ overwritten: input.overwrite
5949
+ };
5950
+ } catch (error) {
5951
+ const code = readFileErrorCode(error);
5952
+ if (code === "EEXIST") throw new ServerPathMutationError("SERVER_PATH_FILE_EXISTS", "one or more upload files already exist");
5953
+ if (code === "ENOENT" || code === "ENOTDIR") throw new ServerPathMutationError("SERVER_PATH_TARGET_NOT_DIRECTORY", "upload target must point to an existing directory");
5954
+ if (code === "EACCES" || code === "EPERM" || code === "EROFS") throw new ServerPathMutationError("SERVER_PATH_TARGET_NOT_WRITABLE", "upload target is not writable");
5955
+ throw error;
5956
+ }
5957
+ }
5958
+ function isServerPathMutationError(error) {
5959
+ return error instanceof ServerPathMutationError;
5960
+ }
5961
+ //#endregion
5617
5962
  //#region src/features/server-path/controllers/server-path.controller.ts
5618
5963
  function readIncludeFilesFlag(value) {
5619
5964
  return value === "1" || value === "true";
@@ -5661,6 +6006,7 @@ var ServerPathRoutesController = class {
5661
6006
  if (!body.ok || !isRecord$2(body.data)) return c.json(err("INVALID_SERVER_PATH_DIRECTORY", "directory input is required"), 400);
5662
6007
  try {
5663
6008
  return c.json(ok(await createServerPathDirectory({
6009
+ basePath: body.data.basePath,
5664
6010
  parentPath: body.data.parentPath,
5665
6011
  name: body.data.name
5666
6012
  })), 201);
@@ -5669,6 +6015,68 @@ var ServerPathRoutesController = class {
5669
6015
  throw error;
5670
6016
  }
5671
6017
  };
6018
+ uploadFiles = async (c) => {
6019
+ let formData;
6020
+ try {
6021
+ formData = await c.req.raw.formData();
6022
+ } catch {
6023
+ return c.json(err("INVALID_SERVER_PATH_UPLOAD", "multipart upload is required"), 400);
6024
+ }
6025
+ const files = formData.getAll("files").flatMap((value) => typeof value === "string" ? [] : [{
6026
+ name: value.name,
6027
+ arrayBuffer: () => value.arrayBuffer()
6028
+ }]);
6029
+ try {
6030
+ return c.json(ok(await uploadServerPathFiles({
6031
+ basePath: formData.get("basePath"),
6032
+ targetPath: formData.get("targetPath"),
6033
+ overwrite: formData.get("overwrite") === "true",
6034
+ files
6035
+ })), 201);
6036
+ } catch (error) {
6037
+ if (isServerPathMutationError(error)) return c.json(err(error.code, error.message, error.details), error.code === "SERVER_PATH_FILE_EXISTS" ? 409 : 400);
6038
+ throw error;
6039
+ }
6040
+ };
6041
+ createFile = async (c) => {
6042
+ const body = await readJson(c.req.raw);
6043
+ if (!body.ok || !isRecord$2(body.data)) return c.json(err("INVALID_SERVER_PATH_FILE", "file input is required"), 400);
6044
+ try {
6045
+ return c.json(ok(await createServerPathFile({
6046
+ basePath: body.data.basePath,
6047
+ parentPath: body.data.parentPath,
6048
+ name: body.data.name
6049
+ })), 201);
6050
+ } catch (error) {
6051
+ if (isServerPathMutationError(error)) return c.json(err(error.code, error.message, error.details), error.code === "SERVER_PATH_FILE_EXISTS" ? 409 : 400);
6052
+ throw error;
6053
+ }
6054
+ };
6055
+ renameEntry = async (c) => {
6056
+ const body = await readJson(c.req.raw);
6057
+ if (!body.ok || !isRecord$2(body.data)) return c.json(err("INVALID_SERVER_PATH_RENAME", "rename input is required"), 400);
6058
+ try {
6059
+ return c.json(ok(await renameServerPathEntry({
6060
+ basePath: body.data.basePath,
6061
+ path: body.data.path,
6062
+ name: body.data.name
6063
+ })));
6064
+ } catch (error) {
6065
+ if (isServerPathMutationError(error)) return c.json(err(error.code, error.message, error.details), error.code === "SERVER_PATH_FILE_EXISTS" ? 409 : 400);
6066
+ throw error;
6067
+ }
6068
+ };
6069
+ deleteEntry = async (c) => {
6070
+ try {
6071
+ return c.json(ok(await deleteServerPathEntry({
6072
+ basePath: c.req.query("basePath"),
6073
+ path: c.req.query("path")
6074
+ })));
6075
+ } catch (error) {
6076
+ if (isServerPathMutationError(error)) return c.json(err(error.code, error.message, error.details), 400);
6077
+ throw error;
6078
+ }
6079
+ };
5672
6080
  read = async (c) => {
5673
6081
  try {
5674
6082
  const payload = await readServerPath({
@@ -5710,11 +6118,13 @@ function createUiRouteControllers(options, authService, marketplaceBaseUrls) {
5710
6118
  const { kernel, panelAppClientSdkScript, remoteAccess, runtimeControl, runtimeUpdate } = options;
5711
6119
  return {
5712
6120
  app: new AppRoutesController(options),
6121
+ appPackages: new AppPackagesRoutesController(kernel.appPackageManager),
5713
6122
  agents: new AgentsRoutesController(options),
5714
6123
  auth: new AuthRoutesController(authService),
5715
6124
  config: new ConfigRoutesController(options),
5716
6125
  cron: new CronRoutesController(options),
5717
6126
  inboxDeliveries: new InboxDeliveriesRoutesController(kernel.inboxDeliveryManager),
6127
+ systemObjectReferences: new SystemObjectReferencesRoutesController(kernel.systemObjectReferenceManager),
5718
6128
  ncpSession: new NcpSessionRoutesController(options),
5719
6129
  ncpAsset: new NcpAssetRoutesController(options),
5720
6130
  panelApps: new PanelAppsRoutesController(kernel.panelAppManager, { panelAppClientSdkScript }),
@@ -5833,7 +6243,7 @@ var UiRouteRegistry = class {
5833
6243
  ]]);
5834
6244
  };
5835
6245
  mountResourceRoutes = () => {
5836
- const { ncpSession, inboxDeliveries, panelApps, preferences, projects, serviceApps, serverPath } = this.controllers;
6246
+ const { appPackages, ncpSession, inboxDeliveries, panelApps, preferences, projects, serviceApps, serverPath, systemObjectReferences } = this.controllers;
5837
6247
  this.mountRoutes([
5838
6248
  [
5839
6249
  "get",
@@ -5905,10 +6315,55 @@ var UiRouteRegistry = class {
5905
6315
  "/api/inbox/deliveries/:deliveryId",
5906
6316
  inboxDeliveries.delete
5907
6317
  ],
6318
+ [
6319
+ "get",
6320
+ "/api/system-object-references",
6321
+ systemObjectReferences.list
6322
+ ],
6323
+ [
6324
+ "post",
6325
+ "/api/system-object-references/resolve",
6326
+ systemObjectReferences.resolve
6327
+ ],
6328
+ [
6329
+ "get",
6330
+ "/api/app-packages",
6331
+ appPackages.list
6332
+ ],
6333
+ [
6334
+ "post",
6335
+ "/api/app-packages/install",
6336
+ appPackages.install
6337
+ ],
6338
+ [
6339
+ "get",
6340
+ "/api/app-packages/:appId",
6341
+ appPackages.get
6342
+ ],
6343
+ [
6344
+ "post",
6345
+ "/api/app-packages/:appId/enable",
6346
+ appPackages.enable
6347
+ ],
6348
+ [
6349
+ "post",
6350
+ "/api/app-packages/:appId/disable",
6351
+ appPackages.disable
6352
+ ],
6353
+ [
6354
+ "post",
6355
+ "/api/app-packages/:appId/update",
6356
+ appPackages.update
6357
+ ],
5908
6358
  [
5909
6359
  "post",
5910
- "/api/inbox/deliveries/:deliveryId/continue",
5911
- inboxDeliveries.continueInChat
6360
+ "/api/app-packages/:appId/rollback",
6361
+ appPackages.rollback
6362
+ ],
6363
+ [
6364
+ "delete",
6365
+ "/api/app-packages/:appId",
6366
+ appPackages.uninstall
5912
6367
  ],
5913
6368
  [
5914
6369
  "get",
@@ -6095,6 +6550,26 @@ var UiRouteRegistry = class {
6095
6550
  "/api/server-paths/directory",
6096
6551
  serverPath.createDirectory
6097
6552
  ],
6553
+ [
6554
+ "post",
6555
+ "/api/server-paths/file",
6556
+ serverPath.createFile
6557
+ ],
6558
+ [
6559
+ "post",
6560
+ "/api/server-paths/files",
6561
+ serverPath.uploadFiles
6562
+ ],
6563
+ [
6564
+ "patch",
6565
+ "/api/server-paths/entry",
6566
+ serverPath.renameEntry
6567
+ ],
6568
+ [
6569
+ "delete",
6570
+ "/api/server-paths/entry",
6571
+ serverPath.deleteEntry
6572
+ ],
6098
6573
  [
6099
6574
  "get",
6100
6575
  "/api/server-paths/read",
@@ -6670,6 +7145,6 @@ async function startUiServer(gateway) {
6670
7145
  };
6671
7146
  }
6672
7147
  //#endregion
6673
- export { ConfigRoutesController, InboxDeliveriesRoutesController, PanelAppsRoutesController, RuntimeControlRoutesController, ServiceAppsRoutesController, buildConfigMeta, buildConfigSchemaView, buildConfigView, buildProviderTemplatesView, buildProvidersView, createProvider, createUiRouter, deleteProvider, ensureUiBridgeSecret, executeConfigAction, getUiBridgeSecretPath, loadConfigOrDefault, readUiBridgeSecret, startUiServer, updateChannel, updateModel, updateProvider, updateRuntime, updateSearch, updateSecrets };
7148
+ export { AppPackagesRoutesController, ConfigRoutesController, InboxDeliveriesRoutesController, PanelAppsRoutesController, RuntimeControlRoutesController, ServiceAppsRoutesController, buildConfigMeta, buildConfigSchemaView, buildConfigView, buildProviderTemplatesView, buildProvidersView, createProvider, createUiRouter, deleteProvider, ensureUiBridgeSecret, executeConfigAction, getUiBridgeSecretPath, loadConfigOrDefault, readUiBridgeSecret, startUiServer, updateChannel, updateModel, updateProvider, updateRuntime, updateSearch, updateSecrets };
6674
7149
 
6675
7150
  //# sourceMappingURL=index.js.map