@aigne/doc-smith 0.8.4 → 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 +30 -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 +34 -28
- package/agents/manage-prefs.mjs +15 -15
- package/agents/publish-docs.mjs +52 -5
- 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/_sidebar.md +1 -1
- 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 +8 -6
- package/utils/auth-utils.mjs +10 -3
- package/utils/blocklet.mjs +25 -6
- package/utils/constants.mjs +2 -0
- package/utils/deploy.mjs +404 -0
|
@@ -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/_sidebar.md
CHANGED
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
|
+
});
|
|
@@ -913,7 +913,7 @@ describe("generateYAML", () => {
|
|
|
913
913
|
// Should always include these sections
|
|
914
914
|
expect(result).toContain("# Project information for documentation publishing");
|
|
915
915
|
expect(result).toContain("# Documentation Configuration");
|
|
916
|
-
expect(result).toContain("projectName:
|
|
916
|
+
expect(result).toContain('"projectName":');
|
|
917
917
|
expect(result).toContain("documentPurpose:");
|
|
918
918
|
expect(result).toContain("targetAudienceTypes:");
|
|
919
919
|
expect(result).toContain("locale:");
|
|
@@ -1287,7 +1287,7 @@ describe("init", () => {
|
|
|
1287
1287
|
|
|
1288
1288
|
// Config should be generated since original was empty
|
|
1289
1289
|
const configContent = await fs.readFile(configPath, "utf8");
|
|
1290
|
-
expect(configContent).toContain("projectName:
|
|
1290
|
+
expect(configContent).toContain('"projectName":');
|
|
1291
1291
|
expect(configContent).toContain("locale: en");
|
|
1292
1292
|
} finally {
|
|
1293
1293
|
await cleanupTempDir(tempDir);
|
|
@@ -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
|
@@ -25,7 +25,7 @@ const WELLKNOWN_SERVICE_PATH_PREFIX = "/.well-known/service";
|
|
|
25
25
|
* @param {string} appUrl - The application URL
|
|
26
26
|
* @returns {Promise<string>} - The access token
|
|
27
27
|
*/
|
|
28
|
-
export async function getAccessToken(appUrl) {
|
|
28
|
+
export async function getAccessToken(appUrl, ltToken = "") {
|
|
29
29
|
const DOC_SMITH_ENV_FILE = join(homedir(), ".aigne", "doc-smith-connected.yaml");
|
|
30
30
|
const { hostname } = new URL(appUrl);
|
|
31
31
|
|
|
@@ -88,11 +88,18 @@ export async function getAccessToken(appUrl) {
|
|
|
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",
|
|
95
|
-
openPage: (pageUrl) =>
|
|
95
|
+
openPage: (pageUrl) => {
|
|
96
|
+
const url = new URL(pageUrl);
|
|
97
|
+
if (ltToken) {
|
|
98
|
+
url.searchParams.set("__lt", ltToken);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
open(url.toString());
|
|
102
|
+
},
|
|
96
103
|
});
|
|
97
104
|
|
|
98
105
|
accessToken = result.accessKeySecret;
|