@nocobase/server 2.1.0-alpha.3 → 2.1.0-alpha.31

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.
Files changed (56) hide show
  1. package/LICENSE +201 -661
  2. package/README.md +79 -10
  3. package/lib/acl/available-action.js +1 -1
  4. package/lib/aes-encryptor.js +3 -2
  5. package/lib/app-supervisor/app-options-factory.d.ts +1 -0
  6. package/lib/app-supervisor/index.js +15 -1
  7. package/lib/app-supervisor/main-only-adapter.d.ts +1 -1
  8. package/lib/app-supervisor/main-only-adapter.js +17 -12
  9. package/lib/application.d.ts +1 -2
  10. package/lib/application.js +3 -24
  11. package/lib/audit-manager/index.d.ts +2 -0
  12. package/lib/audit-manager/index.js +5 -2
  13. package/lib/commands/ai.js +1 -6
  14. package/lib/commands/create-migration.js +1 -1
  15. package/lib/commands/install.js +0 -2
  16. package/lib/commands/pm.js +7 -0
  17. package/lib/commands/start.js +2 -5
  18. package/lib/commands/upgrade.js +0 -2
  19. package/lib/event-queue.js +1 -1
  20. package/lib/gateway/index.d.ts +15 -3
  21. package/lib/gateway/index.js +153 -19
  22. package/lib/gateway/utils.d.ts +17 -0
  23. package/lib/gateway/utils.js +115 -0
  24. package/lib/helper.js +33 -1
  25. package/lib/index.d.ts +1 -0
  26. package/lib/index.js +2 -0
  27. package/lib/main-data-source.js +8 -5
  28. package/lib/plugin-manager/deps.js +3 -2
  29. package/lib/plugin-manager/findPackageNames.js +4 -2
  30. package/lib/plugin-manager/options/resource.d.ts +11 -1
  31. package/lib/plugin-manager/options/resource.js +162 -53
  32. package/lib/plugin-manager/plugin-manager.d.ts +7 -2
  33. package/lib/plugin-manager/plugin-manager.js +57 -44
  34. package/lib/plugin-manager/utils.d.ts +9 -1
  35. package/lib/plugin-manager/utils.js +68 -10
  36. package/lib/plugin.js +46 -2
  37. package/lib/pub-sub-manager/handler-manager.d.ts +1 -0
  38. package/lib/pub-sub-manager/handler-manager.js +11 -0
  39. package/lib/pub-sub-manager/pub-sub-manager.js +2 -1
  40. package/lib/swagger/app.d.ts +102 -0
  41. package/lib/swagger/app.js +124 -0
  42. package/lib/swagger/base.d.ts +244 -0
  43. package/lib/swagger/base.js +292 -0
  44. package/lib/swagger/collections.d.ts +996 -0
  45. package/lib/swagger/collections.js +1264 -0
  46. package/lib/swagger/index.d.ts +1774 -0
  47. package/lib/swagger/index.js +70 -0
  48. package/lib/swagger/pm.d.ts +462 -0
  49. package/lib/swagger/pm.js +422 -0
  50. package/lib/sync-message-manager.js +8 -1
  51. package/lib/worker-mode.d.ts +19 -0
  52. package/lib/worker-mode.js +67 -0
  53. package/package.json +19 -19
  54. package/lib/ai/create-docs-index.d.ts +0 -13
  55. package/lib/ai/create-docs-index.js +0 -892
  56. package/lib/swagger/index.json +0 -1569
@@ -37,6 +37,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
37
37
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
38
38
  var utils_exports = {};
39
39
  __export(utils_exports, {
40
+ assertSafePluginPackageName: () => assertSafePluginPackageName,
40
41
  checkAndGetCompatible: () => checkAndGetCompatible,
41
42
  checkCompatible: () => checkCompatible,
42
43
  copyTempPackageToStorageAndLinkToNodeModules: () => copyTempPackageToStorageAndLinkToNodeModules,
@@ -59,18 +60,19 @@ __export(utils_exports, {
59
60
  getPackagesFromFiles: () => getPackagesFromFiles,
60
61
  getPluginBasePath: () => getPluginBasePath,
61
62
  getPluginInfoByNpm: () => getPluginInfoByNpm,
62
- getPluginStoragePath: () => getPluginStoragePath,
63
63
  getServerPackages: () => getServerPackages,
64
64
  getStoragePluginDir: () => getStoragePluginDir,
65
65
  getTempDir: () => getTempDir,
66
66
  isNotBuiltinModule: () => isNotBuiltinModule,
67
67
  isValidPackageName: () => isValidPackageName,
68
+ pmListSummary: () => pmListSummary,
68
69
  readJSONFileContent: () => readJSONFileContent,
69
70
  removePluginPackage: () => removePluginPackage,
70
71
  removeRequireCache: () => removeRequireCache,
71
72
  removeTmpDir: () => removeTmpDir,
72
73
  requireModule: () => requireModule,
73
74
  requireNoCache: () => requireNoCache,
75
+ resolveSafeChildPath: () => resolveSafeChildPath,
74
76
  updatePluginByCompressedFileUrl: () => updatePluginByCompressedFileUrl
75
77
  });
76
78
  module.exports = __toCommonJS(utils_exports);
@@ -88,17 +90,42 @@ var import_semver = __toESM(require("semver"));
88
90
  var import_clientStaticUtils = require("./clientStaticUtils");
89
91
  var import_constants = require("./constants");
90
92
  var import_deps = __toESM(require("./deps"));
93
+ var import_findPackageNames = require("./findPackageNames");
94
+ var import_plugin_manager = __toESM(require("./plugin-manager"));
91
95
  /* istanbul ignore next -- @preserve */
92
96
  async function getTempDir() {
93
97
  const temporaryDirectory = await import_fs_extra.default.realpath(import_os.default.tmpdir());
94
98
  return import_path.default.join(temporaryDirectory, import_constants.APP_NAME);
95
99
  }
96
100
  __name(getTempDir, "getTempDir");
97
- function getPluginStoragePath() {
98
- const pluginStoragePath = process.env.PLUGIN_STORAGE_PATH || import_constants.DEFAULT_PLUGIN_STORAGE_PATH;
99
- return import_path.default.isAbsolute(pluginStoragePath) ? pluginStoragePath : import_path.default.join(process.cwd(), pluginStoragePath);
101
+ function assertSafePluginPackageName(packageName) {
102
+ if (!packageName || typeof packageName !== "string") {
103
+ throw new Error("Invalid plugin package name");
104
+ }
105
+ if (packageName.includes("\0")) {
106
+ throw new Error("Invalid plugin package name");
107
+ }
108
+ if (import_path.default.isAbsolute(packageName)) {
109
+ throw new Error("Invalid plugin package name");
110
+ }
111
+ if (packageName.includes("..") || packageName.includes("\\")) {
112
+ throw new Error("Invalid plugin package name");
113
+ }
114
+ const valid = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/i.test(packageName);
115
+ if (!valid) {
116
+ throw new Error("Invalid plugin package name");
117
+ }
118
+ }
119
+ __name(assertSafePluginPackageName, "assertSafePluginPackageName");
120
+ function resolveSafeChildPath(baseDir, child) {
121
+ const resolvedBase = import_path.default.resolve(baseDir);
122
+ const resolvedTarget = import_path.default.resolve(baseDir, child);
123
+ if (resolvedTarget !== resolvedBase && !resolvedTarget.startsWith(`${resolvedBase}${import_path.default.sep}`)) {
124
+ throw new Error("Path traversal detected");
125
+ }
126
+ return resolvedTarget;
100
127
  }
101
- __name(getPluginStoragePath, "getPluginStoragePath");
128
+ __name(resolveSafeChildPath, "resolveSafeChildPath");
102
129
  function getLocalPluginPackagesPathArr() {
103
130
  const pluginPackagesPathArr = process.env.PLUGIN_PATH || import_constants.DEFAULT_PLUGIN_PATH;
104
131
  return pluginPackagesPathArr.split(",").map((pluginPackagesPath) => {
@@ -108,8 +135,9 @@ function getLocalPluginPackagesPathArr() {
108
135
  }
109
136
  __name(getLocalPluginPackagesPathArr, "getLocalPluginPackagesPathArr");
110
137
  function getStoragePluginDir(packageName) {
111
- const pluginStoragePath = getPluginStoragePath();
112
- return import_path.default.join(pluginStoragePath, packageName);
138
+ const pluginStoragePath = (0, import_utils.resolvePluginStoragePath)();
139
+ assertSafePluginPackageName(packageName);
140
+ return resolveSafeChildPath(pluginStoragePath, packageName);
113
141
  }
114
142
  __name(getStoragePluginDir, "getStoragePluginDir");
115
143
  function getLocalPluginDir(packageDirBasename) {
@@ -121,7 +149,8 @@ function getLocalPluginDir(packageDirBasename) {
121
149
  }
122
150
  __name(getLocalPluginDir, "getLocalPluginDir");
123
151
  function getNodeModulesPluginDir(packageName) {
124
- return import_path.default.join(process.env.NODE_MODULES_PATH, packageName);
152
+ assertSafePluginPackageName(packageName);
153
+ return resolveSafeChildPath(process.env.NODE_MODULES_PATH, packageName);
125
154
  }
126
155
  __name(getNodeModulesPluginDir, "getNodeModulesPluginDir");
127
156
  function getAuthorizationHeaders(registry, authToken) {
@@ -340,7 +369,7 @@ async function updatePluginByCompressedFileUrl(options) {
340
369
  });
341
370
  if (!instance) {
342
371
  await removeTmpDir(tempFile, tempPackageContentDir);
343
- throw new Error(`plugin ${packageName} does not exist`);
372
+ throw new Error(`${packageName} does not exist`);
344
373
  }
345
374
  const { packageDir } = await copyTempPackageToStorageAndLinkToNodeModules(
346
375
  tempFile,
@@ -525,8 +554,36 @@ async function getPluginBasePath(packageName) {
525
554
  return import_path.default.dirname(import_path.default.dirname(file));
526
555
  }
527
556
  __name(getPluginBasePath, "getPluginBasePath");
557
+ async function pmListSummary(app) {
558
+ const plugins1 = await (0, import_findPackageNames.findBuiltInPlugins)();
559
+ const plugins2 = await (0, import_findPackageNames.findLocalPlugins)();
560
+ let enabledPlugins = [];
561
+ try {
562
+ enabledPlugins = (await app.pm.repository.find({
563
+ filter: {
564
+ enabled: true
565
+ }
566
+ })).map((item) => item.packageName);
567
+ } catch (error) {
568
+ }
569
+ const items = await Promise.all(
570
+ [...plugins1, ...plugins2].map(async (name) => {
571
+ const item = await import_plugin_manager.default.parseName(name);
572
+ const json = await import_plugin_manager.default.getPackageJson(item.packageName);
573
+ return {
574
+ displayName: json.displayName || name,
575
+ packageName: item.packageName,
576
+ enabled: enabledPlugins.includes(item.packageName),
577
+ description: json.description
578
+ };
579
+ })
580
+ );
581
+ return items;
582
+ }
583
+ __name(pmListSummary, "pmListSummary");
528
584
  // Annotate the CommonJS export names for ESM import in node:
529
585
  0 && (module.exports = {
586
+ assertSafePluginPackageName,
530
587
  checkAndGetCompatible,
531
588
  checkCompatible,
532
589
  copyTempPackageToStorageAndLinkToNodeModules,
@@ -549,17 +606,18 @@ __name(getPluginBasePath, "getPluginBasePath");
549
606
  getPackagesFromFiles,
550
607
  getPluginBasePath,
551
608
  getPluginInfoByNpm,
552
- getPluginStoragePath,
553
609
  getServerPackages,
554
610
  getStoragePluginDir,
555
611
  getTempDir,
556
612
  isNotBuiltinModule,
557
613
  isValidPackageName,
614
+ pmListSummary,
558
615
  readJSONFileContent,
559
616
  removePluginPackage,
560
617
  removeRequireCache,
561
618
  removeTmpDir,
562
619
  requireModule,
563
620
  requireNoCache,
621
+ resolveSafeChildPath,
564
622
  updatePluginByCompressedFileUrl
565
623
  });
package/lib/plugin.js CHANGED
@@ -206,6 +206,7 @@ const _Plugin = class _Plugin {
206
206
  return;
207
207
  }
208
208
  const toolsLoader = new import_ai.ToolsLoader(this.ai, {
209
+ pluginName: this.getName(),
209
210
  scan: {
210
211
  basePath,
211
212
  pattern: ["**/tools/**/*.ts", "**/tools/**/*.js", "!**/tools/**/*.d.ts", "**/tools/**/*/description.md"]
@@ -213,6 +214,37 @@ const _Plugin = class _Plugin {
213
214
  log: this.log
214
215
  });
215
216
  await toolsLoader.load();
217
+ const mcpLoader = new import_ai.MCPLoader(this.ai, {
218
+ pluginName: this.getName(),
219
+ scan: {
220
+ basePath,
221
+ pattern: ["mcp/*.ts", "mcp/*.js", "!mcp/*.d.ts"]
222
+ },
223
+ log: this.log
224
+ });
225
+ await mcpLoader.load();
226
+ const skillsLoader = new import_ai.SkillsLoader(this.ai, {
227
+ pluginName: this.getName(),
228
+ scan: { basePath, pattern: ["**/skills/**/SKILLS.md"] },
229
+ log: this.log
230
+ });
231
+ await skillsLoader.load();
232
+ const employeeLoader = new import_ai.AIEmployeeLoader(this.ai, {
233
+ pluginName: this.getName(),
234
+ scan: {
235
+ basePath,
236
+ pattern: [
237
+ "**/ai-employees/*.ts",
238
+ "**/ai-employees/*/index.ts",
239
+ "**/ai-employees/*.js",
240
+ "**/ai-employees/*/index.js",
241
+ "**/ai-employees/*/prompt.md",
242
+ "!**/ai-employees/**/*.d.ts"
243
+ ]
244
+ },
245
+ log: this.log
246
+ });
247
+ await employeeLoader.load();
216
248
  }
217
249
  /**
218
250
  * @deprecated
@@ -234,6 +266,18 @@ const _Plugin = class _Plugin {
234
266
  ...this.options
235
267
  };
236
268
  }
269
+ const langMap = {
270
+ "zh-CN": "cn/",
271
+ "en-US": "",
272
+ "ja-JP": "ja/",
273
+ "es-ES": "es/",
274
+ "pt-PT": "pt/",
275
+ "de-DE": "de",
276
+ "fr-FR": "fr/"
277
+ };
278
+ if (packageName.startsWith("@nocobase/plugin-")) {
279
+ packageJson.homepage = `https://v2.docs.nocobase.com/${langMap[locale] || ""}plugins/${packageName}`;
280
+ }
237
281
  const results = {
238
282
  ...this.options,
239
283
  keywords: packageJson.keywords,
@@ -241,7 +285,7 @@ const _Plugin = class _Plugin {
241
285
  changelogUrl: (0, import_plugin_manager.getExposeChangelogUrl)(packageName),
242
286
  displayName: packageJson[`displayName.${locale}`] || packageJson.displayName || name,
243
287
  description: packageJson[`description.${locale}`] || packageJson.description,
244
- homepage: packageJson[`homepage.${locale}`] || packageJson.homepage
288
+ homepage: packageJson.homepage
245
289
  };
246
290
  if (!options.withOutOpenFile) {
247
291
  const file = await import_fs.default.promises.realpath(
@@ -252,7 +296,7 @@ const _Plugin = class _Plugin {
252
296
  ...await (0, import_utils2.checkAndGetCompatible)(packageName),
253
297
  lastUpdated: (await import_fs.default.promises.stat(file)).ctime,
254
298
  file,
255
- updatable: file.startsWith(process.env.PLUGIN_STORAGE_PATH)
299
+ updatable: file.startsWith((0, import_utils.resolvePluginStoragePath)())
256
300
  };
257
301
  }
258
302
  return results;
@@ -29,6 +29,7 @@ export declare class HandlerManager {
29
29
  set(channel: string, callback: any, options: PubSubManagerSubscribeOptions): (wrappedMessage: any) => Promise<void>;
30
30
  get(channel: string, callback: any): any;
31
31
  delete(channel: string, callback: any): any;
32
+ cancelPendingDebounce(): void;
32
33
  reset(): void;
33
34
  each(callback: any): Promise<void>;
34
35
  }
@@ -129,7 +129,18 @@ const _HandlerManager = class _HandlerManager {
129
129
  headlerMap.delete(callback);
130
130
  return headler;
131
131
  }
132
+ cancelPendingDebounce() {
133
+ if (this.uniqueMessageHandlers) {
134
+ for (const handler of this.uniqueMessageHandlers.values()) {
135
+ if (typeof (handler == null ? void 0 : handler.cancel) === "function") {
136
+ handler.cancel();
137
+ }
138
+ }
139
+ this.uniqueMessageHandlers.clear();
140
+ }
141
+ }
132
142
  reset() {
143
+ this.cancelPendingDebounce();
133
144
  this.handlers = /* @__PURE__ */ new Map();
134
145
  this.uniqueMessageHandlers = /* @__PURE__ */ new Map();
135
146
  }
@@ -38,7 +38,7 @@ const createPubSubManager = /* @__PURE__ */ __name((app, options) => {
38
38
  app.on("afterStart", async () => {
39
39
  await pubSubManager.connect();
40
40
  });
41
- app.on("afterStop", async () => {
41
+ app.on("beforeStop", async () => {
42
42
  await pubSubManager.close();
43
43
  });
44
44
  return pubSubManager;
@@ -77,6 +77,7 @@ const _PubSubManager = class _PubSubManager {
77
77
  });
78
78
  }
79
79
  async close() {
80
+ this.handlerManager.cancelPendingDebounce();
80
81
  if (!this.adapter) {
81
82
  return;
82
83
  }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ declare const _default: {
10
+ readonly '/app:getLang': {
11
+ readonly get: {
12
+ readonly tags: readonly ["app"];
13
+ readonly summary: "Get the current application language";
14
+ readonly description: "Return the current locale used by the server.";
15
+ readonly parameters: readonly [];
16
+ readonly responses: {
17
+ readonly 200: {
18
+ readonly description: "OK";
19
+ readonly content: {
20
+ readonly 'application/json': {
21
+ readonly schema: {
22
+ readonly type: "string";
23
+ };
24
+ };
25
+ };
26
+ };
27
+ };
28
+ };
29
+ };
30
+ readonly '/app:getInfo': {
31
+ readonly get: {
32
+ readonly tags: readonly ["app"];
33
+ readonly summary: "Get application metadata";
34
+ readonly description: "Return basic application information, including version and environment-related metadata.";
35
+ readonly parameters: readonly [];
36
+ readonly responses: {
37
+ readonly 200: {
38
+ readonly description: "OK";
39
+ readonly content: {
40
+ readonly 'application/json': {
41
+ readonly schema: {
42
+ readonly type: "object";
43
+ readonly additionalProperties: true;
44
+ };
45
+ };
46
+ };
47
+ };
48
+ };
49
+ };
50
+ };
51
+ readonly '/app:getPlugins': {
52
+ readonly get: {
53
+ readonly tags: readonly ["app"];
54
+ readonly summary: "List loaded plugins";
55
+ readonly description: "Return plugin metadata for the current application runtime.";
56
+ readonly parameters: readonly [];
57
+ readonly responses: {
58
+ readonly 200: {
59
+ readonly description: "OK";
60
+ readonly content: {
61
+ readonly 'application/json': {
62
+ readonly schema: {
63
+ readonly type: "array";
64
+ readonly items: {
65
+ readonly type: "object";
66
+ readonly additionalProperties: true;
67
+ };
68
+ };
69
+ };
70
+ };
71
+ };
72
+ };
73
+ };
74
+ };
75
+ readonly '/app:restart': {
76
+ readonly post: {
77
+ readonly tags: readonly ["app"];
78
+ readonly summary: "Restart the application";
79
+ readonly description: "Trigger an application restart asynchronously.";
80
+ readonly parameters: readonly [];
81
+ readonly responses: {
82
+ readonly 200: {
83
+ readonly description: "OK";
84
+ };
85
+ };
86
+ };
87
+ };
88
+ readonly '/app:clearCache': {
89
+ readonly post: {
90
+ readonly tags: readonly ["app"];
91
+ readonly summary: "Clear application cache";
92
+ readonly description: "Clear server-side caches used by the current application instance.";
93
+ readonly parameters: readonly [];
94
+ readonly responses: {
95
+ readonly 200: {
96
+ readonly description: "OK";
97
+ };
98
+ };
99
+ };
100
+ };
101
+ };
102
+ export default _default;
@@ -0,0 +1,124 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+ var app_exports = {};
28
+ __export(app_exports, {
29
+ default: () => app_default
30
+ });
31
+ module.exports = __toCommonJS(app_exports);
32
+ var app_default = {
33
+ "/app:getLang": {
34
+ get: {
35
+ tags: ["app"],
36
+ summary: "Get the current application language",
37
+ description: "Return the current locale used by the server.",
38
+ parameters: [],
39
+ responses: {
40
+ 200: {
41
+ description: "OK",
42
+ content: {
43
+ "application/json": {
44
+ schema: {
45
+ type: "string"
46
+ }
47
+ }
48
+ }
49
+ }
50
+ }
51
+ }
52
+ },
53
+ "/app:getInfo": {
54
+ get: {
55
+ tags: ["app"],
56
+ summary: "Get application metadata",
57
+ description: "Return basic application information, including version and environment-related metadata.",
58
+ parameters: [],
59
+ responses: {
60
+ 200: {
61
+ description: "OK",
62
+ content: {
63
+ "application/json": {
64
+ schema: {
65
+ type: "object",
66
+ additionalProperties: true
67
+ }
68
+ }
69
+ }
70
+ }
71
+ }
72
+ }
73
+ },
74
+ "/app:getPlugins": {
75
+ get: {
76
+ tags: ["app"],
77
+ summary: "List loaded plugins",
78
+ description: "Return plugin metadata for the current application runtime.",
79
+ parameters: [],
80
+ responses: {
81
+ 200: {
82
+ description: "OK",
83
+ content: {
84
+ "application/json": {
85
+ schema: {
86
+ type: "array",
87
+ items: {
88
+ type: "object",
89
+ additionalProperties: true
90
+ }
91
+ }
92
+ }
93
+ }
94
+ }
95
+ }
96
+ }
97
+ },
98
+ "/app:restart": {
99
+ post: {
100
+ tags: ["app"],
101
+ summary: "Restart the application",
102
+ description: "Trigger an application restart asynchronously.",
103
+ parameters: [],
104
+ responses: {
105
+ 200: {
106
+ description: "OK"
107
+ }
108
+ }
109
+ }
110
+ },
111
+ "/app:clearCache": {
112
+ post: {
113
+ tags: ["app"],
114
+ summary: "Clear application cache",
115
+ description: "Clear server-side caches used by the current application instance.",
116
+ parameters: [],
117
+ responses: {
118
+ 200: {
119
+ description: "OK"
120
+ }
121
+ }
122
+ }
123
+ }
124
+ };