@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.
@@ -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: 用户语言,如 zhen
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: 用户偏好规则,YAML格式的结构规划和全局范围偏好
31
+ description: Your saved preferences for structure and documentation style
32
32
  docsType:
33
33
  type: string
34
- description: 文档类型,支持:generalgetting-startedreferencefaq
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: 根据 DataSources 分析项目名称,注意是原始项目名称
44
+ description: Project name identified from your content sources
45
45
  projectDesc:
46
46
  type: string
47
- description: 根据 DataSources 分析生成当前项目描述,为原始项目生成描述,描述简洁,不超过 50 个单词
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 the documentation to Discuss Kit
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: target website URL where the documentation will be published (optional - if not provided, will prompt for interactive input)
18
+ description: Website URL where docs will be published (optional - leave empty for interactive setup)
@@ -1,5 +1,5 @@
1
1
  name: translateDoc
2
- description: Translate the wiki article to another language
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: Language to translate the article into (e.g., 'zh_CN' for Chinese)
10
+ description: Target language (e.g., 'zh' for Chinese, 'ja' for Japanese)
11
11
  content:
12
12
  type: string
13
- description: Content to translate
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: Feedback for translation improvement
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: Translation of the content
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
@@ -14,4 +14,4 @@
14
14
  * [Advanced Topics](/advanced.md)
15
15
  * [How It Works](/advanced-how-it-works.md)
16
16
  * [Quality Assurance](/advanced-quality-assurance.md)
17
- * [Changelog](/changelog.md)
17
+ * [Changelog](/changelog.md)
@@ -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 document structure plan. |
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 document structure plan, such as adding, removing, or reorganizing sections. | `aigne doc generate --feedback "Add an API Reference section"` |
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 planning agent using the `generate` command with the `--feedback` flag.
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 plan with specific feedback
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/doc-smith",
3
- "version": "0.8.4",
3
+ "version": "0.8.6",
4
4
  "description": "",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -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("Please select at least one purpose.");
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 select at least one audience.");
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 select at least one priority.");
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 select maximum 2 priorities.");
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"]);
@@ -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 Discuss Kit`,
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) => open(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;