@aigne/doc-smith 0.8.12-beta.1 → 0.8.12-beta.3
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/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +15 -0
- package/agents/publish/publish-docs.mjs +3 -3
- package/agents/update/check-document.mjs +5 -1
- package/agents/update/save-and-translate-document.mjs +9 -4
- package/agents/update/user-review-document.mjs +7 -1
- package/package.json +1 -1
- package/prompts/detail/update/user-prompt.md +2 -2
- package/tests/agents/update/save-and-translate-document.test.mjs +369 -0
- package/utils/auth-utils.mjs +3 -2
- package/utils/constants/index.mjs +2 -1
- package/utils/deploy.mjs +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.8.12-beta.3](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.8.12-beta.2...v0.8.12-beta.3) (2025-10-09)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* should not require admin when publish to cloud ([0a74dd1](https://github.com/AIGNE-io/aigne-doc-smith/commit/0a74dd19a2c2390ca223d0cc393661bf3f408a6f))
|
|
9
|
+
|
|
10
|
+
## [0.8.12-beta.2](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.8.12-beta.1...v0.8.12-beta.2) (2025-10-09)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* fix update document include path ([#166](https://github.com/AIGNE-io/aigne-doc-smith/issues/166)) ([40c3aa2](https://github.com/AIGNE-io/aigne-doc-smith/commit/40c3aa2739cb35d36fb1daed87a0e23c81285328))
|
|
16
|
+
* resolve failure in document update and generate ([#168](https://github.com/AIGNE-io/aigne-doc-smith/issues/168)) ([c00c759](https://github.com/AIGNE-io/aigne-doc-smith/commit/c00c759473a01ce3d16b89f2bfa974f43420b82a))
|
|
17
|
+
|
|
3
18
|
## [0.8.12-beta.1](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.8.12-beta...v0.8.12-beta.1) (2025-10-08)
|
|
4
19
|
|
|
5
20
|
|
|
@@ -6,7 +6,7 @@ import fs from "fs-extra";
|
|
|
6
6
|
|
|
7
7
|
import { getAccessToken } from "../../utils/auth-utils.mjs";
|
|
8
8
|
import {
|
|
9
|
-
|
|
9
|
+
CLOUD_SERVICE_URL_PROD,
|
|
10
10
|
DISCUSS_KIT_STORE_URL,
|
|
11
11
|
DOC_SMITH_DIR,
|
|
12
12
|
TMP_DIR,
|
|
@@ -45,7 +45,7 @@ export default async function publishDocs(
|
|
|
45
45
|
|
|
46
46
|
// Check if appUrl is default and not saved in config (only when not using env variable)
|
|
47
47
|
const config = await loadConfigFromFile();
|
|
48
|
-
const isDefaultAppUrl = appUrl ===
|
|
48
|
+
const isDefaultAppUrl = appUrl === CLOUD_SERVICE_URL_PROD;
|
|
49
49
|
const hasAppUrlInConfig = config?.appUrl;
|
|
50
50
|
|
|
51
51
|
let token = "";
|
|
@@ -207,7 +207,7 @@ publishDocs.input_schema = {
|
|
|
207
207
|
appUrl: {
|
|
208
208
|
type: "string",
|
|
209
209
|
description: "The url of the app",
|
|
210
|
-
default:
|
|
210
|
+
default: CLOUD_SERVICE_URL_PROD,
|
|
211
211
|
},
|
|
212
212
|
boardId: {
|
|
213
213
|
type: "string",
|
|
@@ -112,7 +112,11 @@ export default async function checkDocument(
|
|
|
112
112
|
|
|
113
113
|
const teamAgent = TeamAgent.from({
|
|
114
114
|
name: "generateDocument",
|
|
115
|
-
skills: [
|
|
115
|
+
skills: [
|
|
116
|
+
options.context.agents["handleDocumentUpdate"],
|
|
117
|
+
options.context.agents["translateMultilingual"],
|
|
118
|
+
options.context.agents["saveSingleDoc"],
|
|
119
|
+
],
|
|
116
120
|
});
|
|
117
121
|
|
|
118
122
|
const result = await options.context.invoke(teamAgent, {
|
|
@@ -3,6 +3,10 @@ import { recordUpdate } from "../../utils/history-utils.mjs";
|
|
|
3
3
|
export default async function saveAndTranslateDocument(input, options) {
|
|
4
4
|
const { selectedDocs, docsDir, translateLanguages, locale } = input;
|
|
5
5
|
|
|
6
|
+
if (!Array.isArray(selectedDocs) || selectedDocs.length === 0) {
|
|
7
|
+
return {};
|
|
8
|
+
}
|
|
9
|
+
|
|
6
10
|
// Saves a document with optional translation data
|
|
7
11
|
const saveDocument = async (doc, translates = null, isTranslate = false) => {
|
|
8
12
|
const saveAgent = options.context.agents["saveSingleDoc"];
|
|
@@ -40,7 +44,7 @@ export default async function saveAndTranslateDocument(input, options) {
|
|
|
40
44
|
shouldTranslate = choice === "yes";
|
|
41
45
|
}
|
|
42
46
|
|
|
43
|
-
//
|
|
47
|
+
// Save documents in batches
|
|
44
48
|
const batchSize = 3;
|
|
45
49
|
for (let i = 0; i < selectedDocs.length; i += batchSize) {
|
|
46
50
|
const batch = selectedDocs.slice(i, i + batchSize);
|
|
@@ -56,8 +60,6 @@ export default async function saveAndTranslateDocument(input, options) {
|
|
|
56
60
|
feedback: doc.feedback.trim(),
|
|
57
61
|
documentPath: doc.path,
|
|
58
62
|
});
|
|
59
|
-
// clear feedback
|
|
60
|
-
doc.feedback = "";
|
|
61
63
|
}
|
|
62
64
|
} catch (error) {
|
|
63
65
|
console.error(`❌ Failed to save document ${doc.path}:`, error.message);
|
|
@@ -80,8 +82,11 @@ export default async function saveAndTranslateDocument(input, options) {
|
|
|
80
82
|
|
|
81
83
|
const translatePromises = batch.map(async (doc) => {
|
|
82
84
|
try {
|
|
85
|
+
// Clear feedback to ensure translation is not affected by update feedback
|
|
86
|
+
doc.feedback = "";
|
|
87
|
+
|
|
83
88
|
const result = await options.context.invoke(translateAgent, {
|
|
84
|
-
...input, //
|
|
89
|
+
...input, // context is required
|
|
85
90
|
content: doc.content,
|
|
86
91
|
translates: doc.translates,
|
|
87
92
|
title: doc.title,
|
|
@@ -245,7 +245,13 @@ export default async function userReviewDocument(
|
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
-
return {
|
|
248
|
+
return {
|
|
249
|
+
title,
|
|
250
|
+
description,
|
|
251
|
+
...rest,
|
|
252
|
+
content: options.context.userContext.currentContent,
|
|
253
|
+
feedback: feedbacks.join(". "),
|
|
254
|
+
};
|
|
249
255
|
}
|
|
250
256
|
|
|
251
257
|
userReviewDocument.taskTitle = "User review and modify document content";
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
</user_rules>
|
|
10
10
|
|
|
11
11
|
{% set operation_type = "optimizing" %}
|
|
12
|
-
{% include "
|
|
12
|
+
{% include "../../common/document/user-preferences.md" %}
|
|
13
13
|
|
|
14
14
|
<original_page_content>
|
|
15
15
|
{{originalContent}}
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
{{ assetsContent }}
|
|
26
26
|
</media_list>
|
|
27
27
|
|
|
28
|
-
{% include "
|
|
28
|
+
{% include "../../common/document/media-handling-rules.md" %}
|
|
29
29
|
</datasources>
|
|
30
30
|
|
|
31
31
|
<user_feedback>
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from "bun:test";
|
|
2
|
+
import saveAndTranslateDocument from "../../../agents/update/save-and-translate-document.mjs";
|
|
3
|
+
import * as historyUtils from "../../../utils/history-utils.mjs";
|
|
4
|
+
|
|
5
|
+
describe("save-and-translate-document", () => {
|
|
6
|
+
let mockOptions;
|
|
7
|
+
let consoleErrorSpy;
|
|
8
|
+
let recordUpdateSpy;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
// Reset all mocks
|
|
12
|
+
mock.restore();
|
|
13
|
+
|
|
14
|
+
mockOptions = {
|
|
15
|
+
prompts: {
|
|
16
|
+
select: mock(async () => "no"),
|
|
17
|
+
},
|
|
18
|
+
context: {
|
|
19
|
+
agents: {
|
|
20
|
+
saveSingleDoc: { mockSaveAgent: true },
|
|
21
|
+
translateMultilingual: { mockTranslateAgent: true },
|
|
22
|
+
},
|
|
23
|
+
invoke: mock(async () => ({ mockResult: true })),
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
consoleErrorSpy = spyOn(console, "error").mockImplementation(() => {});
|
|
28
|
+
recordUpdateSpy = spyOn(historyUtils, "recordUpdate").mockImplementation(() => {});
|
|
29
|
+
|
|
30
|
+
// Clear context mock call history
|
|
31
|
+
mockOptions.prompts.select.mockClear();
|
|
32
|
+
mockOptions.context.invoke.mockClear();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
consoleErrorSpy?.mockRestore();
|
|
37
|
+
recordUpdateSpy?.mockRestore();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// INPUT VALIDATION TESTS
|
|
41
|
+
test("should handle empty or invalid selectedDocs", async () => {
|
|
42
|
+
const testCases = [
|
|
43
|
+
{ selectedDocs: [], description: "empty array" },
|
|
44
|
+
{ selectedDocs: null, description: "null" },
|
|
45
|
+
{ selectedDocs: undefined, description: "undefined" },
|
|
46
|
+
{ selectedDocs: "not-array", description: "non-array" },
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
for (const testCase of testCases) {
|
|
50
|
+
const input = {
|
|
51
|
+
selectedDocs: testCase.selectedDocs,
|
|
52
|
+
docsDir: "./docs",
|
|
53
|
+
translateLanguages: ["en", "zh"],
|
|
54
|
+
locale: "en",
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const result = await saveAndTranslateDocument(input, mockOptions);
|
|
58
|
+
|
|
59
|
+
expect(result).toEqual({});
|
|
60
|
+
expect(mockOptions.context.invoke).not.toHaveBeenCalled();
|
|
61
|
+
expect(mockOptions.prompts.select).not.toHaveBeenCalled();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// SCENARIO 1: NO TRANSLATION CONFIGURATION
|
|
66
|
+
test("should skip translation when no translation languages configured", async () => {
|
|
67
|
+
const testCases = [
|
|
68
|
+
{ translateLanguages: null, description: "null" },
|
|
69
|
+
{ translateLanguages: undefined, description: "undefined" },
|
|
70
|
+
{ translateLanguages: [], description: "empty array" },
|
|
71
|
+
{ translateLanguages: ["en"], description: "only current locale" },
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
for (const testCase of testCases) {
|
|
75
|
+
const input = {
|
|
76
|
+
selectedDocs: [
|
|
77
|
+
{
|
|
78
|
+
path: "/docs/test.md",
|
|
79
|
+
content: "# Test Document",
|
|
80
|
+
translates: {},
|
|
81
|
+
labels: {},
|
|
82
|
+
feedback: "Good content",
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
docsDir: "./docs",
|
|
86
|
+
translateLanguages: testCase.translateLanguages,
|
|
87
|
+
locale: "en",
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const result = await saveAndTranslateDocument(input, mockOptions);
|
|
91
|
+
|
|
92
|
+
expect(result).toEqual({});
|
|
93
|
+
expect(mockOptions.prompts.select).not.toHaveBeenCalled();
|
|
94
|
+
expect(mockOptions.context.invoke).toHaveBeenCalledTimes(1);
|
|
95
|
+
expect(recordUpdateSpy).toHaveBeenCalledWith({
|
|
96
|
+
operation: "document_update",
|
|
97
|
+
feedback: "Good content",
|
|
98
|
+
documentPath: "/docs/test.md",
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Reset mocks for next iteration
|
|
102
|
+
mockOptions.prompts.select.mockClear();
|
|
103
|
+
mockOptions.context.invoke.mockClear();
|
|
104
|
+
recordUpdateSpy.mockClear();
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// SCENARIO 2: USER CHOOSES NOT TO TRANSLATE
|
|
109
|
+
test("should save documents and skip translation when user chooses no", async () => {
|
|
110
|
+
const input = {
|
|
111
|
+
selectedDocs: [
|
|
112
|
+
{
|
|
113
|
+
path: "/docs/test1.md",
|
|
114
|
+
content: "# Test Document 1",
|
|
115
|
+
translates: {},
|
|
116
|
+
labels: {},
|
|
117
|
+
feedback: "Update needed",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
path: "/docs/test2.md",
|
|
121
|
+
content: "# Test Document 2",
|
|
122
|
+
translates: {},
|
|
123
|
+
labels: {},
|
|
124
|
+
feedback: "Second feedback",
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
path: "/docs/test3.md",
|
|
128
|
+
content: "# Test Document 3",
|
|
129
|
+
translates: {},
|
|
130
|
+
labels: {},
|
|
131
|
+
feedback: " ", // Whitespace only
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
docsDir: "./docs",
|
|
135
|
+
translateLanguages: ["en", "zh", "ja"],
|
|
136
|
+
locale: "en",
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
mockOptions.prompts.select.mockResolvedValue("no");
|
|
140
|
+
|
|
141
|
+
const result = await saveAndTranslateDocument(input, mockOptions);
|
|
142
|
+
|
|
143
|
+
expect(result).toEqual({});
|
|
144
|
+
expect(mockOptions.prompts.select).toHaveBeenCalledWith({
|
|
145
|
+
message: "Document update completed. Would you like to translate these documents now?",
|
|
146
|
+
choices: [
|
|
147
|
+
{
|
|
148
|
+
name: "Review documents first, translate later",
|
|
149
|
+
value: "no",
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: "Translate now",
|
|
153
|
+
value: "yes",
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
});
|
|
157
|
+
expect(mockOptions.context.invoke).toHaveBeenCalledTimes(3); // Only saveDocument calls
|
|
158
|
+
expect(recordUpdateSpy).toHaveBeenCalledTimes(2); // Only documents with non-empty feedback
|
|
159
|
+
expect(recordUpdateSpy).toHaveBeenCalledWith({
|
|
160
|
+
operation: "document_update",
|
|
161
|
+
feedback: "Update needed",
|
|
162
|
+
documentPath: "/docs/test1.md",
|
|
163
|
+
});
|
|
164
|
+
expect(recordUpdateSpy).toHaveBeenCalledWith({
|
|
165
|
+
operation: "document_update",
|
|
166
|
+
feedback: "Second feedback",
|
|
167
|
+
documentPath: "/docs/test2.md",
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// SCENARIO 3: USER CHOOSES TO TRANSLATE
|
|
172
|
+
test("should save and translate documents when user chooses yes", async () => {
|
|
173
|
+
const input = {
|
|
174
|
+
selectedDocs: [
|
|
175
|
+
{
|
|
176
|
+
path: "/docs/test1.md",
|
|
177
|
+
content: "# Test Document 1",
|
|
178
|
+
translates: {},
|
|
179
|
+
labels: {},
|
|
180
|
+
feedback: "Translation needed",
|
|
181
|
+
title: "Test Document 1",
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
path: "/docs/test2.md",
|
|
185
|
+
content: "# Test Document 2",
|
|
186
|
+
translates: {},
|
|
187
|
+
labels: {},
|
|
188
|
+
title: "Test Document 2",
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
docsDir: "./docs",
|
|
192
|
+
translateLanguages: ["en", "zh"],
|
|
193
|
+
locale: "en",
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
mockOptions.prompts.select.mockResolvedValue("yes");
|
|
197
|
+
mockOptions.context.invoke
|
|
198
|
+
.mockResolvedValueOnce({ mockSaveResult: true }) // saveDocument 1
|
|
199
|
+
.mockResolvedValueOnce({ mockSaveResult: true }) // saveDocument 2
|
|
200
|
+
.mockResolvedValueOnce({ translates: { zh: "# 测试文档 1" } }) // translateMultilingual 1
|
|
201
|
+
.mockResolvedValueOnce({ translates: { zh: "# 测试文档 2" } }) // translateMultilingual 2
|
|
202
|
+
.mockResolvedValueOnce({ mockSaveResult: true }) // saveDocument with translation 1
|
|
203
|
+
.mockResolvedValueOnce({ mockSaveResult: true }); // saveDocument with translation 2
|
|
204
|
+
|
|
205
|
+
const result = await saveAndTranslateDocument(input, mockOptions);
|
|
206
|
+
|
|
207
|
+
expect(result).toEqual({});
|
|
208
|
+
expect(mockOptions.prompts.select).toHaveBeenCalledWith({
|
|
209
|
+
message: "Document update completed. Would you like to translate these documents now?",
|
|
210
|
+
choices: [
|
|
211
|
+
{
|
|
212
|
+
name: "Review documents first, translate later",
|
|
213
|
+
value: "no",
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: "Translate now",
|
|
217
|
+
value: "yes",
|
|
218
|
+
},
|
|
219
|
+
],
|
|
220
|
+
});
|
|
221
|
+
expect(mockOptions.context.invoke).toHaveBeenCalledTimes(6);
|
|
222
|
+
|
|
223
|
+
// Verify feedback is cleared before translation
|
|
224
|
+
expect(input.selectedDocs[0].feedback).toBe("");
|
|
225
|
+
expect(input.selectedDocs[1].feedback).toBe("");
|
|
226
|
+
|
|
227
|
+
expect(recordUpdateSpy).toHaveBeenCalledWith({
|
|
228
|
+
operation: "document_update",
|
|
229
|
+
feedback: "Translation needed",
|
|
230
|
+
documentPath: "/docs/test1.md",
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// ERROR HANDLING TESTS
|
|
235
|
+
test("should handle errors gracefully", async () => {
|
|
236
|
+
// Test saveDocument error
|
|
237
|
+
const saveErrorInput = {
|
|
238
|
+
selectedDocs: [
|
|
239
|
+
{
|
|
240
|
+
path: "/docs/test1.md",
|
|
241
|
+
content: "# Test Document 1",
|
|
242
|
+
translates: {},
|
|
243
|
+
labels: {},
|
|
244
|
+
feedback: "Error test",
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
docsDir: "./docs",
|
|
248
|
+
translateLanguages: ["en", "zh"],
|
|
249
|
+
locale: "en",
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
mockOptions.prompts.select.mockResolvedValue("no");
|
|
253
|
+
mockOptions.context.invoke.mockRejectedValue(new Error("Save failed"));
|
|
254
|
+
|
|
255
|
+
const saveErrorResult = await saveAndTranslateDocument(saveErrorInput, mockOptions);
|
|
256
|
+
|
|
257
|
+
expect(saveErrorResult).toEqual({});
|
|
258
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
259
|
+
"❌ Failed to save document /docs/test1.md:",
|
|
260
|
+
"Save failed",
|
|
261
|
+
);
|
|
262
|
+
expect(recordUpdateSpy).not.toHaveBeenCalled(); // Should not record if save failed
|
|
263
|
+
|
|
264
|
+
// Reset mocks
|
|
265
|
+
mockOptions.prompts.select.mockClear();
|
|
266
|
+
mockOptions.context.invoke.mockClear();
|
|
267
|
+
consoleErrorSpy.mockClear();
|
|
268
|
+
recordUpdateSpy.mockClear();
|
|
269
|
+
|
|
270
|
+
// Test translateMultilingual error
|
|
271
|
+
const translateErrorInput = {
|
|
272
|
+
selectedDocs: [
|
|
273
|
+
{
|
|
274
|
+
path: "/docs/test2.md",
|
|
275
|
+
content: "# Test Document 2",
|
|
276
|
+
translates: {},
|
|
277
|
+
labels: {},
|
|
278
|
+
title: "Test Document 2",
|
|
279
|
+
},
|
|
280
|
+
],
|
|
281
|
+
docsDir: "./docs",
|
|
282
|
+
translateLanguages: ["en", "zh"],
|
|
283
|
+
locale: "en",
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
mockOptions.prompts.select.mockResolvedValue("yes");
|
|
287
|
+
mockOptions.context.invoke
|
|
288
|
+
.mockResolvedValueOnce({ mockSaveResult: true }) // saveDocument succeeds
|
|
289
|
+
.mockRejectedValueOnce(new Error("Translation failed")); // translateMultilingual fails
|
|
290
|
+
|
|
291
|
+
const translateErrorResult = await saveAndTranslateDocument(translateErrorInput, mockOptions);
|
|
292
|
+
|
|
293
|
+
expect(translateErrorResult).toEqual({});
|
|
294
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
295
|
+
"❌ Failed to translate document /docs/test2.md:",
|
|
296
|
+
"Translation failed",
|
|
297
|
+
);
|
|
298
|
+
expect(mockOptions.context.invoke).toHaveBeenCalledTimes(2);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// EDGE CASES AND INTEGRATION
|
|
302
|
+
test("should handle edge cases and complex scenarios", async () => {
|
|
303
|
+
// Test edge cases with different document properties
|
|
304
|
+
const edgeCaseInput = {
|
|
305
|
+
selectedDocs: [
|
|
306
|
+
{
|
|
307
|
+
path: "/docs/test1.md",
|
|
308
|
+
content: "# Test Document 1",
|
|
309
|
+
translates: null, // null translates
|
|
310
|
+
labels: {},
|
|
311
|
+
feedback: "", // empty feedback
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
path: "/docs/test2.md",
|
|
315
|
+
content: "# Test Document 2",
|
|
316
|
+
translates: undefined, // undefined translates
|
|
317
|
+
labels: {},
|
|
318
|
+
feedback: " ", // whitespace feedback
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
path: "/docs/test3.md",
|
|
322
|
+
content: "# Test Document 3",
|
|
323
|
+
translates: {},
|
|
324
|
+
labels: {},
|
|
325
|
+
// no title
|
|
326
|
+
},
|
|
327
|
+
],
|
|
328
|
+
docsDir: "./docs",
|
|
329
|
+
translateLanguages: ["en", "zh"],
|
|
330
|
+
locale: "en",
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
mockOptions.prompts.select.mockResolvedValue("no");
|
|
334
|
+
|
|
335
|
+
const edgeCaseResult = await saveAndTranslateDocument(edgeCaseInput, mockOptions);
|
|
336
|
+
|
|
337
|
+
expect(edgeCaseResult).toEqual({});
|
|
338
|
+
expect(mockOptions.context.invoke).toHaveBeenCalledTimes(3);
|
|
339
|
+
expect(recordUpdateSpy).not.toHaveBeenCalled(); // No valid feedback
|
|
340
|
+
|
|
341
|
+
// Reset mocks
|
|
342
|
+
mockOptions.prompts.select.mockClear();
|
|
343
|
+
mockOptions.context.invoke.mockClear();
|
|
344
|
+
recordUpdateSpy.mockClear();
|
|
345
|
+
|
|
346
|
+
// Test batch processing with multiple documents
|
|
347
|
+
const batchInput = {
|
|
348
|
+
selectedDocs: Array.from({ length: 5 }, (_, i) => ({
|
|
349
|
+
path: `/docs/batch${i + 1}.md`,
|
|
350
|
+
content: `# Batch Document ${i + 1}`,
|
|
351
|
+
translates: {},
|
|
352
|
+
labels: {},
|
|
353
|
+
title: `Batch Document ${i + 1}`,
|
|
354
|
+
})),
|
|
355
|
+
docsDir: "./docs",
|
|
356
|
+
translateLanguages: ["en", "zh"],
|
|
357
|
+
locale: "en",
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
mockOptions.prompts.select.mockResolvedValue("yes");
|
|
361
|
+
mockOptions.context.invoke.mockResolvedValue({ mockResult: true });
|
|
362
|
+
|
|
363
|
+
const batchResult = await saveAndTranslateDocument(batchInput, mockOptions);
|
|
364
|
+
|
|
365
|
+
expect(batchResult).toEqual({});
|
|
366
|
+
// 5 documents * 3 calls each (save, translate, save) = 15 calls
|
|
367
|
+
expect(mockOptions.context.invoke).toHaveBeenCalledTimes(15);
|
|
368
|
+
});
|
|
369
|
+
});
|
package/utils/auth-utils.mjs
CHANGED
|
@@ -15,7 +15,8 @@ import {
|
|
|
15
15
|
} from "./blocklet.mjs";
|
|
16
16
|
import {
|
|
17
17
|
BLOCKLET_ADD_COMPONENT_DOCS,
|
|
18
|
-
|
|
18
|
+
CLOUD_SERVICE_URL_PROD,
|
|
19
|
+
CLOUD_SERVICE_URL_STAGING,
|
|
19
20
|
DISCUSS_KIT_DID,
|
|
20
21
|
DISCUSS_KIT_STORE_URL,
|
|
21
22
|
DOC_OFFICIAL_ACCESS_TOKEN,
|
|
@@ -98,7 +99,7 @@ export async function getAccessToken(appUrl, ltToken = "") {
|
|
|
98
99
|
appLogo: "https://docsmith.aigne.io/image-bin/uploads/9645caf64b4232699982c4d940b03b90.svg",
|
|
99
100
|
openPage: (pageUrl) => {
|
|
100
101
|
const url = new URL(pageUrl);
|
|
101
|
-
if (url.
|
|
102
|
+
if ([CLOUD_SERVICE_URL_PROD, CLOUD_SERVICE_URL_STAGING].includes(url.origin) === false) {
|
|
102
103
|
url.searchParams.set("required_roles", "owner,admin");
|
|
103
104
|
}
|
|
104
105
|
if (ltToken) {
|
|
@@ -335,7 +335,8 @@ export const PAYMENT_KIT_DID = "z2qaCNvKMv5GjouKdcDWexv6WqtHbpNPQDnAk";
|
|
|
335
335
|
export const DOC_OFFICIAL_ACCESS_TOKEN = "DOC_OFFICIAL_ACCESS_TOKEN";
|
|
336
336
|
|
|
337
337
|
// Default application URL for the document deployment website.
|
|
338
|
-
export const
|
|
338
|
+
export const CLOUD_SERVICE_URL_PROD = "https://docsmith.aigne.io";
|
|
339
|
+
export const CLOUD_SERVICE_URL_STAGING = "https://staging.docsmith.aigne.io";
|
|
339
340
|
|
|
340
341
|
// Discuss Kit related URLs
|
|
341
342
|
export const DISCUSS_KIT_STORE_URL =
|
package/utils/deploy.mjs
CHANGED
|
@@ -2,11 +2,11 @@ import { BrokerClient, STEPS } from "@blocklet/payment-broker-client/node";
|
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import open from "open";
|
|
4
4
|
import { getOfficialAccessToken } from "./auth-utils.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { CLOUD_SERVICE_URL_PROD } from "./constants/index.mjs";
|
|
6
6
|
import { saveValueToConfig } from "./utils.mjs";
|
|
7
7
|
|
|
8
8
|
// ==================== Configuration ====================
|
|
9
|
-
const BASE_URL = process.env.DOC_SMITH_BASE_URL ||
|
|
9
|
+
const BASE_URL = process.env.DOC_SMITH_BASE_URL || CLOUD_SERVICE_URL_PROD;
|
|
10
10
|
const SUCCESS_MESSAGE = {
|
|
11
11
|
en: "Congratulations! Your website has been successfully installed. You can return to the command-line tool to continue the next steps.",
|
|
12
12
|
zh: "恭喜您,你的网站已安装成功!可以返回命令行工具继续后续操作!",
|