plugin-build-guide-block 1.0.11 → 1.0.12

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 (40) hide show
  1. package/dist/client/index.js +1 -1
  2. package/dist/client/locale.d.ts +3 -0
  3. package/dist/client/models/UserGuideBlockModel.d.ts +3 -3
  4. package/dist/client/schemaSettings.d.ts +2 -0
  5. package/dist/client/schemas/spacesSchema.d.ts +41 -0
  6. package/dist/externalVersion.js +8 -8
  7. package/dist/locale/en-US.json +3 -0
  8. package/dist/locale/namespace.d.ts +6 -0
  9. package/dist/locale/namespace.js +36 -0
  10. package/dist/locale/vi-VN.json +3 -0
  11. package/dist/locale/zh-CN.json +3 -0
  12. package/dist/node_modules/marked/bin/main.js +279 -0
  13. package/dist/node_modules/marked/bin/marked.js +15 -0
  14. package/dist/node_modules/marked/lib/marked.cjs +1 -0
  15. package/dist/node_modules/marked/lib/marked.d.cts +657 -0
  16. package/dist/node_modules/marked/lib/marked.d.ts +657 -0
  17. package/dist/node_modules/marked/lib/marked.esm.js +2432 -0
  18. package/dist/node_modules/marked/lib/marked.umd.js +2456 -0
  19. package/dist/node_modules/marked/man/marked.1 +111 -0
  20. package/dist/node_modules/marked/marked.min.js +6 -0
  21. package/dist/node_modules/marked/package.json +1 -0
  22. package/dist/server/actions/build.js +66 -52
  23. package/dist/server/actions/getMarkdown.d.ts +2 -0
  24. package/dist/server/actions/getMarkdown.js +53 -0
  25. package/dist/server/collections/ai-build-guide-spaces.js +9 -0
  26. package/dist/server/plugin.js +8 -1
  27. package/package.json +51 -31
  28. package/src/client/locale.ts +18 -0
  29. package/src/client/plugin.tsx +52 -30
  30. package/src/client/schemaSettings.ts +75 -0
  31. package/src/client/schemas/spacesSchema.ts +42 -1
  32. package/src/locale/en-US.json +3 -0
  33. package/src/locale/namespace.ts +6 -0
  34. package/src/locale/vi-VN.json +3 -0
  35. package/src/locale/zh-CN.json +3 -0
  36. package/src/server/actions/build.ts +46 -33
  37. package/src/server/actions/getMarkdown.ts +26 -0
  38. package/src/server/collections/ai-build-guide-spaces.ts +9 -0
  39. package/src/server/plugin.ts +83 -76
  40. package/src/server/collections/.gitkeep +0 -0
@@ -1,30 +1,52 @@
1
- import { Plugin } from '@nocobase/client';
2
- import { UserGuideManager } from './UserGuideManager';
3
- import { UserGuideBlockProvider } from './UserGuideBlockProvider';
4
- import { UserGuideBlockInitializer } from './UserGuideBlockInitializer';
5
- import { UserGuideBlockModel } from './models/UserGuideBlockModel';
6
-
7
- export class PluginBuildGuideBlockClient extends Plugin {
8
- async load() {
9
- this.app.pluginSettingsManager.add('ai-build-guide', {
10
- icon: 'ReadOutlined',
11
- title: '{{t("Build Guide Block", { ns: "build-guide-block" })}}',
12
- Component: UserGuideManager,
13
- aclSnippet: 'pm.ai-build-guide',
14
- });
15
-
16
- this.app.use(UserGuideBlockProvider);
17
-
18
- const blocksInit = this.app.schemaInitializerManager.get('page:addBlock');
19
- blocksInit?.add('otherBlocks.aiUserGuide', {
20
- title: '{{t("User Guide", { ns: "build-guide-block" })}}',
21
- Component: 'UserGuideBlockInitializer',
22
- });
23
-
24
- this.flowEngine.registerModels({
25
- UserGuideBlockModel,
26
- });
27
- }
28
- }
29
-
30
- export default PluginBuildGuideBlockClient;
1
+ import { Plugin, RemoteSelect } from '@nocobase/client';
2
+ import { UserGuideManager } from './UserGuideManager';
3
+ import { UserGuideBlockProvider } from './UserGuideBlockProvider';
4
+ import { UserGuideBlockInitializer } from './UserGuideBlockInitializer';
5
+ import { UserGuideBlock } from './UserGuideBlock';
6
+ import { UserGuideBlockModel } from './models/UserGuideBlockModel';
7
+ import { userGuideBlockSettings } from './schemaSettings';
8
+ import { BuildButton } from './components/BuildButton';
9
+ import { LLMServiceSelect } from './components/LLMServiceSelect';
10
+ import { ModelSelect } from './components/ModelSelect';
11
+ import { StatusTag } from './components/StatusTag';
12
+ import { namespace } from './locale';
13
+
14
+ export class PluginBuildGuideBlockClient extends Plugin {
15
+ async load() {
16
+ this.app.addComponents({
17
+ UserGuideBlock,
18
+ UserGuideBlockInitializer,
19
+ BuildButton,
20
+ LLMServiceSelect,
21
+ ModelSelect,
22
+ StatusTag,
23
+ RemoteSelect,
24
+ });
25
+
26
+ this.app.schemaSettingsManager.add(userGuideBlockSettings);
27
+
28
+ this.app.use(UserGuideBlockProvider);
29
+
30
+ this.app.pluginSettingsManager.add('ai-build-guide', {
31
+ icon: 'ReadOutlined',
32
+ title: `{{t("Build Guide Block", { ns: "${namespace}" })}}`,
33
+ Component: UserGuideManager,
34
+ aclSnippet: 'pm.ai-build-guide',
35
+ });
36
+
37
+ const initializerItem = {
38
+ title: `{{t("User Guide", { ns: "${namespace}" })}}`,
39
+ Component: 'UserGuideBlockInitializer',
40
+ };
41
+
42
+ this.app.schemaInitializerManager.addItem('page:addBlock', 'otherBlocks.aiUserGuide', initializerItem);
43
+ this.app.schemaInitializerManager.addItem('popup:common:addBlock', 'otherBlocks.aiUserGuide', initializerItem);
44
+ this.app.schemaInitializerManager.addItem('popup:addNew:addBlock', 'otherBlocks.aiUserGuide', initializerItem);
45
+
46
+ this.flowEngine.registerModels({
47
+ UserGuideBlockModel,
48
+ });
49
+ }
50
+ }
51
+
52
+ export default PluginBuildGuideBlockClient;
@@ -0,0 +1,75 @@
1
+ import { useFieldSchema } from '@formily/react';
2
+ import { SchemaSettings, useDesignable } from '@nocobase/client';
3
+ import { useT } from './locale';
4
+
5
+ export const userGuideBlockSettings = new SchemaSettings({
6
+ name: 'userGuideBlockSettings',
7
+ items: [
8
+ {
9
+ name: 'selectSpace',
10
+ type: 'modal',
11
+ useComponentProps() {
12
+ const fieldSchema = useFieldSchema();
13
+ const { dn } = useDesignable();
14
+ const t = useT();
15
+
16
+ const currentSpaceId = fieldSchema?.['x-component-props']?.spaceId || '';
17
+
18
+ return {
19
+ title: t('Select Space'),
20
+ schema: {
21
+ type: 'object',
22
+ properties: {
23
+ spaceId: {
24
+ title: t('Space'),
25
+ type: 'string',
26
+ 'x-decorator': 'FormItem',
27
+ 'x-component': 'RemoteSelect',
28
+ 'x-component-props': {
29
+ showSearch: true,
30
+ fieldNames: { label: 'title', value: 'id' },
31
+ service: {
32
+ resource: 'aiBuildGuideSpaces',
33
+ action: 'list',
34
+ params: {
35
+ filter: { status: 'completed' },
36
+ },
37
+ },
38
+ },
39
+ default: currentSpaceId,
40
+ required: true,
41
+ },
42
+ },
43
+ },
44
+ onSubmit({ spaceId }: { spaceId: string }) {
45
+ const componentProps = { ...fieldSchema['x-component-props'], spaceId };
46
+ fieldSchema['x-component-props'] = componentProps;
47
+ dn.emit('patch', {
48
+ schema: {
49
+ 'x-uid': fieldSchema['x-uid'],
50
+ 'x-component-props': componentProps,
51
+ },
52
+ });
53
+ dn.refresh();
54
+ },
55
+ };
56
+ },
57
+ },
58
+ {
59
+ name: 'divider',
60
+ type: 'divider',
61
+ },
62
+ {
63
+ name: 'delete',
64
+ type: 'remove',
65
+ useComponentProps() {
66
+ return {
67
+ removeParentsIfNoChildren: true,
68
+ breakRemoveOn: {
69
+ 'x-component': 'Grid',
70
+ },
71
+ };
72
+ },
73
+ },
74
+ ],
75
+ });
@@ -75,6 +75,18 @@ export const spacesSchema = {
75
75
  },
76
76
  },
77
77
  },
78
+ outputFormat: {
79
+ type: 'string',
80
+ title: '{{t("Output format")}}',
81
+ required: true,
82
+ default: 'html',
83
+ 'x-decorator': 'FormItem',
84
+ 'x-component': 'Select',
85
+ enum: [
86
+ { label: 'HTML', value: 'html' },
87
+ { label: 'Markdown', value: 'markdown' },
88
+ ],
89
+ },
78
90
  systemPrompt: {
79
91
  type: 'string',
80
92
  title: '{{t("System Prompt")}}',
@@ -84,7 +96,7 @@ export const spacesSchema = {
84
96
  rows: 4,
85
97
  },
86
98
  default:
87
- 'You are an expert technical writer. Please generate a comprehensive HTML user guide based on the provided documents. Ensure the output is valid HTML and does not include Markdown syntax like ```html blocks.',
99
+ 'You are an expert technical writer. Generate a comprehensive user guide based on the provided documents.',
88
100
  },
89
101
  documents: {
90
102
  type: 'array',
@@ -231,6 +243,17 @@ export const spacesSchema = {
231
243
  'x-decorator': 'FormItem',
232
244
  'x-component': 'ModelSelect',
233
245
  },
246
+ outputFormat: {
247
+ type: 'string',
248
+ title: '{{t("Output format")}}',
249
+ required: true,
250
+ 'x-decorator': 'FormItem',
251
+ 'x-component': 'Select',
252
+ enum: [
253
+ { label: 'HTML', value: 'html' },
254
+ { label: 'Markdown', value: 'markdown' },
255
+ ],
256
+ },
234
257
  systemPrompt: {
235
258
  type: 'string',
236
259
  title: '{{t("System Prompt")}}',
@@ -261,6 +284,24 @@ export const spacesSchema = {
261
284
  },
262
285
  'x-read-pretty': true,
263
286
  },
287
+ generatedMarkdown: {
288
+ type: 'string',
289
+ title: '{{t("Generated Markdown")}}',
290
+ 'x-decorator': 'FormItem',
291
+ 'x-component': 'Input.TextArea',
292
+ 'x-component-props': {
293
+ rows: 6,
294
+ },
295
+ 'x-read-pretty': true,
296
+ 'x-reactions': {
297
+ dependencies: ['outputFormat'],
298
+ fulfill: {
299
+ state: {
300
+ visible: '{{$deps[0] === "markdown"}}',
301
+ },
302
+ },
303
+ },
304
+ },
264
305
  buildLog: {
265
306
  type: 'string',
266
307
  title: '{{t("Build Log")}}',
@@ -16,6 +16,9 @@
16
16
  "Edit": "Edit",
17
17
  "Edit space": "Edit space",
18
18
  "Generated HTML": "Generated HTML",
19
+ "Generated Markdown": "Generated Markdown",
20
+ "Output format": "Output format",
21
+ "Select Space": "Select Space",
19
22
  "Build Log": "Build Log",
20
23
  "Delete": "Delete",
21
24
  "Saved successfully": "Saved successfully",
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Shared namespace constant.
3
+ * Matches the namespace already used in inline schema strings
4
+ * such as {{t("...", { ns: "build-guide-block" })}}.
5
+ */
6
+ export const name = 'build-guide-block';
@@ -16,6 +16,9 @@
16
16
  "Edit": "Sửa",
17
17
  "Edit space": "Sửa Space",
18
18
  "Generated HTML": "HTML Đã tạo",
19
+ "Generated Markdown": "Markdown Đã tạo",
20
+ "Output format": "Định dạng đầu ra",
21
+ "Select Space": "Chọn Space",
19
22
  "Build Log": "Log quá trình Build",
20
23
  "Delete": "Xóa",
21
24
  "Saved successfully": "Lưu thành công",
@@ -16,6 +16,9 @@
16
16
  "Edit": "编辑",
17
17
  "Edit space": "编辑空间",
18
18
  "Generated HTML": "生成的 HTML",
19
+ "Generated Markdown": "生成的 Markdown",
20
+ "Output format": "输出格式",
21
+ "Select Space": "选择空间",
19
22
  "Build Log": "构建日志",
20
23
  "Delete": "删除",
21
24
  "Saved successfully": "保存成功",
@@ -9,6 +9,28 @@ import { HumanMessage, SystemMessage } from '@langchain/core/messages';
9
9
  import axios from 'axios';
10
10
  import fs from 'fs';
11
11
  import path from 'path';
12
+ import { marked } from 'marked';
13
+
14
+ const SANITIZE_OPTIONS: sanitizeHtml.IOptions = {
15
+ allowedTags: [
16
+ 'div', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
17
+ 'ul', 'ol', 'li', 'table', 'thead', 'tbody', 'tr', 'td', 'th',
18
+ 'a', 'img', 'span', 'strong', 'em', 'code', 'pre', 'blockquote', 'br', 'hr',
19
+ ],
20
+ allowedAttributes: {
21
+ a: ['href', 'target'],
22
+ img: ['src', 'alt', 'width', 'height'],
23
+ '*': ['style', 'class'],
24
+ },
25
+ allowedStyles: {
26
+ '*': {
27
+ color: [/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/, /^rgb/, /^rgba/],
28
+ 'background-color': [/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/, /^rgb/, /^rgba/],
29
+ 'text-align': [/^left$/, /^right$/, /^center$/, /^justify$/],
30
+ 'font-size': [/^\d+(?:px|em|%)$/],
31
+ },
32
+ },
33
+ };
12
34
 
13
35
  async function fetchFileContent(app: any, file: any): Promise<string> {
14
36
  const fileManager = app.pm.get('file-manager') as PluginFileManagerServer;
@@ -87,7 +109,8 @@ export async function build(ctx: Context, next: Next) {
87
109
  throw new Error('Plugin AI is not available');
88
110
  }
89
111
 
90
- const { llmService, model, systemPrompt } = space.get();
112
+ const { llmService, model, systemPrompt, outputFormat } = space.get();
113
+ const format = outputFormat === 'markdown' ? 'markdown' : 'html';
91
114
 
92
115
  if (!llmService || !model) {
93
116
  throw new Error('LLM Service or model is missing in space configuration');
@@ -101,44 +124,34 @@ export async function build(ctx: Context, next: Next) {
101
124
  messages.push(new SystemMessage(systemPrompt));
102
125
  }
103
126
 
104
- const instruction = `Please generate an HTML user guide based on the following documents. Output ONLY valid HTML without Markdown blocks.\n\nDocuments:\n${documentsText}`;
127
+ const instruction =
128
+ format === 'markdown'
129
+ ? `Please generate a comprehensive user guide in pure Markdown based on the following documents. Output ONLY Markdown content (no HTML wrappers, no code-fence around the whole document).\n\nDocuments:\n${documentsText}`
130
+ : `Please generate an HTML user guide based on the following documents. Output ONLY valid HTML without Markdown blocks.\n\nDocuments:\n${documentsText}`;
105
131
  messages.push(new HumanMessage(instruction));
106
132
 
107
133
  const response = await provider.chatModel.invoke(messages);
108
- let rawHtml = typeof response.content === 'string' ? response.content : JSON.stringify(response.content);
109
-
110
- // Strip markdown code block if present
111
- rawHtml = rawHtml.replace(/^```html\s*/, '').replace(/```\s*$/, '');
112
-
113
- // 2c. Sanitize HTML output
114
- const cleanHtml = sanitizeHtml(rawHtml, {
115
- allowedTags: [
116
- 'div', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
117
- 'ul', 'ol', 'li', 'table', 'thead', 'tbody', 'tr', 'td', 'th',
118
- 'a', 'img', 'span', 'strong', 'em', 'code', 'pre', 'blockquote', 'br', 'hr'
119
- ],
120
- allowedAttributes: {
121
- 'a': ['href', 'target'],
122
- 'img': ['src', 'alt', 'width', 'height'],
123
- '*': ['style', 'class']
124
- },
125
- allowedStyles: {
126
- '*': {
127
- 'color': [/^\#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/, /^rgb/, /^rgba/],
128
- 'background-color': [/^\#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/, /^rgb/, /^rgba/],
129
- 'text-align': [/^left$/, /^right$/, /^center$/, /^justify$/],
130
- 'font-size': [/^\d+(?:px|em|%)$/]
131
- }
132
- }
133
- });
134
+ const rawText =
135
+ typeof response.content === 'string' ? response.content : JSON.stringify(response.content);
136
+
137
+ const updateValues: Record<string, unknown> = { status: 'completed' };
138
+
139
+ if (format === 'markdown') {
140
+ const rawMarkdown = rawText.replace(/^```(?:markdown|md)?\s*/, '').replace(/```\s*$/, '');
141
+ const renderedHtml = await marked.parse(rawMarkdown, { async: true });
142
+ const cleanHtml = sanitizeHtml(renderedHtml, SANITIZE_OPTIONS);
143
+ updateValues.generatedMarkdown = rawMarkdown;
144
+ updateValues.generatedHtml = cleanHtml;
145
+ } else {
146
+ const rawHtml = rawText.replace(/^```html\s*/, '').replace(/```\s*$/, '');
147
+ const cleanHtml = sanitizeHtml(rawHtml, SANITIZE_OPTIONS);
148
+ updateValues.generatedHtml = cleanHtml;
149
+ updateValues.generatedMarkdown = null;
150
+ }
134
151
 
135
- // 2d. Save generated HTML
136
152
  await bgRepo.update({
137
153
  filterByTk,
138
- values: {
139
- generatedHtml: cleanHtml,
140
- status: 'completed',
141
- },
154
+ values: updateValues,
142
155
  });
143
156
  })();
144
157
 
@@ -0,0 +1,26 @@
1
+ import { Context, Next } from '@nocobase/actions';
2
+ import { Repository } from '@nocobase/database';
3
+
4
+ export async function getMarkdown(ctx: Context, next: Next) {
5
+ const { filterByTk } = ctx.action.params;
6
+ const { resourceName } = ctx.action;
7
+ const repository = ctx.db.getRepository<any>(resourceName) as Repository;
8
+ const model = await repository.findById(filterByTk);
9
+
10
+ if (!model) {
11
+ ctx.throw(404, 'User Guide not found');
12
+ }
13
+
14
+ if (model.get('status') !== 'completed') {
15
+ ctx.throw(400, 'User Guide is not ready yet');
16
+ }
17
+
18
+ ctx.body = model.get('generatedMarkdown') || '';
19
+ ctx.withoutDataWrapping = true;
20
+
21
+ ctx.set({
22
+ 'Content-Type': 'text/markdown; charset=UTF-8',
23
+ });
24
+
25
+ await next();
26
+ }
@@ -28,10 +28,19 @@ export default defineCollection({
28
28
  type: 'text',
29
29
  name: 'systemPrompt',
30
30
  },
31
+ {
32
+ type: 'string',
33
+ name: 'outputFormat',
34
+ defaultValue: 'html',
35
+ },
31
36
  {
32
37
  type: 'text',
33
38
  name: 'generatedHtml',
34
39
  },
40
+ {
41
+ type: 'text',
42
+ name: 'generatedMarkdown',
43
+ },
35
44
  {
36
45
  type: 'string',
37
46
  name: 'status',
@@ -1,76 +1,83 @@
1
- import { InstallOptions, Plugin } from '@nocobase/server';
2
- import path from 'path';
3
- import { build } from './actions/build';
4
- import { getHtml } from './actions/getHtml';
5
-
6
- export class PluginBuildGuideBlockServer extends Plugin {
7
- afterAdd() {}
8
-
9
- beforeLoad() {}
10
-
11
- async load() {
12
- this.app.resourceManager.registerActionHandlers({
13
- 'aiBuildGuideSpaces:build': build,
14
- 'aiBuildGuideSpaces:getHtml': getHtml,
15
- });
16
-
17
- this.app.acl.allow('aiBuildGuideSpaces', 'getHtml', 'loggedIn');
18
- this.app.acl.registerSnippet({
19
- name: 'pm.ai-build-guide',
20
- actions: [
21
- 'aiBuildGuideSpaces:create',
22
- 'aiBuildGuideSpaces:update',
23
- 'aiBuildGuideSpaces:destroy',
24
- 'aiBuildGuideSpaces:list',
25
- 'aiBuildGuideSpaces:get',
26
- 'aiBuildGuideSpaces:build',
27
- ],
28
- });
29
-
30
- // Recover stale "building" status after server restart
31
- this.app.on('afterStart', async () => {
32
- try {
33
- const repo = this.db.getRepository('aiBuildGuideSpaces');
34
- await repo.update({
35
- filter: { status: 'building' },
36
- values: {
37
- status: 'error',
38
- buildLog: 'Build interrupted by server restart',
39
- },
40
- });
41
- } catch (err) {
42
- this.app.logger.warn('[plugin-build-guide-block] Failed to recover stale builds', err);
43
- }
44
- });
45
- }
46
-
47
- async install(options?: InstallOptions) {
48
- const collection = this.db.getCollection('aiBuildGuideSpaces');
49
- if (collection) {
50
- await collection.model.sync();
51
- }
52
- const repo = this.db.getRepository<any>('collections');
53
- if (repo) {
54
- await repo.db2cm('aiBuildGuideSpaces');
55
- }
56
- }
57
-
58
- async upgrade() {
59
- const collection = this.db.getCollection('aiBuildGuideSpaces');
60
- if (collection) {
61
- await collection.model.sync();
62
- }
63
- const repo = this.db.getRepository<any>('collections');
64
- if (repo) {
65
- await repo.db2cm('aiBuildGuideSpaces');
66
- }
67
- }
68
-
69
- async afterEnable() {}
70
-
71
- async afterDisable() {}
72
-
73
- async remove() {}
74
- }
75
-
76
- export default PluginBuildGuideBlockServer;
1
+ import { InstallOptions, Plugin } from '@nocobase/server';
2
+ import { resolve } from 'path';
3
+ import { build } from './actions/build';
4
+ import { getHtml } from './actions/getHtml';
5
+ import { getMarkdown } from './actions/getMarkdown';
6
+
7
+ export class PluginBuildGuideBlockServer extends Plugin {
8
+ afterAdd() {}
9
+
10
+ beforeLoad() {}
11
+
12
+ async load() {
13
+ await this.db.import({
14
+ directory: resolve(__dirname, 'collections'),
15
+ });
16
+
17
+ this.app.resourceManager.registerActionHandlers({
18
+ 'aiBuildGuideSpaces:build': build,
19
+ 'aiBuildGuideSpaces:getHtml': getHtml,
20
+ 'aiBuildGuideSpaces:getMarkdown': getMarkdown,
21
+ });
22
+
23
+ this.app.acl.allow('aiBuildGuideSpaces', 'getHtml', 'loggedIn');
24
+ this.app.acl.allow('aiBuildGuideSpaces', 'getMarkdown', 'loggedIn');
25
+ this.app.acl.registerSnippet({
26
+ name: 'pm.ai-build-guide',
27
+ actions: [
28
+ 'aiBuildGuideSpaces:create',
29
+ 'aiBuildGuideSpaces:update',
30
+ 'aiBuildGuideSpaces:destroy',
31
+ 'aiBuildGuideSpaces:list',
32
+ 'aiBuildGuideSpaces:get',
33
+ 'aiBuildGuideSpaces:build',
34
+ ],
35
+ });
36
+
37
+ // Recover stale "building" status after server restart
38
+ this.app.on('afterStart', async () => {
39
+ try {
40
+ const repo = this.db.getRepository('aiBuildGuideSpaces');
41
+ await repo.update({
42
+ filter: { status: 'building' },
43
+ values: {
44
+ status: 'error',
45
+ buildLog: 'Build interrupted by server restart',
46
+ },
47
+ });
48
+ } catch (err) {
49
+ this.app.logger.warn('[plugin-build-guide-block] Failed to recover stale builds', err);
50
+ }
51
+ });
52
+ }
53
+
54
+ async install(options?: InstallOptions) {
55
+ const collection = this.db.getCollection('aiBuildGuideSpaces');
56
+ if (collection) {
57
+ await collection.model.sync();
58
+ }
59
+ const repo = this.db.getRepository<any>('collections');
60
+ if (repo) {
61
+ await repo.db2cm('aiBuildGuideSpaces');
62
+ }
63
+ }
64
+
65
+ async upgrade() {
66
+ const collection = this.db.getCollection('aiBuildGuideSpaces');
67
+ if (collection) {
68
+ await collection.model.sync();
69
+ }
70
+ const repo = this.db.getRepository<any>('collections');
71
+ if (repo) {
72
+ await repo.db2cm('aiBuildGuideSpaces');
73
+ }
74
+ }
75
+
76
+ async afterEnable() {}
77
+
78
+ async afterDisable() {}
79
+
80
+ async remove() {}
81
+ }
82
+
83
+ export default PluginBuildGuideBlockServer;
File without changes