@aigne/doc-smith 0.8.5 → 0.8.6
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/CHANGELOG.md +18 -0
- package/agents/action-success.mjs +1 -1
- package/agents/check-structure-plan.mjs +1 -1
- package/agents/detail-regenerator.yaml +6 -6
- package/agents/docs-generator.yaml +4 -4
- package/agents/feedback-refiner.yaml +10 -10
- package/agents/find-item-by-path.mjs +1 -1
- package/agents/find-items-by-paths.mjs +1 -1
- package/agents/input-generator.mjs +22 -22
- package/agents/manage-prefs.mjs +15 -15
- package/agents/publish-docs.mjs +9 -11
- package/agents/retranslate.yaml +6 -6
- package/agents/structure-planning.yaml +17 -17
- package/agents/team-publish-docs.yaml +2 -2
- package/agents/translate.yaml +7 -7
- package/docs/cli-reference.md +1 -1
- package/docs/features-generate-documentation.md +1 -1
- package/docs/features-update-and-refine.md +2 -2
- package/package.json +1 -1
- package/tests/deploy.test.mjs +376 -0
- package/tests/input-generator.test.mjs +6 -4
- package/utils/auth-utils.mjs +1 -1
- package/utils/deploy.mjs +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.8.6](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.8.5...v0.8.6) (2025-09-11)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add deploy unit tests and improve publish workflow with better logging ([e33a94b](https://github.com/AIGNE-io/aigne-doc-smith/commit/e33a94bef5eda09398901fa1f953e662ae5fbd16))
|
|
9
|
+
* **publish:** display publish url for the default publish processing ([9d1d018](https://github.com/AIGNE-io/aigne-doc-smith/commit/9d1d0180dc9c8bb0a4393a893eed2395eec300ab))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* **deploy:** ensure log is saved after await to prevent save failure ([793343f](https://github.com/AIGNE-io/aigne-doc-smith/commit/793343fc7f96ab962e70eb310cb07f4e7eaec9e0))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Miscellaneous Chores
|
|
18
|
+
|
|
19
|
+
* release 0.8.6 ([1e25cb4](https://github.com/AIGNE-io/aigne-doc-smith/commit/1e25cb49a26d8bcc3c83ec36120b6bad4042cadf))
|
|
20
|
+
|
|
3
21
|
## [0.8.5](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.8.4...v0.8.5) (2025-09-10)
|
|
4
22
|
|
|
5
23
|
|
|
@@ -21,7 +21,7 @@ export default async function checkStructurePlan(
|
|
|
21
21
|
// Prompt for feedback if originalStructurePlan exists and no feedback provided
|
|
22
22
|
if (originalStructurePlan && !feedback) {
|
|
23
23
|
const userFeedback = await options.prompts.input({
|
|
24
|
-
message: "
|
|
24
|
+
message: "How can we improve the documentation structure? (press Enter to skip):",
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
if (userFeedback?.trim()) {
|
|
@@ -2,7 +2,7 @@ type: team
|
|
|
2
2
|
name: update
|
|
3
3
|
alias:
|
|
4
4
|
- up
|
|
5
|
-
description:
|
|
5
|
+
description: Update specific documents and their translations
|
|
6
6
|
skills:
|
|
7
7
|
- url: ./input-generator.mjs
|
|
8
8
|
default_input:
|
|
@@ -39,24 +39,24 @@ skills:
|
|
|
39
39
|
stage: document_refine
|
|
40
40
|
- url: ./action-success.mjs
|
|
41
41
|
default_input:
|
|
42
|
-
action: "
|
|
42
|
+
action: "✅ Documents updated successfully"
|
|
43
43
|
input_schema:
|
|
44
44
|
type: object
|
|
45
45
|
properties:
|
|
46
46
|
glossary:
|
|
47
47
|
type: string
|
|
48
|
-
description: Glossary
|
|
48
|
+
description: Glossary file for consistent terminology (use @filename.md)
|
|
49
49
|
docs:
|
|
50
50
|
type: array
|
|
51
51
|
items:
|
|
52
52
|
type: string
|
|
53
|
-
description:
|
|
53
|
+
description: Documents to update
|
|
54
54
|
feedback:
|
|
55
55
|
type: string
|
|
56
|
-
description:
|
|
56
|
+
description: Tell us what to change in this content
|
|
57
57
|
reset:
|
|
58
58
|
type: boolean
|
|
59
|
-
description:
|
|
59
|
+
description: Start fresh - ignore previous versions
|
|
60
60
|
output_schema:
|
|
61
61
|
type: object
|
|
62
62
|
properties:
|
|
@@ -3,7 +3,7 @@ name: generate
|
|
|
3
3
|
alias:
|
|
4
4
|
- gen
|
|
5
5
|
- g
|
|
6
|
-
description:
|
|
6
|
+
description: Generate complete documentation for your project
|
|
7
7
|
skills:
|
|
8
8
|
- url: ./input-generator.mjs
|
|
9
9
|
default_input:
|
|
@@ -47,13 +47,13 @@ input_schema:
|
|
|
47
47
|
properties:
|
|
48
48
|
glossary:
|
|
49
49
|
type: string
|
|
50
|
-
description: Glossary
|
|
50
|
+
description: Glossary file for consistent terminology (use @filename.md)
|
|
51
51
|
feedback:
|
|
52
52
|
type: string
|
|
53
|
-
description:
|
|
53
|
+
description: Tell us how to improve the documentation structure
|
|
54
54
|
forceRegenerate:
|
|
55
55
|
type: boolean
|
|
56
|
-
description:
|
|
56
|
+
description: Rebuild all documentation from scratch
|
|
57
57
|
required:
|
|
58
58
|
- config
|
|
59
59
|
mode: sequential
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name: feedbackRefiner
|
|
2
|
-
description:
|
|
2
|
+
description: Learn from your feedback to improve future documentation
|
|
3
3
|
|
|
4
4
|
# The instructions file contains the core logic (the prompt) for this Agent.
|
|
5
5
|
instructions:
|
|
@@ -10,18 +10,18 @@ input_schema:
|
|
|
10
10
|
properties:
|
|
11
11
|
feedback:
|
|
12
12
|
type: string
|
|
13
|
-
description:
|
|
13
|
+
description: Tell us what you think about the documentation
|
|
14
14
|
stage:
|
|
15
15
|
type: string
|
|
16
|
-
description:
|
|
16
|
+
description: Which part of the process this feedback is about (structure_planning, document_refine, translation_refine)
|
|
17
17
|
paths:
|
|
18
18
|
type: array
|
|
19
19
|
items:
|
|
20
20
|
type: string
|
|
21
|
-
description:
|
|
21
|
+
description: Specific documents this feedback applies to (optional)
|
|
22
22
|
existingPreferences:
|
|
23
23
|
type: string
|
|
24
|
-
description:
|
|
24
|
+
description: Your existing preferences to avoid duplicates (optional)
|
|
25
25
|
required:
|
|
26
26
|
- feedback
|
|
27
27
|
- stage
|
|
@@ -31,19 +31,19 @@ output_schema:
|
|
|
31
31
|
properties:
|
|
32
32
|
rule:
|
|
33
33
|
type: string
|
|
34
|
-
description:
|
|
34
|
+
description: General rule created from your feedback
|
|
35
35
|
scope:
|
|
36
36
|
type: string
|
|
37
|
-
description:
|
|
37
|
+
description: Where this rule applies (global, structure, document, translation)
|
|
38
38
|
save:
|
|
39
39
|
type: boolean
|
|
40
|
-
description:
|
|
40
|
+
description: Should we remember this preference for next time?
|
|
41
41
|
limitToInputPaths:
|
|
42
42
|
type: boolean
|
|
43
|
-
description:
|
|
43
|
+
description: Apply only to the specific documents mentioned?
|
|
44
44
|
reason:
|
|
45
45
|
type: string
|
|
46
|
-
description:
|
|
46
|
+
description: Why we made this decision and how the rule was created
|
|
47
47
|
required:
|
|
48
48
|
- rule
|
|
49
49
|
- scope
|
|
@@ -89,7 +89,7 @@ export default async function findItemByPath(
|
|
|
89
89
|
if (!userFeedback) {
|
|
90
90
|
const feedbackMessage = getActionText(
|
|
91
91
|
isTranslate,
|
|
92
|
-
"
|
|
92
|
+
"How should we improve this {action}? (press Enter to skip):",
|
|
93
93
|
);
|
|
94
94
|
|
|
95
95
|
userFeedback = await options.prompts.input({
|
|
@@ -90,7 +90,7 @@ export default async function selectedDocs(
|
|
|
90
90
|
if (!userFeedback) {
|
|
91
91
|
const feedbackMessage = getActionText(
|
|
92
92
|
isTranslate,
|
|
93
|
-
"
|
|
93
|
+
"How should we improve this {action}? (press Enter to skip):",
|
|
94
94
|
);
|
|
95
95
|
|
|
96
96
|
userFeedback = await options.prompts.input({
|
|
@@ -44,14 +44,14 @@ export default async function init(
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
console.log("🚀 Welcome to AIGNE DocSmith!");
|
|
47
|
-
console.log("Let's
|
|
47
|
+
console.log("Let's set up your documentation preferences.\n");
|
|
48
48
|
|
|
49
49
|
// Collect user information
|
|
50
50
|
const input = {};
|
|
51
51
|
|
|
52
52
|
// 1. Primary purpose - what's the main outcome you want readers to achieve?
|
|
53
53
|
const purposeChoices = await options.prompts.checkbox({
|
|
54
|
-
message: "📝 [1/8]: What
|
|
54
|
+
message: "📝 [1/8]: What should your documentation help readers achieve?",
|
|
55
55
|
choices: Object.entries(DOCUMENT_STYLES)
|
|
56
56
|
.filter(([key]) => key !== "custom") // Remove custom option for multiselect
|
|
57
57
|
.map(([key, style]) => ({
|
|
@@ -61,7 +61,7 @@ export default async function init(
|
|
|
61
61
|
})),
|
|
62
62
|
validate: (input) => {
|
|
63
63
|
if (input.length === 0) {
|
|
64
|
-
return "Please
|
|
64
|
+
return "Please choose at least one goal for your documentation.";
|
|
65
65
|
}
|
|
66
66
|
return true;
|
|
67
67
|
},
|
|
@@ -81,10 +81,10 @@ export default async function init(
|
|
|
81
81
|
})),
|
|
82
82
|
validate: (input) => {
|
|
83
83
|
if (input.length === 0) {
|
|
84
|
-
return "Please
|
|
84
|
+
return "Please choose at least one priority.";
|
|
85
85
|
}
|
|
86
86
|
if (input.length > 2) {
|
|
87
|
-
return "Please
|
|
87
|
+
return "Please choose maximum 2 priorities.";
|
|
88
88
|
}
|
|
89
89
|
return true;
|
|
90
90
|
},
|
|
@@ -99,7 +99,7 @@ export default async function init(
|
|
|
99
99
|
|
|
100
100
|
// 2. Target audience - who will be reading this most often?
|
|
101
101
|
const audienceChoices = await options.prompts.checkbox({
|
|
102
|
-
message: "👥 [2/8]: Who
|
|
102
|
+
message: "👥 [2/8]: Who will be reading your documentation?",
|
|
103
103
|
choices: Object.entries(TARGET_AUDIENCES)
|
|
104
104
|
.filter(([key]) => key !== "custom") // Remove custom option for multiselect
|
|
105
105
|
.map(([key, audience]) => ({
|
|
@@ -109,7 +109,7 @@ export default async function init(
|
|
|
109
109
|
})),
|
|
110
110
|
validate: (input) => {
|
|
111
111
|
if (input.length === 0) {
|
|
112
|
-
return "Please
|
|
112
|
+
return "Please choose at least one audience.";
|
|
113
113
|
}
|
|
114
114
|
return true;
|
|
115
115
|
},
|
|
@@ -133,7 +133,7 @@ export default async function init(
|
|
|
133
133
|
);
|
|
134
134
|
|
|
135
135
|
const knowledgeChoice = await options.prompts.select({
|
|
136
|
-
message: "🧠 [3/8]:
|
|
136
|
+
message: "🧠 [3/8]: How much do readers already know about your project?",
|
|
137
137
|
choices: Object.entries(filteredKnowledgeOptions).map(([key, level]) => ({
|
|
138
138
|
name: `${level.name}`,
|
|
139
139
|
description: level.description,
|
|
@@ -178,7 +178,7 @@ export default async function init(
|
|
|
178
178
|
);
|
|
179
179
|
|
|
180
180
|
const depthChoice = await options.prompts.select({
|
|
181
|
-
message: "📊 [4/8]: How
|
|
181
|
+
message: "📊 [4/8]: How detailed should your documentation be?",
|
|
182
182
|
choices: Object.entries(filteredDepthOptions).map(([key, depth]) => ({
|
|
183
183
|
name: `${depth.name}`,
|
|
184
184
|
description: depth.description,
|
|
@@ -196,7 +196,7 @@ export default async function init(
|
|
|
196
196
|
|
|
197
197
|
// Let user select primary language from supported list
|
|
198
198
|
const primaryLanguageChoice = await options.prompts.select({
|
|
199
|
-
message: "🌐 [5/8]:
|
|
199
|
+
message: "🌐 [5/8]: What's your main documentation language?",
|
|
200
200
|
choices: SUPPORTED_LANGUAGES.map((lang) => ({
|
|
201
201
|
name: `${lang.label} - ${lang.sample}`,
|
|
202
202
|
value: lang.code,
|
|
@@ -213,7 +213,7 @@ export default async function init(
|
|
|
213
213
|
);
|
|
214
214
|
|
|
215
215
|
const translateLanguageChoices = await options.prompts.checkbox({
|
|
216
|
-
message: "🔄 [6/8]:
|
|
216
|
+
message: "🔄 [6/8]: Which languages should we translate to?",
|
|
217
217
|
choices: availableTranslationLanguages.map((lang) => ({
|
|
218
218
|
name: `${lang.label} - ${lang.sample}`,
|
|
219
219
|
value: lang.code,
|
|
@@ -224,21 +224,23 @@ export default async function init(
|
|
|
224
224
|
|
|
225
225
|
// 7. Documentation directory
|
|
226
226
|
const docsDirInput = await options.prompts.input({
|
|
227
|
-
message: `📁 [7/8]: Where
|
|
227
|
+
message: `📁 [7/8]: Where should we save your documentation?`,
|
|
228
228
|
default: `${outputPath}/docs`,
|
|
229
229
|
});
|
|
230
230
|
input.docsDir = docsDirInput.trim() || `${outputPath}/docs`;
|
|
231
231
|
|
|
232
|
-
// 8.
|
|
233
|
-
console.log("\n🔍 [8/8]:
|
|
234
|
-
console.log(
|
|
235
|
-
|
|
236
|
-
|
|
232
|
+
// 8. Content sources
|
|
233
|
+
console.log("\n🔍 [8/8]: Content Sources");
|
|
234
|
+
console.log(
|
|
235
|
+
"What folders/files should we analyze for documentation? (e.g., ./src, ./docs, ./README.md)",
|
|
236
|
+
);
|
|
237
|
+
console.log("💡 Advanced: Use patterns like src/**/*.js or docs/**/*.md for specific files");
|
|
238
|
+
console.log("💡 Leave empty to scan everything");
|
|
237
239
|
|
|
238
240
|
const sourcePaths = [];
|
|
239
241
|
while (true) {
|
|
240
242
|
const selectedPath = await options.prompts.search({
|
|
241
|
-
message: "
|
|
243
|
+
message: "File/folder path or pattern:",
|
|
242
244
|
source: async (input) => {
|
|
243
245
|
if (!input || input.trim() === "") {
|
|
244
246
|
return [
|
|
@@ -330,15 +332,13 @@ export default async function init(
|
|
|
330
332
|
await mkdir(dirPath, { recursive: true });
|
|
331
333
|
|
|
332
334
|
await writeFile(filePath, yamlContent, "utf8");
|
|
333
|
-
console.log(`\n
|
|
335
|
+
console.log(`\n✅ Setup complete! Configuration saved to: ${chalk.cyan(filePath)}`);
|
|
334
336
|
// Print YAML content for user review
|
|
335
337
|
console.log(chalk.cyan("---"));
|
|
336
338
|
console.log(chalk.cyan(yamlContent));
|
|
337
339
|
console.log(chalk.cyan("---"));
|
|
338
340
|
console.log("💡 You can edit the configuration file anytime to modify settings.\n");
|
|
339
|
-
console.log(
|
|
340
|
-
`🚀 Run ${chalk.cyan("'aigne doc generate'")} to start documentation generation!\n`,
|
|
341
|
-
);
|
|
341
|
+
console.log(`🚀 Ready to generate docs? Run: ${chalk.cyan("aigne doc generate")}\n`);
|
|
342
342
|
|
|
343
343
|
return {};
|
|
344
344
|
} catch (error) {
|
package/agents/manage-prefs.mjs
CHANGED
|
@@ -8,7 +8,7 @@ function listPreferences() {
|
|
|
8
8
|
const preferences = readPreferences();
|
|
9
9
|
|
|
10
10
|
if (preferences.rules.length === 0) {
|
|
11
|
-
return { message: "No preferences found." };
|
|
11
|
+
return { message: "No saved preferences found." };
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
let message = "# User Preferences\n\n";
|
|
@@ -53,7 +53,7 @@ async function removePreferences(id, options) {
|
|
|
53
53
|
if (!targetIds || targetIds.length === 0) {
|
|
54
54
|
// Interactive selection
|
|
55
55
|
if (preferences.rules.length === 0) {
|
|
56
|
-
return { message: "No preferences
|
|
56
|
+
return { message: "No preferences available to remove." };
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
const choices = preferences.rules.map((rule) => ({
|
|
@@ -63,18 +63,18 @@ async function removePreferences(id, options) {
|
|
|
63
63
|
}));
|
|
64
64
|
|
|
65
65
|
targetIds = await options.prompts.checkbox({
|
|
66
|
-
message: "
|
|
66
|
+
message: "Choose preferences to delete:",
|
|
67
67
|
choices,
|
|
68
68
|
validate: (answer) => {
|
|
69
69
|
if (answer.length === 0) {
|
|
70
|
-
return "Please
|
|
70
|
+
return "Please choose at least one preference to delete";
|
|
71
71
|
}
|
|
72
72
|
return true;
|
|
73
73
|
},
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
if (!targetIds || targetIds.length === 0) {
|
|
77
|
-
return { message: "No preferences selected for
|
|
77
|
+
return { message: "No preferences selected for deletion." };
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -108,7 +108,7 @@ async function togglePreferences(id, options) {
|
|
|
108
108
|
if (!targetIds || targetIds.length === 0) {
|
|
109
109
|
// Interactive selection
|
|
110
110
|
if (preferences.rules.length === 0) {
|
|
111
|
-
return { message: "No preferences
|
|
111
|
+
return { message: "No preferences available to toggle." };
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
const choices = preferences.rules.map((rule) => ({
|
|
@@ -118,18 +118,18 @@ async function togglePreferences(id, options) {
|
|
|
118
118
|
}));
|
|
119
119
|
|
|
120
120
|
targetIds = await options.prompts.checkbox({
|
|
121
|
-
message: "
|
|
121
|
+
message: "Choose preferences to enable/disable:",
|
|
122
122
|
choices,
|
|
123
123
|
validate: (answer) => {
|
|
124
124
|
if (answer.length === 0) {
|
|
125
|
-
return "Please
|
|
125
|
+
return "Please choose at least one preference to toggle";
|
|
126
126
|
}
|
|
127
127
|
return true;
|
|
128
128
|
},
|
|
129
129
|
});
|
|
130
130
|
|
|
131
131
|
if (!targetIds || targetIds.length === 0) {
|
|
132
|
-
return { message: "No preferences selected
|
|
132
|
+
return { message: "No preferences selected to toggle." };
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
|
|
@@ -172,7 +172,7 @@ export default async function prefs({ list, remove, toggle, id }, options) {
|
|
|
172
172
|
return await togglePreferences(id, options);
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
return { message: "Please
|
|
175
|
+
return { message: "Please choose an action: --list, --remove, or --toggle." };
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
prefs.input_schema = {
|
|
@@ -180,24 +180,24 @@ prefs.input_schema = {
|
|
|
180
180
|
properties: {
|
|
181
181
|
list: {
|
|
182
182
|
type: "boolean",
|
|
183
|
-
description: "
|
|
183
|
+
description: "Show all saved preferences",
|
|
184
184
|
},
|
|
185
185
|
remove: {
|
|
186
186
|
type: "boolean",
|
|
187
|
-
description: "
|
|
187
|
+
description: "Delete saved preferences",
|
|
188
188
|
},
|
|
189
189
|
toggle: {
|
|
190
190
|
type: "boolean",
|
|
191
|
-
description: "
|
|
191
|
+
description: "Enable/disable preferences",
|
|
192
192
|
},
|
|
193
193
|
id: {
|
|
194
194
|
type: "array",
|
|
195
195
|
items: {
|
|
196
196
|
type: "string",
|
|
197
197
|
},
|
|
198
|
-
description: "
|
|
198
|
+
description: "Specific preference IDs to work with",
|
|
199
199
|
},
|
|
200
200
|
},
|
|
201
201
|
};
|
|
202
202
|
|
|
203
|
-
prefs.description = "Manage
|
|
203
|
+
prefs.description = "Manage your saved documentation preferences";
|
package/agents/publish-docs.mjs
CHANGED
|
@@ -53,21 +53,17 @@ export default async function publishDocs(
|
|
|
53
53
|
message: "Select platform to publish your documents:",
|
|
54
54
|
choices: [
|
|
55
55
|
{
|
|
56
|
-
name:
|
|
57
|
-
chalk.blue("Publish to docsmith.aigne.io") +
|
|
58
|
-
" - free, but your documents will be publicly accessible, recommended for open-source projects",
|
|
56
|
+
name: `${chalk.blue("DocSmith Cloud (docsmith.aigne.io)")} – ${chalk.green("Free")} hosting. Your documents will be publicly accessible. Best for open-source projects or community sharing.`,
|
|
59
57
|
value: "default",
|
|
60
58
|
},
|
|
61
59
|
{
|
|
62
|
-
name: `${chalk.blue("
|
|
60
|
+
name: `${chalk.blue("Your existing website")} - Integrate and publish directly on your current site (setup required)`,
|
|
63
61
|
value: "custom",
|
|
64
62
|
},
|
|
65
63
|
...(hasCachedCheckoutId && hasDocSmithBaseUrl
|
|
66
64
|
? [
|
|
67
65
|
{
|
|
68
|
-
name:
|
|
69
|
-
chalk.yellow("Continue your previous website setup") +
|
|
70
|
-
" - resume from where you left off",
|
|
66
|
+
name: `${chalk.yellow("Resume previous website setup")} - ${chalk.green("Already paid.")} Continue where you left off. Your payment is already processed.`,
|
|
71
67
|
value: "new-instance-continue",
|
|
72
68
|
},
|
|
73
69
|
]
|
|
@@ -75,7 +71,7 @@ export default async function publishDocs(
|
|
|
75
71
|
...(hasDocSmithBaseUrl
|
|
76
72
|
? [
|
|
77
73
|
{
|
|
78
|
-
name: `${chalk.blue("
|
|
74
|
+
name: `${chalk.blue("New website")} - ${chalk.yellow("Paid service.")} We'll help you set up a brand-new website with custom domain and hosting. Great if you want a professional presence.`,
|
|
79
75
|
value: "new-instance",
|
|
80
76
|
},
|
|
81
77
|
]
|
|
@@ -113,7 +109,7 @@ export default async function publishDocs(
|
|
|
113
109
|
paymentUrl = config?.paymentUrl;
|
|
114
110
|
console.log(`\nResuming your previous website setup...`);
|
|
115
111
|
} else {
|
|
116
|
-
console.log(`\nCreating
|
|
112
|
+
console.log(`\nCreating new website for your documentation...`);
|
|
117
113
|
}
|
|
118
114
|
const { appUrl: homeUrl, token: ltToken } = (await deploy(id, paymentUrl)) || {};
|
|
119
115
|
|
|
@@ -127,6 +123,8 @@ export default async function publishDocs(
|
|
|
127
123
|
}
|
|
128
124
|
}
|
|
129
125
|
|
|
126
|
+
console.log(`\nPublishing docs to ${chalk.cyan(appUrl)}\n`);
|
|
127
|
+
|
|
130
128
|
const accessToken = await getAccessToken(appUrl, token);
|
|
131
129
|
|
|
132
130
|
process.env.DOC_ROOT_DIR = docsDir;
|
|
@@ -185,7 +183,7 @@ export default async function publishDocs(
|
|
|
185
183
|
} catch (error) {
|
|
186
184
|
message = `❌ Failed to publish docs: ${error.message}`;
|
|
187
185
|
}
|
|
188
|
-
saveValueToConfig("checkoutId", "", "Checkout ID for document deployment service");
|
|
186
|
+
await saveValueToConfig("checkoutId", "", "Checkout ID for document deployment service");
|
|
189
187
|
|
|
190
188
|
// clean up tmp work dir
|
|
191
189
|
await fs.rm(docsDir, { recursive: true, force: true });
|
|
@@ -211,4 +209,4 @@ publishDocs.input_schema = {
|
|
|
211
209
|
},
|
|
212
210
|
};
|
|
213
211
|
|
|
214
|
-
publishDocs.description = "Publish the documentation to
|
|
212
|
+
publishDocs.description = "Publish the documentation to website";
|
package/agents/retranslate.yaml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
type: team
|
|
2
2
|
name: translate
|
|
3
|
-
description: Translate
|
|
3
|
+
description: Translate documents into other languages
|
|
4
4
|
skills:
|
|
5
5
|
- url: ./input-generator.mjs
|
|
6
6
|
default_input:
|
|
@@ -42,24 +42,24 @@ skills:
|
|
|
42
42
|
stage: translation_refine
|
|
43
43
|
- url: ./action-success.mjs
|
|
44
44
|
default_input:
|
|
45
|
-
action: "
|
|
45
|
+
action: "✅ Translation completed"
|
|
46
46
|
input_schema:
|
|
47
47
|
type: object
|
|
48
48
|
properties:
|
|
49
49
|
glossary:
|
|
50
50
|
type: string
|
|
51
|
-
description: Glossary
|
|
51
|
+
description: Glossary file for consistent terminology (use @filename.md)
|
|
52
52
|
docs:
|
|
53
53
|
type: array
|
|
54
54
|
items:
|
|
55
55
|
type: string
|
|
56
|
-
description:
|
|
56
|
+
description: Documents to translate
|
|
57
57
|
langs:
|
|
58
58
|
type: array
|
|
59
59
|
items:
|
|
60
60
|
type: string
|
|
61
|
-
description: "
|
|
61
|
+
description: "Target languages (available: en, zh, zh-TW, ja, fr, de, es, it, ru, ko, pt, ar)"
|
|
62
62
|
feedback:
|
|
63
63
|
type: string
|
|
64
|
-
description:
|
|
64
|
+
description: Tell us how to improve the translation style
|
|
65
65
|
mode: sequential
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name: structurePlanGenerator
|
|
2
|
-
description:
|
|
2
|
+
description: Plan the structure and organization of your documentation
|
|
3
3
|
instructions:
|
|
4
4
|
url: ../prompts/structure-planning.md
|
|
5
5
|
input_schema:
|
|
@@ -7,31 +7,31 @@ input_schema:
|
|
|
7
7
|
properties:
|
|
8
8
|
rules:
|
|
9
9
|
type: string
|
|
10
|
-
description:
|
|
10
|
+
description: Your specific requirements for documentation structure
|
|
11
11
|
locale:
|
|
12
12
|
type: string
|
|
13
|
-
description:
|
|
13
|
+
description: Primary language for documentation (e.g., zh, en, ja)
|
|
14
14
|
datasources:
|
|
15
15
|
type: string
|
|
16
|
-
description:
|
|
16
|
+
description: Project content and context to help plan structure
|
|
17
17
|
targetAudience:
|
|
18
18
|
type: string
|
|
19
|
-
description:
|
|
19
|
+
description: Target audience for the documentation
|
|
20
20
|
nodeName:
|
|
21
21
|
type: string
|
|
22
|
-
description:
|
|
22
|
+
description: Specific section or page name to focus on
|
|
23
23
|
glossary:
|
|
24
24
|
type: string
|
|
25
|
-
description:
|
|
25
|
+
description: Glossary for consistent terminology
|
|
26
26
|
feedback:
|
|
27
27
|
type: string
|
|
28
|
-
description:
|
|
28
|
+
description: Tell us how to improve the documentation structure
|
|
29
29
|
userPreferences:
|
|
30
30
|
type: string
|
|
31
|
-
description:
|
|
31
|
+
description: Your saved preferences for structure and documentation style
|
|
32
32
|
docsType:
|
|
33
33
|
type: string
|
|
34
|
-
description:
|
|
34
|
+
description: "Documentation type (options: general, getting-started, reference, faq)"
|
|
35
35
|
default: general
|
|
36
36
|
required:
|
|
37
37
|
- rules
|
|
@@ -41,18 +41,18 @@ output_schema:
|
|
|
41
41
|
properties:
|
|
42
42
|
projectName:
|
|
43
43
|
type: string
|
|
44
|
-
description:
|
|
44
|
+
description: Project name identified from your content sources
|
|
45
45
|
projectDesc:
|
|
46
46
|
type: string
|
|
47
|
-
description:
|
|
47
|
+
description: Brief project description generated from content analysis (under 50 words)
|
|
48
48
|
structurePlan: ./schema/structure-plan.yaml
|
|
49
49
|
structurePlanTree:
|
|
50
50
|
type: string
|
|
51
51
|
description: |
|
|
52
|
-
|
|
52
|
+
Visual tree structure showing documentation hierarchy with indented levels for easy review:
|
|
53
53
|
```
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
54
|
+
- Home
|
|
55
|
+
- Getting Started
|
|
56
|
+
- Installation
|
|
57
|
+
- Requirements
|
|
58
58
|
```
|
|
@@ -3,7 +3,7 @@ name: publish
|
|
|
3
3
|
alias:
|
|
4
4
|
- pub
|
|
5
5
|
- p
|
|
6
|
-
description: Publish
|
|
6
|
+
description: Publish your documentation online
|
|
7
7
|
skills:
|
|
8
8
|
- url: ./input-generator.mjs
|
|
9
9
|
default_input:
|
|
@@ -15,4 +15,4 @@ input_schema:
|
|
|
15
15
|
properties:
|
|
16
16
|
appUrl:
|
|
17
17
|
type: string
|
|
18
|
-
description:
|
|
18
|
+
description: Website URL where docs will be published (optional - leave empty for interactive setup)
|
package/agents/translate.yaml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name: translateDoc
|
|
2
|
-
description: Translate
|
|
2
|
+
description: Translate content to another language
|
|
3
3
|
instructions:
|
|
4
4
|
url: ../prompts/translator.md
|
|
5
5
|
input_schema:
|
|
@@ -7,16 +7,16 @@ input_schema:
|
|
|
7
7
|
properties:
|
|
8
8
|
language:
|
|
9
9
|
type: string
|
|
10
|
-
description:
|
|
10
|
+
description: Target language (e.g., 'zh' for Chinese, 'ja' for Japanese)
|
|
11
11
|
content:
|
|
12
12
|
type: string
|
|
13
|
-
description:
|
|
13
|
+
description: Text content to translate
|
|
14
14
|
glossary:
|
|
15
15
|
type: string
|
|
16
|
-
description:
|
|
16
|
+
description: Glossary for consistent terminology
|
|
17
17
|
feedback:
|
|
18
18
|
type: string
|
|
19
|
-
description:
|
|
19
|
+
description: Tell us how to improve the translation style
|
|
20
20
|
required:
|
|
21
21
|
- language
|
|
22
22
|
- content
|
|
@@ -25,7 +25,7 @@ output_schema:
|
|
|
25
25
|
properties:
|
|
26
26
|
translation:
|
|
27
27
|
type: string
|
|
28
|
-
description:
|
|
28
|
+
description: Translated text
|
|
29
29
|
language:
|
|
30
30
|
type: string
|
|
31
|
-
description: Language of the translation
|
|
31
|
+
description: Language code of the translation
|
package/docs/cli-reference.md
CHANGED
|
@@ -89,7 +89,7 @@ Analyzes your source code and generates a complete set of documentation based on
|
|
|
89
89
|
|
|
90
90
|
| Option | Type | Description |
|
|
91
91
|
|---|---|---|
|
|
92
|
-
| `--feedback` | string | Provides feedback to adjust and refine the overall
|
|
92
|
+
| `--feedback` | string | Provides feedback to adjust and refine the overall documentation structure. |
|
|
93
93
|
| `--forceRegenerate` | boolean | Discards existing content and regenerates all documentation from scratch. |
|
|
94
94
|
| `--model` | string | Specifies a particular LLM to use for generation (e.g., `openai:gpt-4o`). Overrides the default model. |
|
|
95
95
|
| `--glossary` | string | Path to a glossary file for consistent terminology. Use the format `@path/to/glossary.md`. |
|
|
@@ -90,7 +90,7 @@ While the default `generate` command is sufficient for most use cases, you can u
|
|
|
90
90
|
| Option | Description | Example |
|
|
91
91
|
|---------------------|------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------|
|
|
92
92
|
| `--forceRegenerate` | Deletes all existing documents and regenerates them from scratch. Use this after making significant changes to your source code or configuration. | `aigne doc generate --forceRegenerate` |
|
|
93
|
-
| `--feedback` | Provides high-level feedback to refine the overall
|
|
93
|
+
| `--feedback` | Provides high-level feedback to refine the overall documentation structure, such as adding, removing, or reorganizing sections. | `aigne doc generate --feedback "Add an API Reference section"` |
|
|
94
94
|
| `--model` | Specifies a particular Large Language Model from AIGNE Hub to use for content generation, allowing you to switch between models. | `aigne doc generate --model claude:claude-3-5-sonnet` |
|
|
95
95
|
|
|
96
96
|
## What's Next?
|
|
@@ -123,12 +123,12 @@ Key parameters for the `update` command:
|
|
|
123
123
|
|
|
124
124
|
## Optimizing the Overall Structure
|
|
125
125
|
|
|
126
|
-
Beyond refining the content of individual documents, you can also adjust the overall documentation structure. If a section is missing or the existing organization could be improved, you can provide feedback to the structure
|
|
126
|
+
Beyond refining the content of individual documents, you can also adjust the overall documentation structure. If a section is missing or the existing organization could be improved, you can provide feedback to improve the documentation structure using the `generate` command with the `--feedback` flag.
|
|
127
127
|
|
|
128
128
|
This command instructs DocSmith to reconsider the entire document plan based on your new input.
|
|
129
129
|
|
|
130
130
|
```bash
|
|
131
|
-
# Regenerate the structure
|
|
131
|
+
# Regenerate the documentation structure with specific feedback
|
|
132
132
|
aigne doc generate --feedback "Remove the 'About' section and add a more detailed 'API Reference'."
|
|
133
133
|
```
|
|
134
134
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
// Create mock functions for blocklet module only
|
|
4
|
+
const mockGetComponentInfoWithMountPoint = mock();
|
|
5
|
+
const mockGetComponentInfo = mock();
|
|
6
|
+
|
|
7
|
+
// Mock only the blocklet module globally (it's safe as it's specific to this functionality)
|
|
8
|
+
mock.module("../utils/blocklet.mjs", () => ({
|
|
9
|
+
getComponentInfoWithMountPoint: mockGetComponentInfoWithMountPoint,
|
|
10
|
+
getComponentInfo: mockGetComponentInfo,
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
// Mock the open module to prevent opening browser during tests
|
|
14
|
+
const mockOpenDefault = mock(() => Promise.resolve());
|
|
15
|
+
mock.module("open", () => ({
|
|
16
|
+
default: mockOpenDefault,
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
// Import the real utils module and deploy function
|
|
20
|
+
import { deploy } from "../utils/deploy.mjs";
|
|
21
|
+
import * as utils from "../utils/utils.mjs";
|
|
22
|
+
|
|
23
|
+
describe("deploy function", () => {
|
|
24
|
+
let originalFetch;
|
|
25
|
+
let originalConsole;
|
|
26
|
+
let consoleOutput;
|
|
27
|
+
let saveValueToConfigSpy;
|
|
28
|
+
let originalSetTimeout;
|
|
29
|
+
|
|
30
|
+
beforeEach(async () => {
|
|
31
|
+
// Reset environment
|
|
32
|
+
process.env.DOC_SMITH_BASE_URL = "https://test.example.com";
|
|
33
|
+
process.env.NODE_ENV = "test";
|
|
34
|
+
|
|
35
|
+
// Mock console to capture output
|
|
36
|
+
consoleOutput = [];
|
|
37
|
+
originalConsole = {
|
|
38
|
+
log: console.log,
|
|
39
|
+
error: console.error,
|
|
40
|
+
};
|
|
41
|
+
console.log = (...args) => consoleOutput.push({ type: "log", args });
|
|
42
|
+
console.error = (...args) => consoleOutput.push({ type: "error", args });
|
|
43
|
+
|
|
44
|
+
// Mock setTimeout to make tests run instantly
|
|
45
|
+
originalSetTimeout = global.setTimeout;
|
|
46
|
+
global.setTimeout = (callback, _delay) => {
|
|
47
|
+
// Call immediately in tests
|
|
48
|
+
return originalSetTimeout(callback, 0);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Mock fetch
|
|
52
|
+
originalFetch = global.fetch;
|
|
53
|
+
|
|
54
|
+
// Use spyOn to mock saveValueToConfig without affecting other tests
|
|
55
|
+
saveValueToConfigSpy = spyOn(utils, "saveValueToConfig").mockResolvedValue();
|
|
56
|
+
|
|
57
|
+
// Reset blocklet mocks
|
|
58
|
+
mockGetComponentInfoWithMountPoint.mockReset();
|
|
59
|
+
mockGetComponentInfo.mockReset();
|
|
60
|
+
|
|
61
|
+
// Set default mock implementations
|
|
62
|
+
mockGetComponentInfoWithMountPoint.mockResolvedValue({
|
|
63
|
+
mountPoint: "/payment",
|
|
64
|
+
PAYMENT_LINK_ID: "test-payment-id",
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
mockGetComponentInfo.mockResolvedValue({
|
|
68
|
+
status: "running",
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
afterEach(() => {
|
|
73
|
+
// Restore originals
|
|
74
|
+
global.fetch = originalFetch;
|
|
75
|
+
global.setTimeout = originalSetTimeout;
|
|
76
|
+
console.log = originalConsole.log;
|
|
77
|
+
console.error = originalConsole.error;
|
|
78
|
+
|
|
79
|
+
// Restore spies
|
|
80
|
+
if (saveValueToConfigSpy) {
|
|
81
|
+
saveValueToConfigSpy.mockRestore();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Reset open mock
|
|
85
|
+
mockOpenDefault.mockReset();
|
|
86
|
+
|
|
87
|
+
// Reset environment
|
|
88
|
+
delete process.env.NODE_ENV;
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("successful deployment flow", async () => {
|
|
92
|
+
// Mock API responses for the complete flow
|
|
93
|
+
let callCount = 0;
|
|
94
|
+
global.fetch = mock(async (url) => {
|
|
95
|
+
callCount++;
|
|
96
|
+
|
|
97
|
+
// Step 1: Create payment session
|
|
98
|
+
if (url.includes("/api/checkout-sessions/start")) {
|
|
99
|
+
return {
|
|
100
|
+
ok: true,
|
|
101
|
+
status: 200,
|
|
102
|
+
json: async () => ({
|
|
103
|
+
checkoutSession: { id: "checkout-123" },
|
|
104
|
+
paymentUrl: "https://payment.test/checkout-123",
|
|
105
|
+
}),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Step 2-4: Poll payment/installation/service status
|
|
110
|
+
if (url.includes("/api/vendors/order/checkout-123/status")) {
|
|
111
|
+
if (callCount <= 2) {
|
|
112
|
+
// First call: payment completed, installation in progress
|
|
113
|
+
return {
|
|
114
|
+
ok: true,
|
|
115
|
+
status: 200,
|
|
116
|
+
json: async () => ({
|
|
117
|
+
payment_status: "paid",
|
|
118
|
+
vendors: [{ id: "vendor-1", progress: 50, appUrl: null }],
|
|
119
|
+
}),
|
|
120
|
+
};
|
|
121
|
+
} else {
|
|
122
|
+
// Subsequent calls: installation complete
|
|
123
|
+
return {
|
|
124
|
+
ok: true,
|
|
125
|
+
status: 200,
|
|
126
|
+
json: async () => ({
|
|
127
|
+
payment_status: "paid",
|
|
128
|
+
vendors: [{ id: "vendor-1", progress: 100, appUrl: "https://app.test" }],
|
|
129
|
+
}),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Step 5: Get order details
|
|
135
|
+
if (url.includes("/api/vendors/order/checkout-123/detail")) {
|
|
136
|
+
return {
|
|
137
|
+
ok: true,
|
|
138
|
+
status: 200,
|
|
139
|
+
json: async () => ({
|
|
140
|
+
vendors: [
|
|
141
|
+
{
|
|
142
|
+
appUrl: "https://app.test",
|
|
143
|
+
dashboardUrl: "https://dashboard.test",
|
|
144
|
+
homeUrl: "https://home.test",
|
|
145
|
+
token: "auth-token-123",
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
}),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
throw new Error(`Unexpected URL: ${url}`);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const result = await deploy();
|
|
156
|
+
|
|
157
|
+
// Verify result
|
|
158
|
+
expect(result).toEqual({
|
|
159
|
+
appUrl: "https://app.test",
|
|
160
|
+
homeUrl: "https://home.test",
|
|
161
|
+
token: "auth-token-123",
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Verify saveValueToConfig was called
|
|
165
|
+
expect(saveValueToConfigSpy).toHaveBeenCalledWith(
|
|
166
|
+
"checkoutId",
|
|
167
|
+
"checkout-123",
|
|
168
|
+
"Checkout ID for document deployment service",
|
|
169
|
+
);
|
|
170
|
+
expect(saveValueToConfigSpy).toHaveBeenCalledWith(
|
|
171
|
+
"paymentUrl",
|
|
172
|
+
expect.stringContaining("payment"),
|
|
173
|
+
"Payment URL for document deployment service",
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
// Verify console output shows progress
|
|
177
|
+
const logs = consoleOutput.filter((o) => o.type === "log").map((o) => o.args.join(" "));
|
|
178
|
+
expect(logs.some((log) => log.includes("Step 1/4: Waiting for payment"))).toBe(true);
|
|
179
|
+
expect(logs.some((log) => log.includes("Step 2/4: Installing service"))).toBe(true);
|
|
180
|
+
expect(logs.some((log) => log.includes("Step 3/4: Starting service"))).toBe(true);
|
|
181
|
+
expect(logs.some((log) => log.includes("Step 4/4: Getting service URL"))).toBe(true);
|
|
182
|
+
expect(logs.some((log) => log.includes("Your website is available at"))).toBe(true);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test("handles missing payment link ID", async () => {
|
|
186
|
+
mockGetComponentInfoWithMountPoint.mockResolvedValue({
|
|
187
|
+
mountPoint: "/payment",
|
|
188
|
+
PAYMENT_LINK_ID: null,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
await expect(deploy()).rejects.toThrow("Payment link ID not found");
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("handles payment session creation failure", async () => {
|
|
195
|
+
global.fetch = mock(async (url) => {
|
|
196
|
+
if (url.includes("/api/checkout-sessions/start")) {
|
|
197
|
+
return {
|
|
198
|
+
ok: false,
|
|
199
|
+
status: 400,
|
|
200
|
+
json: async () => ({ error: "Payment creation failed" }),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
await expect(deploy()).rejects.toThrow("Failed to create payment session");
|
|
206
|
+
|
|
207
|
+
const errors = consoleOutput.filter((o) => o.type === "error");
|
|
208
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("handles network errors gracefully", async () => {
|
|
212
|
+
global.fetch = mock(async () => {
|
|
213
|
+
throw new Error("Network connection failed");
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
await expect(deploy()).rejects.toThrow("Failed to create payment session");
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("handles browser opening failure gracefully", async () => {
|
|
220
|
+
// Mock successful API flow
|
|
221
|
+
global.fetch = mock(async (url) => {
|
|
222
|
+
if (url.includes("/api/checkout-sessions/start")) {
|
|
223
|
+
return {
|
|
224
|
+
ok: true,
|
|
225
|
+
status: 200,
|
|
226
|
+
json: async () => ({
|
|
227
|
+
checkoutSession: { id: "checkout-123" },
|
|
228
|
+
paymentUrl: "https://payment.test/checkout-123",
|
|
229
|
+
}),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (url.includes("/status")) {
|
|
234
|
+
return {
|
|
235
|
+
ok: true,
|
|
236
|
+
status: 200,
|
|
237
|
+
json: async () => ({
|
|
238
|
+
payment_status: "paid",
|
|
239
|
+
vendors: [{ id: "vendor-1", progress: 100, appUrl: "https://app.test" }],
|
|
240
|
+
}),
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (url.includes("/detail")) {
|
|
245
|
+
return {
|
|
246
|
+
ok: true,
|
|
247
|
+
status: 200,
|
|
248
|
+
json: async () => ({
|
|
249
|
+
vendors: [
|
|
250
|
+
{
|
|
251
|
+
appUrl: "https://app.test",
|
|
252
|
+
homeUrl: "https://home.test",
|
|
253
|
+
token: "auth-token-123",
|
|
254
|
+
},
|
|
255
|
+
],
|
|
256
|
+
}),
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// Mock open to fail
|
|
262
|
+
mockOpenDefault.mockRejectedValue(new Error("Cannot open browser"));
|
|
263
|
+
|
|
264
|
+
// Call deploy without cached parameters - should still succeed
|
|
265
|
+
const result = await deploy();
|
|
266
|
+
|
|
267
|
+
// Should still complete successfully despite browser opening failure
|
|
268
|
+
expect(result.appUrl).toBe("https://app.test");
|
|
269
|
+
expect(result.homeUrl).toBe("https://home.test");
|
|
270
|
+
expect(result.token).toBe("auth-token-123");
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test("handles cached checkout ID", async () => {
|
|
274
|
+
// Mock successful status check for cached ID
|
|
275
|
+
global.fetch = mock(async (url) => {
|
|
276
|
+
if (url.includes("/status")) {
|
|
277
|
+
return {
|
|
278
|
+
ok: true,
|
|
279
|
+
status: 200,
|
|
280
|
+
json: async () => ({
|
|
281
|
+
payment_status: "paid",
|
|
282
|
+
vendors: [{ id: "vendor-1", progress: 100, appUrl: "https://app.test" }],
|
|
283
|
+
}),
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (url.includes("/detail")) {
|
|
288
|
+
return {
|
|
289
|
+
ok: true,
|
|
290
|
+
status: 200,
|
|
291
|
+
json: async () => ({
|
|
292
|
+
vendors: [
|
|
293
|
+
{
|
|
294
|
+
appUrl: "https://app.test",
|
|
295
|
+
homeUrl: "https://home.test",
|
|
296
|
+
token: "auth-token-123",
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
}),
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
const result = await deploy("existing-checkout-id", "https://cached-payment.url");
|
|
305
|
+
|
|
306
|
+
expect(result.appUrl).toBe("https://app.test");
|
|
307
|
+
|
|
308
|
+
// Should not call open since using cached checkout
|
|
309
|
+
expect(mockOpenDefault).not.toHaveBeenCalled();
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test("clears checkout ID when cache check fails", async () => {
|
|
313
|
+
// Mock successful responses for the complete flow after cache check fails
|
|
314
|
+
let callCount = 0;
|
|
315
|
+
global.fetch = mock(async (url) => {
|
|
316
|
+
callCount++;
|
|
317
|
+
|
|
318
|
+
// First call: cache check fails
|
|
319
|
+
if (callCount === 1 && url.includes("/status")) {
|
|
320
|
+
throw new Error("Network error during cache check");
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Create payment session
|
|
324
|
+
if (url.includes("/api/checkout-sessions/start")) {
|
|
325
|
+
return {
|
|
326
|
+
ok: true,
|
|
327
|
+
status: 200,
|
|
328
|
+
json: async () => ({
|
|
329
|
+
checkoutSession: { id: "new-checkout-123" },
|
|
330
|
+
paymentUrl: "https://payment.test/new-checkout-123",
|
|
331
|
+
}),
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Subsequent status checks and detail calls
|
|
336
|
+
if (url.includes("/status")) {
|
|
337
|
+
return {
|
|
338
|
+
ok: true,
|
|
339
|
+
status: 200,
|
|
340
|
+
json: async () => ({
|
|
341
|
+
payment_status: "paid",
|
|
342
|
+
vendors: [{ id: "vendor-1", progress: 100, appUrl: "https://app.test" }],
|
|
343
|
+
}),
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (url.includes("/detail")) {
|
|
348
|
+
return {
|
|
349
|
+
ok: true,
|
|
350
|
+
status: 200,
|
|
351
|
+
json: async () => ({
|
|
352
|
+
vendors: [
|
|
353
|
+
{
|
|
354
|
+
appUrl: "https://app.test",
|
|
355
|
+
homeUrl: "https://home.test",
|
|
356
|
+
token: "auth-token-123",
|
|
357
|
+
},
|
|
358
|
+
],
|
|
359
|
+
}),
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
// Call deploy with invalid cached checkout ID - should clear it and create new one
|
|
365
|
+
const result = await deploy("invalid-checkout-id");
|
|
366
|
+
|
|
367
|
+
expect(result.appUrl).toBe("https://app.test");
|
|
368
|
+
|
|
369
|
+
// Verify that checkoutId was cleared due to cache check failure
|
|
370
|
+
expect(saveValueToConfigSpy).toHaveBeenCalledWith(
|
|
371
|
+
"checkoutId",
|
|
372
|
+
"",
|
|
373
|
+
"Checkout ID for document deployment service",
|
|
374
|
+
);
|
|
375
|
+
});
|
|
376
|
+
});
|
|
@@ -1307,7 +1307,9 @@ describe("init", () => {
|
|
|
1307
1307
|
if (options.message.includes("[1/8]") && options.validate) {
|
|
1308
1308
|
// Test the validation function directly
|
|
1309
1309
|
const validationResult = options.validate([]);
|
|
1310
|
-
expect(validationResult).toBe(
|
|
1310
|
+
expect(validationResult).toBe(
|
|
1311
|
+
"Please choose at least one goal for your documentation.",
|
|
1312
|
+
);
|
|
1311
1313
|
validateCalled = true;
|
|
1312
1314
|
// Return valid result after testing validation
|
|
1313
1315
|
return Promise.resolve(["getStarted"]);
|
|
@@ -1346,7 +1348,7 @@ describe("init", () => {
|
|
|
1346
1348
|
if (options.message.includes("[2/8]") && options.validate) {
|
|
1347
1349
|
// Test the validation function for target audience
|
|
1348
1350
|
const validationResult = options.validate([]);
|
|
1349
|
-
expect(validationResult).toBe("Please
|
|
1351
|
+
expect(validationResult).toBe("Please choose at least one audience.");
|
|
1350
1352
|
audienceValidateCalled = true;
|
|
1351
1353
|
return Promise.resolve(["developers"]); // Valid result after testing
|
|
1352
1354
|
}
|
|
@@ -1385,11 +1387,11 @@ describe("init", () => {
|
|
|
1385
1387
|
if (options.message.includes("Which is most important?") && options.validate) {
|
|
1386
1388
|
// Test validation for empty selection
|
|
1387
1389
|
let validationResult = options.validate([]);
|
|
1388
|
-
expect(validationResult).toBe("Please
|
|
1390
|
+
expect(validationResult).toBe("Please choose at least one priority.");
|
|
1389
1391
|
|
|
1390
1392
|
// Test validation for too many selections
|
|
1391
1393
|
validationResult = options.validate(["getStarted", "completeTasks", "findAnswers"]);
|
|
1392
|
-
expect(validationResult).toBe("Please
|
|
1394
|
+
expect(validationResult).toBe("Please choose maximum 2 priorities.");
|
|
1393
1395
|
|
|
1394
1396
|
// Test validation for valid selection
|
|
1395
1397
|
validationResult = options.validate(["getStarted", "completeTasks"]);
|
package/utils/auth-utils.mjs
CHANGED
|
@@ -88,7 +88,7 @@ export async function getAccessToken(appUrl, ltToken = "") {
|
|
|
88
88
|
const result = await createConnect({
|
|
89
89
|
connectUrl: connectUrl,
|
|
90
90
|
connectAction: "gen-simple-access-key",
|
|
91
|
-
source: `AIGNE DocSmith connect to
|
|
91
|
+
source: `AIGNE DocSmith connect to website`,
|
|
92
92
|
closeOnSuccess: true,
|
|
93
93
|
appName: "AIGNE DocSmith",
|
|
94
94
|
appLogo: "https://docsmith.aigne.io/image-bin/uploads/a7910a71364ee15a27e86f869ad59009.svg",
|
package/utils/deploy.mjs
CHANGED
|
@@ -116,8 +116,8 @@ export async function deploy(id, cachedUrl) {
|
|
|
116
116
|
console.log(`${chalk.blue("⏳")} Step 1/4: Waiting for payment...`);
|
|
117
117
|
console.log(`${chalk.blue("🔗")} Payment link: ${chalk.cyan(paymentUrl)}\n`);
|
|
118
118
|
await pollPaymentStatus(checkoutId);
|
|
119
|
-
saveValueToConfig("checkoutId", checkoutId, "Checkout ID for document deployment service");
|
|
120
|
-
saveValueToConfig("paymentUrl", paymentUrl, "Payment URL for document deployment service");
|
|
119
|
+
await saveValueToConfig("checkoutId", checkoutId, "Checkout ID for document deployment service");
|
|
120
|
+
await saveValueToConfig("paymentUrl", paymentUrl, "Payment URL for document deployment service");
|
|
121
121
|
|
|
122
122
|
// Step 3: Wait for service installation
|
|
123
123
|
console.log(`${chalk.blue("📦")} Step 2/4: Installing service...`);
|
|
@@ -174,7 +174,7 @@ async function checkCacheCheckoutId(checkoutId) {
|
|
|
174
174
|
|
|
175
175
|
return isPaid ? checkoutId : "";
|
|
176
176
|
} catch (_error) {
|
|
177
|
-
saveValueToConfig("checkoutId", "", "Checkout ID for document deployment service");
|
|
177
|
+
await saveValueToConfig("checkoutId", "", "Checkout ID for document deployment service");
|
|
178
178
|
return "";
|
|
179
179
|
}
|
|
180
180
|
}
|