@nocobase/ai 2.0.0

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 (58) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +30 -0
  3. package/lib/ai-manager.d.ts +16 -0
  4. package/lib/ai-manager.js +49 -0
  5. package/lib/document-manager/document.d.ts +8 -0
  6. package/lib/document-manager/document.js +9 -0
  7. package/lib/document-manager/id-mapper.d.ts +17 -0
  8. package/lib/document-manager/id-mapper.js +79 -0
  9. package/lib/document-manager/index.d.ts +19 -0
  10. package/lib/document-manager/index.js +63 -0
  11. package/lib/document-manager/search-index.d.ts +19 -0
  12. package/lib/document-manager/search-index.js +69 -0
  13. package/lib/index.d.ts +12 -0
  14. package/lib/index.js +36 -0
  15. package/lib/loader/index.d.ts +11 -0
  16. package/lib/loader/index.js +34 -0
  17. package/lib/loader/scanner.d.ts +27 -0
  18. package/lib/loader/scanner.js +91 -0
  19. package/lib/loader/tools.d.ts +36 -0
  20. package/lib/loader/tools.js +151 -0
  21. package/lib/loader/types.d.ts +18 -0
  22. package/lib/loader/types.js +49 -0
  23. package/lib/loader/utils.d.ts +9 -0
  24. package/lib/loader/utils.js +51 -0
  25. package/lib/tools-manager/index.d.ts +23 -0
  26. package/lib/tools-manager/index.js +127 -0
  27. package/lib/tools-manager/types.d.ts +42 -0
  28. package/lib/tools-manager/types.js +24 -0
  29. package/package.json +21 -0
  30. package/src/__tests__/resource/ai/skills/document/tools/read.ts +23 -0
  31. package/src/__tests__/resource/ai/skills/document/tools/search/description.md +1 -0
  32. package/src/__tests__/resource/ai/skills/document/tools/search/index.ts +23 -0
  33. package/src/__tests__/resource/ai/tools/desc/description.md +1 -0
  34. package/src/__tests__/resource/ai/tools/desc/index.ts +23 -0
  35. package/src/__tests__/resource/ai/tools/empty.ts +9 -0
  36. package/src/__tests__/resource/ai/tools/group/group1.ts +23 -0
  37. package/src/__tests__/resource/ai/tools/group/group2.ts +23 -0
  38. package/src/__tests__/resource/ai/tools/group/group3/description.md +1 -0
  39. package/src/__tests__/resource/ai/tools/group/group3/index.ts +23 -0
  40. package/src/__tests__/resource/ai/tools/hallow/index.ts +23 -0
  41. package/src/__tests__/resource/ai/tools/ignored.ts +13 -0
  42. package/src/__tests__/resource/ai/tools/jsfiles/formFiller.js +61 -0
  43. package/src/__tests__/resource/ai/tools/jsfiles/formFiller2/index.js +61 -0
  44. package/src/__tests__/resource/ai/tools/print.ts +23 -0
  45. package/src/__tests__/tools.test.ts +133 -0
  46. package/src/ai-manager.ts +21 -0
  47. package/src/document-manager/document.ts +9 -0
  48. package/src/document-manager/id-mapper.ts +56 -0
  49. package/src/document-manager/index.ts +38 -0
  50. package/src/document-manager/search-index.ts +48 -0
  51. package/src/index.ts +13 -0
  52. package/src/loader/index.ts +12 -0
  53. package/src/loader/scanner.ts +54 -0
  54. package/src/loader/tools.ts +135 -0
  55. package/src/loader/types.ts +25 -0
  56. package/src/loader/utils.ts +14 -0
  57. package/src/tools-manager/index.ts +97 -0
  58. package/src/tools-manager/types.ts +50 -0
@@ -0,0 +1,23 @@
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
+ import { Context } from '@nocobase/actions';
11
+ import { defineTools } from '@nocobase/ai';
12
+
13
+ export default defineTools({
14
+ scope: 'GENERAL',
15
+ definition: {
16
+ name: 'group3',
17
+ description: 'hallow group3',
18
+ schema: null,
19
+ },
20
+ invoke: async (ctx: Context, args: any, id: string) => {
21
+ return { status: 'success' };
22
+ },
23
+ });
@@ -0,0 +1,23 @@
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
+ import { Context } from '@nocobase/actions';
11
+ import { defineTools } from '@nocobase/ai';
12
+
13
+ export default defineTools({
14
+ scope: 'GENERAL',
15
+ definition: {
16
+ name: 'hallow',
17
+ description: 'hallow tools',
18
+ schema: null,
19
+ },
20
+ invoke: async (ctx: Context, args: any, id: string) => {
21
+ return { status: 'success' };
22
+ },
23
+ });
@@ -0,0 +1,13 @@
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
+ import { Context } from '@nocobase/actions';
11
+ import { defineTools } from '@nocobase/ai';
12
+
13
+ export const IGNORE = true;
@@ -0,0 +1,61 @@
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) __defProp(target, name, { get: all[name], enumerable: true });
16
+ };
17
+ var __copyProps = (to, from, except, desc) => {
18
+ if ((from && typeof from === 'object') || typeof from === 'function') {
19
+ for (let key of __getOwnPropNames(from))
20
+ if (!__hasOwnProp.call(to, key) && key !== except)
21
+ __defProp(to, key, {
22
+ get: () => from[key],
23
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable,
24
+ });
25
+ }
26
+ return to;
27
+ };
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, '__esModule', { value: true }), mod);
29
+ var formFiller_exports = {};
30
+ __export(formFiller_exports, {
31
+ default: () => formFiller_default,
32
+ });
33
+ module.exports = __toCommonJS(formFiller_exports);
34
+ var import_ai = require('@nocobase/ai');
35
+ var import_zod = require('zod');
36
+ var formFiller_default = (0, import_ai.defineTools)({
37
+ scope: 'GENERAL',
38
+ defaultPermission: 'ALLOW',
39
+ execution: 'frontend',
40
+ introduction: {
41
+ title: '{{t("Form filler")}}',
42
+ about: '{{t("Fill the form with the given content")}}',
43
+ },
44
+ definition: {
45
+ name: 'formFiller',
46
+ description: 'Fill the form with the given content',
47
+ schema: import_zod.z.object({
48
+ form: import_zod.z.string().describe('The UI Schema ID of the target form to be filled.'),
49
+ data: import_zod.z.object({}).catchall(import_zod.z.any()).describe(
50
+ `Structured key-value pairs matching the form's JSON Schema,
51
+ to be assigned to form.values.
52
+ Example: { "username": "alice", "email": "alice@example.com", "age": 30 }`,
53
+ ),
54
+ }),
55
+ },
56
+ invoke: async () => {
57
+ return {
58
+ status: 'success',
59
+ };
60
+ },
61
+ });
@@ -0,0 +1,61 @@
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) __defProp(target, name, { get: all[name], enumerable: true });
16
+ };
17
+ var __copyProps = (to, from, except, desc) => {
18
+ if ((from && typeof from === 'object') || typeof from === 'function') {
19
+ for (let key of __getOwnPropNames(from))
20
+ if (!__hasOwnProp.call(to, key) && key !== except)
21
+ __defProp(to, key, {
22
+ get: () => from[key],
23
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable,
24
+ });
25
+ }
26
+ return to;
27
+ };
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, '__esModule', { value: true }), mod);
29
+ var formFiller_exports = {};
30
+ __export(formFiller_exports, {
31
+ default: () => formFiller_default,
32
+ });
33
+ module.exports = __toCommonJS(formFiller_exports);
34
+ var import_ai = require('@nocobase/ai');
35
+ var import_zod = require('zod');
36
+ var formFiller_default = (0, import_ai.defineTools)({
37
+ scope: 'GENERAL',
38
+ defaultPermission: 'ALLOW',
39
+ execution: 'frontend',
40
+ introduction: {
41
+ title: '{{t("Form filler")}}',
42
+ about: '{{t("Fill the form with the given content")}}',
43
+ },
44
+ definition: {
45
+ name: 'formFiller',
46
+ description: 'Fill the form with the given content',
47
+ schema: import_zod.z.object({
48
+ form: import_zod.z.string().describe('The UI Schema ID of the target form to be filled.'),
49
+ data: import_zod.z.object({}).catchall(import_zod.z.any()).describe(
50
+ `Structured key-value pairs matching the form's JSON Schema,
51
+ to be assigned to form.values.
52
+ Example: { "username": "alice", "email": "alice@example.com", "age": 30 }`,
53
+ ),
54
+ }),
55
+ },
56
+ invoke: async () => {
57
+ return {
58
+ status: 'success',
59
+ };
60
+ },
61
+ });
@@ -0,0 +1,23 @@
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
+ import { Context } from '@nocobase/actions';
11
+ import { defineTools } from '@nocobase/ai';
12
+
13
+ export default defineTools({
14
+ scope: 'GENERAL',
15
+ definition: {
16
+ name: 'print',
17
+ description: 'print tools',
18
+ schema: null,
19
+ },
20
+ invoke: async (ctx: Context, args: any, id: string) => {
21
+ return { status: 'success' };
22
+ },
23
+ });
@@ -0,0 +1,133 @@
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
+ import { createMockServer, MockServer } from '@nocobase/test';
11
+ import { ToolsLoader } from '../loader';
12
+ import path from 'path';
13
+ import { AIManager } from '../ai-manager';
14
+ import { ToolsManager } from '../tools-manager';
15
+
16
+ describe('Tools loader test cases', () => {
17
+ const basePath = path.join(process.cwd(), 'packages/core/ai/src/__tests__/resource/ai');
18
+ let app: MockServer;
19
+ let aiManager: AIManager;
20
+ let toolsManager: ToolsManager;
21
+ let loader: ToolsLoader;
22
+
23
+ beforeEach(async () => {
24
+ app = await createMockServer();
25
+ aiManager = app.aiManager;
26
+ toolsManager = aiManager.toolsManager;
27
+ loader = new ToolsLoader(aiManager, {
28
+ scan: {
29
+ basePath,
30
+ pattern: ['**/tools/**/*.ts', '**/tools/**/*.js', '!**/tools/**/*.d.ts', '**/tools/**/*/description.md'],
31
+ },
32
+ });
33
+ });
34
+
35
+ afterEach(async () => {
36
+ await app.destroy();
37
+ });
38
+
39
+ it('should load tools file in tools root directory ', async () => {
40
+ await loader.load();
41
+ const tools = await toolsManager.getTools('print');
42
+ expect(tools).toBeDefined();
43
+ expect(tools.definition.name).eq('print');
44
+ expect(tools.definition.description).eq('print tools');
45
+ expect(await tools.invoke(null, null, null)).toEqual({ status: 'success' });
46
+ });
47
+
48
+ it('should load tools directory in tools root directory ', async () => {
49
+ await loader.load();
50
+ const tools = await toolsManager.getTools('hallow');
51
+ expect(tools).toBeDefined();
52
+ expect(tools.definition.name).eq('hallow');
53
+ expect(tools.definition.description).eq('hallow tools');
54
+ expect(await tools.invoke(null, null, null)).toEqual({ status: 'success' });
55
+ });
56
+
57
+ it('should load description markdown file in tools directory ', async () => {
58
+ await loader.load();
59
+ const tools = await toolsManager.getTools('desc');
60
+ expect(tools).toBeDefined();
61
+ expect(tools.definition.name).eq('desc');
62
+ expect(tools.definition.description).eq('# DESC\n');
63
+ expect(await tools.invoke(null, null, null)).toEqual({ status: 'success' });
64
+ expect(await tools.invoke(null, null, null)).toEqual({ status: 'success' });
65
+ });
66
+
67
+ it('should load tools file in skills directory ', async () => {
68
+ await loader.load();
69
+ const tools = await toolsManager.getTools('read');
70
+ expect(tools).toBeDefined();
71
+ expect(tools.definition.name).eq('read');
72
+ expect(tools.definition.description).eq('read document');
73
+ expect(await tools.invoke(null, null, null)).toEqual({ status: 'success' });
74
+ });
75
+
76
+ it('should load tools directory in skills directory ', async () => {
77
+ await loader.load();
78
+ const tools = await toolsManager.getTools('search');
79
+ expect(tools).toBeDefined();
80
+ expect(tools.definition.name).eq('search');
81
+ expect(tools.definition.description).eq('# SEARCH\n');
82
+ expect(await tools.invoke(null, null, null)).toEqual({ status: 'success' });
83
+ });
84
+
85
+ it('should load tools when tools in directory for grouping', async () => {
86
+ await loader.load();
87
+ const group1 = await toolsManager.getTools('group1');
88
+ expect(group1).toBeDefined();
89
+ expect(group1.definition.name).eq('group1');
90
+ expect(group1.definition.description).eq('hallow group1');
91
+ expect(await group1.invoke(null, null, null)).toEqual({ status: 'success' });
92
+
93
+ const group2 = await toolsManager.getTools('group2');
94
+ expect(group2).toBeDefined();
95
+ expect(group2.definition.name).eq('group2');
96
+ expect(group2.definition.description).eq('hallow group2');
97
+ expect(await group2.invoke(null, null, null)).toEqual({ status: 'success' });
98
+
99
+ const group3 = await toolsManager.getTools('group3');
100
+ expect(group3).toBeDefined();
101
+ expect(group3.definition.name).eq('group3');
102
+ expect(group3.definition.description).eq('# GROUP3\n');
103
+ expect(await group3.invoke(null, null, null)).toEqual({ status: 'success' });
104
+ });
105
+
106
+ it('should ignore tools which not export default', async () => {
107
+ await loader.load();
108
+ const tools = await toolsManager.getTools('ignored');
109
+ expect(tools).toBeUndefined();
110
+ });
111
+
112
+ it.skip('should load .js file', async () => {
113
+ await loader.load();
114
+ const toolsNames = ['formFiller', 'formFiller2'];
115
+ for (const toolsName of toolsNames) {
116
+ const tools = await toolsManager.getTools(toolsName);
117
+ expect(tools).toBeDefined();
118
+ expect(tools.definition.name).eq(toolsName);
119
+ expect(tools.definition.description).eq('Fill the form with the given content');
120
+ expect(await tools.invoke(null, null, null)).toEqual({ status: 'success' });
121
+ }
122
+ });
123
+
124
+ it('should ignore empty file', async () => {
125
+ const toolsLoader = new ToolsLoader(aiManager, {
126
+ scan: { basePath, pattern: ['tools/empty.ts'] },
127
+ });
128
+ // const saved = process.env.VITEST;
129
+ delete process.env.VITEST;
130
+ await expect(toolsLoader.load()).resolves.not.toThrow();
131
+ // process.env.VITEST = saved;
132
+ });
133
+ });
@@ -0,0 +1,21 @@
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
+ import { DocumentManager } from './document-manager';
11
+ import { DefaultToolsManager, ToolsManager } from './tools-manager';
12
+
13
+ export class AIManager {
14
+ documentManager: DocumentManager;
15
+ toolsManager: ToolsManager;
16
+
17
+ constructor(protected readonly app: any) {
18
+ this.documentManager = new DocumentManager();
19
+ this.toolsManager = new DefaultToolsManager();
20
+ }
21
+ }
@@ -0,0 +1,9 @@
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
+
@@ -0,0 +1,56 @@
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
+ export class IdMapper {
11
+ private nextId = 1;
12
+ private ext2num = new Map<string, number>();
13
+ private num2ext = new Map<number, string>();
14
+
15
+ toNumeric(id: string | number): number {
16
+ if (typeof id === 'number') {
17
+ return id;
18
+ }
19
+
20
+ let num = this.ext2num.get(id);
21
+ if (!num) {
22
+ num = this.nextId++;
23
+ this.ext2num.set(id, num);
24
+ this.num2ext.set(num, id);
25
+ }
26
+ return num;
27
+ }
28
+
29
+ getNumeric(id: string | number): number | undefined {
30
+ if (typeof id === 'number') {
31
+ return id;
32
+ }
33
+
34
+ return this.ext2num.get(id);
35
+ }
36
+
37
+ toExternal(id: number): string | number {
38
+ return this.num2ext.get(id) ?? id;
39
+ }
40
+
41
+ remove(id: string | number) {
42
+ if (typeof id === 'number') {
43
+ const ext = this.num2ext.get(id);
44
+ if (ext) {
45
+ this.num2ext.delete(id);
46
+ this.ext2num.delete(ext);
47
+ }
48
+ } else {
49
+ const num = this.ext2num.get(id);
50
+ if (num) {
51
+ this.ext2num.delete(id);
52
+ this.num2ext.delete(num);
53
+ }
54
+ }
55
+ }
56
+ }
@@ -0,0 +1,38 @@
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
+ import { Document, DocumentOptions, IndexOptions } from 'flexsearch';
11
+ import { Index } from './search-index';
12
+
13
+ export class DocumentManager {
14
+ indexes: Map<string, Index> = new Map();
15
+ documents: Map<string, Document> = new Map();
16
+
17
+ addIndex(name: string, options?: IndexOptions) {
18
+ const index = new Index(options);
19
+ this.indexes.set(name, index);
20
+ return index;
21
+ }
22
+
23
+ getIndex(name: string): Index | undefined {
24
+ return this.indexes.get(name);
25
+ }
26
+
27
+ addDocument(name: string, options?: DocumentOptions) {
28
+ const doc = new Document(options);
29
+ this.documents.set(name, doc);
30
+ return doc;
31
+ }
32
+
33
+ getDocument(name: string): Document | undefined {
34
+ return this.documents.get(name);
35
+ }
36
+ }
37
+
38
+ export { Index as FlexSearchIndex } from 'flexsearch';
@@ -0,0 +1,48 @@
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
+ import { Index as BaseIndex, ExportHandler, IndexOptions } from 'flexsearch';
11
+ import { IdMapper } from './id-mapper';
12
+
13
+ export class Index {
14
+ private index: BaseIndex;
15
+ private ids = new IdMapper();
16
+
17
+ constructor(options?: IndexOptions) {
18
+ this.index = new BaseIndex(options);
19
+ }
20
+
21
+ async add(id: string | number, text: string) {
22
+ const numericId = this.ids.toNumeric(id);
23
+ return this.index.addAsync(numericId, text);
24
+ }
25
+
26
+ async remove(id: string | number) {
27
+ const numericId = this.ids.getNumeric(id);
28
+ if (numericId === undefined) {
29
+ return;
30
+ }
31
+
32
+ await this.index.removeAsync(numericId);
33
+ this.ids.remove(id);
34
+ }
35
+
36
+ async search(query: string, options?: any): Promise<(string | number)[]> {
37
+ const result = (await this.index.searchAsync(query, options)) as number[];
38
+ return result.map((id) => this.ids.toExternal(id));
39
+ }
40
+
41
+ export(handler: ExportHandler) {
42
+ return this.index.export(handler);
43
+ }
44
+
45
+ import(key: string, data: string) {
46
+ return this.index.import(key, data);
47
+ }
48
+ }
package/src/index.ts ADDED
@@ -0,0 +1,13 @@
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
+ export * from './ai-manager';
11
+ export * from './document-manager';
12
+ export * from './tools-manager';
13
+ export * from './loader';
@@ -0,0 +1,12 @@
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
+ export * from './types';
11
+ export * from './scanner';
12
+ export * from './tools';
@@ -0,0 +1,54 @@
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
+ import fg from 'fast-glob';
11
+ import path from 'path';
12
+
13
+ export type DirectoryScannerOptions = {
14
+ basePath: string;
15
+ pattern: string[];
16
+ };
17
+
18
+ export class DirectoryScanner {
19
+ protected readonly files: FileDescriptor[] = [];
20
+
21
+ constructor(private readonly options: DirectoryScannerOptions) {}
22
+
23
+ async scan(): Promise<FileDescriptor[]> {
24
+ const { basePath, pattern } = this.options;
25
+ const filePaths = await fg(pattern, {
26
+ cwd: basePath,
27
+ absolute: true,
28
+ onlyFiles: true,
29
+ dot: false,
30
+ followSymbolicLinks: false,
31
+ });
32
+
33
+ return filePaths.map((f) => new FileDescriptor(f));
34
+ }
35
+ }
36
+
37
+ export class FileDescriptor {
38
+ constructor(private readonly filePath: string) {}
39
+ get name() {
40
+ return path.parse(this.filePath).name;
41
+ }
42
+ get directory() {
43
+ return path.basename(path.dirname(this.filePath));
44
+ }
45
+ get path() {
46
+ return this.filePath;
47
+ }
48
+ get extname() {
49
+ return path.extname(this.filePath);
50
+ }
51
+ get basename() {
52
+ return path.basename(this.filePath);
53
+ }
54
+ }