@aigne/doc-smith 0.9.7-beta.1 → 0.9.7-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/agents/clear/choose-contents.mjs +1 -2
- package/agents/clear/clear-auth-tokens.mjs +8 -29
- package/agents/evaluate/document-structure.yaml +1 -2
- package/agents/evaluate/document.yaml +1 -2
- package/agents/history/view.mjs +1 -1
- package/agents/media/generate-media-description.yaml +1 -1
- package/agents/update/update-single/update-single-document-detail.mjs +5 -1
- package/agents/utils/check-detail-result.mjs +1 -1
- package/aigne.yaml +3 -4
- package/package.json +2 -1
- package/prompts/detail/custom/custom-components/x-card-usage-rules.md +1 -0
- package/utils/auth-utils.mjs +7 -35
- package/utils/history-utils.mjs +1 -1
- package/utils/markdown-checker.mjs +63 -0
- package/utils/store/index.mjs +45 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9.7-beta.3](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.9.7-beta.2...v0.9.7-beta.3) (2025-11-28)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* ensure x-card data-image is accessible ([#341](https://github.com/AIGNE-io/aigne-doc-smith/issues/341)) ([9209990](https://github.com/AIGNE-io/aigne-doc-smith/commit/92099909d9b1a0d5760a9b9977c940287af1aaec))
|
|
9
|
+
|
|
10
|
+
## [0.9.7-beta.2](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.9.7-beta.1...v0.9.7-beta.2) (2025-11-28)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* support secret store for publish config ([#339](https://github.com/AIGNE-io/aigne-doc-smith/issues/339)) ([31bb282](https://github.com/AIGNE-io/aigne-doc-smith/commit/31bb282065707ad718d68d65722a9ced70047c91))
|
|
16
|
+
|
|
3
17
|
## [0.9.7-beta.1](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.9.7-beta...v0.9.7-beta.1) (2025-11-27)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { getDocSmithEnvFilePath } from "../../utils/auth-utils.mjs";
|
|
2
1
|
import {
|
|
3
2
|
getConfigFilePath,
|
|
4
3
|
getMediaDescriptionCachePath,
|
|
@@ -32,7 +31,7 @@ const TARGET_METADATA = {
|
|
|
32
31
|
authTokens: {
|
|
33
32
|
label: "Authorizations",
|
|
34
33
|
description: () =>
|
|
35
|
-
|
|
34
|
+
"Delete authorization information. You will need to re-authorize after clearing.",
|
|
36
35
|
agent: "clearAuthTokens",
|
|
37
36
|
},
|
|
38
37
|
deploymentConfig: {
|
|
@@ -1,25 +1,13 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { readFile, writeFile } from "node:fs/promises";
|
|
3
1
|
import chalk from "chalk";
|
|
4
|
-
import {
|
|
5
|
-
import { getDocSmithEnvFilePath } from "../../utils/auth-utils.mjs";
|
|
2
|
+
import { createStore } from "../../utils/store/index.mjs";
|
|
6
3
|
|
|
7
4
|
export default async function clearAuthTokens(_input = {}, options = {}) {
|
|
8
|
-
const
|
|
9
|
-
// Check if the file exists
|
|
10
|
-
if (!existsSync(DOC_SMITH_ENV_FILE)) {
|
|
11
|
-
return {
|
|
12
|
-
message: "🔑 No site authorizations found to clear",
|
|
13
|
-
};
|
|
14
|
-
}
|
|
5
|
+
const store = await createStore();
|
|
15
6
|
|
|
16
7
|
try {
|
|
17
|
-
|
|
18
|
-
const data = await readFile(DOC_SMITH_ENV_FILE, "utf8");
|
|
19
|
-
const envs = parse(data) || {};
|
|
20
|
-
|
|
8
|
+
const listMap = await store.listMap();
|
|
21
9
|
// Get all available sites
|
|
22
|
-
const siteHostnames = Object.keys(
|
|
10
|
+
const siteHostnames = Object.keys(listMap);
|
|
23
11
|
|
|
24
12
|
if (siteHostnames.length === 0) {
|
|
25
13
|
return {
|
|
@@ -67,24 +55,15 @@ export default async function clearAuthTokens(_input = {}, options = {}) {
|
|
|
67
55
|
|
|
68
56
|
if (selectedSites.includes("__ALL__")) {
|
|
69
57
|
// Clear all site authorizations
|
|
70
|
-
await
|
|
58
|
+
await store.clear();
|
|
71
59
|
results.push(`✔ Cleared site authorization for all sites (${siteHostnames.length} sites)`);
|
|
72
60
|
clearedCount = siteHostnames.length;
|
|
73
61
|
} else {
|
|
74
|
-
// Clear site authorizations for selected sites
|
|
75
|
-
const updatedEnvs = { ...envs };
|
|
76
|
-
|
|
77
62
|
for (const hostname of selectedSites) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
results.push(`✔ Cleared site authorization for ${chalk.cyan(hostname)}`);
|
|
83
|
-
clearedCount++;
|
|
84
|
-
}
|
|
63
|
+
await store.deleteItem(hostname);
|
|
64
|
+
results.push(`✔ Cleared site authorization for ${chalk.cyan(hostname)}`);
|
|
65
|
+
clearedCount++;
|
|
85
66
|
}
|
|
86
|
-
|
|
87
|
-
await writeFile(DOC_SMITH_ENV_FILE, stringify(updatedEnvs));
|
|
88
67
|
}
|
|
89
68
|
|
|
90
69
|
const header = `🔑 Successfully cleared site authorizations!`;
|
|
@@ -3,9 +3,8 @@ description: Evaluates the results generated by the document-structure agent to
|
|
|
3
3
|
instructions:
|
|
4
4
|
url: ../../prompts/evaluate/document-structure.md
|
|
5
5
|
model:
|
|
6
|
-
model:
|
|
6
|
+
model: gpt-5
|
|
7
7
|
temperature: 1
|
|
8
|
-
# model: anthropic/claude-opus-4-0
|
|
9
8
|
task_render_mode: collapse
|
|
10
9
|
task_title: Evaluate the structure of the documentation
|
|
11
10
|
input_schema:
|
|
@@ -3,9 +3,8 @@ description: Evaluates the quality of generated document content, ensuring compl
|
|
|
3
3
|
instructions:
|
|
4
4
|
url: ../../prompts/evaluate/document.md
|
|
5
5
|
model:
|
|
6
|
-
model:
|
|
6
|
+
model: gpt-5
|
|
7
7
|
temperature: 1
|
|
8
|
-
# model: anthropic/claude-opus-4-0
|
|
9
8
|
task_render_mode: collapse
|
|
10
9
|
task_title: Evaluate document for '{{ title }}'
|
|
11
10
|
input_schema:
|
package/agents/history/view.mjs
CHANGED
|
@@ -103,7 +103,11 @@ Your task is to remove ${DIAGRAM_PLACEHOLDER} and adjust the document context (b
|
|
|
103
103
|
|
|
104
104
|
<user_feedback>
|
|
105
105
|
{{feedback}}
|
|
106
|
-
</user_feedback
|
|
106
|
+
</user_feedback>
|
|
107
|
+
|
|
108
|
+
<output_constraints>
|
|
109
|
+
- Do not provide any explanations; include only the document content itself
|
|
110
|
+
</output_constraints>`;
|
|
107
111
|
const deleteAgent = AIAgent.from({
|
|
108
112
|
name: "deleteDiagram",
|
|
109
113
|
description: "Remove a diagram from the document content",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { checkMarkdown } from "../../utils/markdown-checker.mjs";
|
|
2
1
|
import { buildAllowedLinksFromStructure } from "../../utils/docs-finder-utils.mjs";
|
|
2
|
+
import { checkMarkdown } from "../../utils/markdown-checker.mjs";
|
|
3
3
|
|
|
4
4
|
export default async function checkDetailResult({ documentStructure, reviewContent, docsDir }) {
|
|
5
5
|
if (!reviewContent || reviewContent.trim() === "") {
|
package/aigne.yaml
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env aigne
|
|
2
2
|
|
|
3
3
|
model:
|
|
4
|
-
# model:
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
# model: gemini-3-pro-preview
|
|
5
|
+
model: gemini-2.5-pro
|
|
6
|
+
temperature: 0.8
|
|
7
7
|
# https://github.com/AIGNE-io/aigne-framework/blob/main/models/gemini/src/gemini-chat-model.ts#L115
|
|
8
8
|
reasoning_effort:
|
|
9
9
|
$get: reasoningEffort
|
|
10
|
-
temperature: 0.8
|
|
11
10
|
agents:
|
|
12
11
|
# Initialization
|
|
13
12
|
- ./agents/init/index.mjs
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aigne/doc-smith",
|
|
3
|
-
"version": "0.9.7-beta.
|
|
3
|
+
"version": "0.9.7-beta.3",
|
|
4
4
|
"description": "AI-driven documentation generation tool built on the AIGNE Framework",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"@aigne/cli": "^1.56.0",
|
|
28
28
|
"@aigne/core": "^1.69.0",
|
|
29
29
|
"@aigne/publish-docs": "^0.14.1",
|
|
30
|
+
"@aigne/secrets": "^0.1.1-beta.3",
|
|
30
31
|
"@blocklet/payment-broker-client": "^1.22.24",
|
|
31
32
|
"@terrastruct/d2": "^0.1.33",
|
|
32
33
|
"chalk": "^5.5.0",
|
|
@@ -9,6 +9,7 @@ XCard is individual link display card, suitable for displaying individual links
|
|
|
9
9
|
- `data-icon` (optional): Icon identifier (e.g., lucide:icon-name or material-symbols:rocket-outline).
|
|
10
10
|
- Icons should prioritize Lucide (lucide:icon-name). If not available in Lucide, use Iconify (collection:icon-name, e.g., material-symbols:rocket-outline).
|
|
11
11
|
- `data-image` (optional): Image URL, can coexist with icon.
|
|
12
|
+
- Prefer to use image url from `<media_file_list>`.
|
|
12
13
|
- **Requirement**: At least one of `data-icon` or `data-image` must be provided.
|
|
13
14
|
- It's recommended to always provide data-icon.
|
|
14
15
|
- `data-href` (optional): Navigation link for clicking the card or button.
|
package/utils/auth-utils.mjs
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { existsSync, mkdirSync } from "node:fs";
|
|
2
|
-
import { readFile, writeFile } from "node:fs/promises";
|
|
3
2
|
import { homedir } from "node:os";
|
|
4
3
|
import { join } from "node:path";
|
|
5
4
|
import { createConnect } from "@aigne/cli/utils/aigne-hub/credential.js";
|
|
6
5
|
import chalk from "chalk";
|
|
7
6
|
import open from "open";
|
|
8
|
-
import { joinURL } from "ufo";
|
|
9
|
-
import { parse, stringify } from "yaml";
|
|
10
|
-
|
|
7
|
+
import { joinURL, withQuery } from "ufo";
|
|
11
8
|
import {
|
|
12
9
|
ComponentNotFoundError,
|
|
13
10
|
getComponentMountPoint,
|
|
@@ -21,7 +18,7 @@ import {
|
|
|
21
18
|
DISCUSS_KIT_STORE_URL,
|
|
22
19
|
PAYMENT_KIT_DID,
|
|
23
20
|
} from "./constants/index.mjs";
|
|
24
|
-
import {
|
|
21
|
+
import { createStore } from "./store/index.mjs";
|
|
25
22
|
|
|
26
23
|
const WELLKNOWN_SERVICE_PATH_PREFIX = "/.well-known/service";
|
|
27
24
|
|
|
@@ -30,10 +27,6 @@ const FETCH_INTERVAL = 3000; // 3 seconds
|
|
|
30
27
|
|
|
31
28
|
const RETRY_COUNT = (TIMEOUT_MINUTES * 60 * 1000) / FETCH_INTERVAL;
|
|
32
29
|
|
|
33
|
-
export function getDocSmithEnvFilePath() {
|
|
34
|
-
return join(homedir(), ".aigne", "doc-smith-connected.yaml");
|
|
35
|
-
}
|
|
36
|
-
|
|
37
30
|
/**
|
|
38
31
|
* Get access token from environment, config file, or prompt user for authorization
|
|
39
32
|
* @param {string} baseUrl - The application URL
|
|
@@ -41,7 +34,7 @@ export function getDocSmithEnvFilePath() {
|
|
|
41
34
|
*/
|
|
42
35
|
export async function getCachedAccessToken(baseUrl) {
|
|
43
36
|
const { hostname: targetHostname } = new URL(baseUrl);
|
|
44
|
-
const
|
|
37
|
+
const store = await createStore();
|
|
45
38
|
|
|
46
39
|
let accessToken =
|
|
47
40
|
process.env.DOC_SMITH_PUBLISH_ACCESS_TOKEN || process.env.DOC_DISCUSS_KIT_ACCESS_TOKEN;
|
|
@@ -49,16 +42,8 @@ export async function getCachedAccessToken(baseUrl) {
|
|
|
49
42
|
// Check if access token exists in environment or config file
|
|
50
43
|
if (!accessToken) {
|
|
51
44
|
try {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (data.includes("DOC_DISCUSS_KIT_ACCESS_TOKEN")) {
|
|
55
|
-
// Handle empty or invalid YAML files
|
|
56
|
-
const envs = data.trim() ? parse(data) : null;
|
|
57
|
-
if (envs?.[targetHostname]?.DOC_DISCUSS_KIT_ACCESS_TOKEN) {
|
|
58
|
-
accessToken = envs[targetHostname].DOC_DISCUSS_KIT_ACCESS_TOKEN;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
45
|
+
const storeItem = await store.getItem(targetHostname);
|
|
46
|
+
accessToken = storeItem?.DOC_DISCUSS_KIT_ACCESS_TOKEN;
|
|
62
47
|
} catch (error) {
|
|
63
48
|
console.warn("Could not read the configuration file:", error.message);
|
|
64
49
|
}
|
|
@@ -275,27 +260,14 @@ export async function getOfficialAccessToken(baseUrl, openPage = true, locale =
|
|
|
275
260
|
*/
|
|
276
261
|
async function saveTokenToConfigFile(hostname, fields) {
|
|
277
262
|
try {
|
|
278
|
-
const
|
|
263
|
+
const store = await createStore();
|
|
279
264
|
|
|
280
265
|
const aigneDir = join(homedir(), ".aigne");
|
|
281
266
|
if (!existsSync(aigneDir)) {
|
|
282
267
|
mkdirSync(aigneDir, { recursive: true });
|
|
283
268
|
}
|
|
284
269
|
|
|
285
|
-
|
|
286
|
-
if (existsSync(configFile)) {
|
|
287
|
-
const fileContent = await readFile(configFile, "utf8");
|
|
288
|
-
const parsedConfig = fileContent.trim() ? parse(fileContent) : null;
|
|
289
|
-
existingConfig = parsedConfig || {};
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
await writeFile(
|
|
293
|
-
configFile,
|
|
294
|
-
stringify({
|
|
295
|
-
...existingConfig,
|
|
296
|
-
[hostname]: fields,
|
|
297
|
-
}),
|
|
298
|
-
);
|
|
270
|
+
await store.setItem(hostname, fields);
|
|
299
271
|
} catch (error) {
|
|
300
272
|
console.warn(`Could not save the token to the configuration file: ${error.message}`, error);
|
|
301
273
|
// The token is already in the environment, so we don't need to throw an error here.
|
package/utils/history-utils.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execSync } from "node:child_process";
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { parse, stringify } from "yaml";
|
|
5
5
|
import { DOC_SMITH_DIR } from "./constants/index.mjs";
|
|
@@ -308,6 +308,67 @@ async function checkRemoteImages(markdown, source, errorMessages) {
|
|
|
308
308
|
}
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
+
async function checkXCardImage(markdown, source, errorMessages, markdownFilePath, baseDir) {
|
|
312
|
+
const imageRegex = /data-image="([^"]*)"/g;
|
|
313
|
+
let match;
|
|
314
|
+
|
|
315
|
+
while (true) {
|
|
316
|
+
match = imageRegex.exec(markdown);
|
|
317
|
+
if (match === null) break;
|
|
318
|
+
const imagePath = match[1].trim();
|
|
319
|
+
|
|
320
|
+
// Skip data URLs
|
|
321
|
+
if (/^data:/.test(imagePath)) continue;
|
|
322
|
+
|
|
323
|
+
if (isRelative(imagePath)) {
|
|
324
|
+
try {
|
|
325
|
+
let resolvedPath;
|
|
326
|
+
if (markdownFilePath) {
|
|
327
|
+
// Resolve relative to the markdown file's directory
|
|
328
|
+
const markdownDir = path.dirname(markdownFilePath);
|
|
329
|
+
resolvedPath = path.resolve(markdownDir, imagePath);
|
|
330
|
+
} else if (baseDir) {
|
|
331
|
+
// Resolve relative to the provided base directory
|
|
332
|
+
resolvedPath = path.resolve(baseDir, imagePath);
|
|
333
|
+
} else {
|
|
334
|
+
// Fallback to current working directory
|
|
335
|
+
resolvedPath = path.resolve(imagePath);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
339
|
+
errorMessages.push(
|
|
340
|
+
`Found invalid local image in ${source}: ${imagePath} - only valid media resources can be used`,
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
} catch {
|
|
344
|
+
errorMessages.push(
|
|
345
|
+
`Found invalid local image in ${source}: ${imagePath} - only valid media resources can be used`,
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
} else if (imagePath.startsWith("/")) {
|
|
349
|
+
try {
|
|
350
|
+
if (!fs.existsSync(imagePath)) {
|
|
351
|
+
errorMessages.push(
|
|
352
|
+
`Found invalid local image in ${source}: ${imagePath} - only valid media resources can be used`,
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
} catch {
|
|
356
|
+
errorMessages.push(
|
|
357
|
+
`Found invalid local image in ${source}: ${imagePath} - only valid media resources can be used`,
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
} else if (isRemoteFile(imagePath)) {
|
|
361
|
+
const isAvailable = await isRemoteFileAvailable(imagePath);
|
|
362
|
+
if (isAvailable) continue;
|
|
363
|
+
else {
|
|
364
|
+
errorMessages.push(
|
|
365
|
+
`Found invalid remote image in ${source}: ${imagePath} - only valid media resources can be used`,
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
311
372
|
/**
|
|
312
373
|
* Check content structure and formatting issues
|
|
313
374
|
* @param {string} markdown - The markdown content
|
|
@@ -449,6 +510,8 @@ export async function checkMarkdown(markdown, source = "content", options = {})
|
|
|
449
510
|
// 3. Check remote images existence
|
|
450
511
|
await checkRemoteImages(markdown, source, errorMessages);
|
|
451
512
|
|
|
513
|
+
await checkXCardImage(markdown, source, errorMessages, filePath, baseDir);
|
|
514
|
+
|
|
452
515
|
// 4. Check content structure and formatting issues
|
|
453
516
|
checkContentStructure(markdown, source, errorMessages);
|
|
454
517
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { access, rm } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import createSecretStore, { FileStore } from "@aigne/secrets";
|
|
5
|
+
|
|
6
|
+
export async function createStore() {
|
|
7
|
+
const filepath = join(homedir(), ".aigne", "doc-smith-connected.yaml");
|
|
8
|
+
const secretStore = await createSecretStore({
|
|
9
|
+
filepath,
|
|
10
|
+
serviceName: "aigne-doc-smith-publish",
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
async function migrate() {
|
|
14
|
+
// system doesn't support keyring
|
|
15
|
+
if (secretStore instanceof FileStore) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
// already migrated
|
|
19
|
+
try {
|
|
20
|
+
await access(filepath);
|
|
21
|
+
} catch {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const fileStore = new FileStore({ filepath });
|
|
26
|
+
const map = await fileStore.listMap();
|
|
27
|
+
for (const [key, value] of Object.entries(map)) {
|
|
28
|
+
await secretStore.setItem(key, value);
|
|
29
|
+
}
|
|
30
|
+
await rm(filepath);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function clear() {
|
|
34
|
+
const map = await secretStore.listMap();
|
|
35
|
+
for (const key of Object.keys(map)) {
|
|
36
|
+
await secretStore.deleteItem(key);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
await migrate();
|
|
41
|
+
|
|
42
|
+
secretStore.clear = clear;
|
|
43
|
+
|
|
44
|
+
return secretStore;
|
|
45
|
+
}
|