@aigne/doc-smith 0.9.10 → 0.9.11-beta

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.
Files changed (308) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +189 -219
  3. package/README.zh.md +270 -0
  4. package/agents/bash-executor/index.mjs +347 -0
  5. package/agents/clear/ai/intent.md +142 -0
  6. package/agents/clear/choose-contents.mjs +13 -65
  7. package/agents/clear/clear-auth-tokens.mjs +17 -21
  8. package/agents/clear/clear-deployment-config.mjs +33 -24
  9. package/agents/clear/index.yaml +1 -9
  10. package/agents/content-checker/ai/intent.md +209 -0
  11. package/agents/content-checker/clean-invalid-docs.mjs +254 -0
  12. package/agents/content-checker/index.mjs +191 -0
  13. package/agents/content-checker/validate-content.mjs +983 -0
  14. package/agents/generate-images/generate-image.yaml +75 -0
  15. package/agents/generate-images/generate-summary.mjs +213 -0
  16. package/agents/generate-images/index.yaml +39 -0
  17. package/agents/generate-images/prepare-generation.mjs +286 -0
  18. package/agents/generate-images/prepare-image-generation.mjs +130 -0
  19. package/{prompts/detail/diagram/generate-image-system.md → agents/generate-images/prompts/system.md} +22 -56
  20. package/agents/generate-images/prompts/user.md +85 -0
  21. package/agents/generate-images/save-image-result.mjs +247 -0
  22. package/agents/generate-images/scan-image-slots.mjs +247 -0
  23. package/agents/localize/index.yaml +19 -42
  24. package/{prompts/translate → agents/localize/prompts}/translate-document.md +0 -139
  25. package/agents/localize/translate-documents/generate-summary.mjs +163 -0
  26. package/agents/localize/translate-documents/load-glossary.mjs +52 -0
  27. package/agents/localize/translate-documents/prepare-translation.mjs +249 -0
  28. package/agents/localize/translate-documents/save-translation.mjs +171 -0
  29. package/agents/localize/translate-documents/translate-document-to-language.mjs +209 -0
  30. package/agents/localize/translate-documents/translate-document.yaml +23 -0
  31. package/agents/localize/translate-documents/translate-to-languages.yaml +10 -0
  32. package/agents/localize/translate-images/check-image-translation.mjs +225 -0
  33. package/agents/localize/translate-images/detect-text/detect-and-update-shared.mjs +148 -0
  34. package/agents/localize/translate-images/detect-text/detect-image-text.yaml +44 -0
  35. package/agents/localize/translate-images/detect-text/detect-images-text.yaml +21 -0
  36. package/agents/localize/translate-images/detect-text/prompts/detect-image-text-system.md +43 -0
  37. package/agents/localize/translate-images/detect-text/prompts/detect-image-text-user.md +14 -0
  38. package/agents/localize/translate-images/detect-text/save-text-detection.mjs +105 -0
  39. package/agents/localize/translate-images/prepare-image-input.mjs +124 -0
  40. package/agents/localize/translate-images/save-image-translation.mjs +172 -0
  41. package/agents/localize/translate-images/scan-doc-images.mjs +165 -0
  42. package/agents/localize/translate-images/translate-doc-images.yaml +24 -0
  43. package/agents/localize/{translate-diagram.yaml → translate-images/translate-image.yaml} +25 -14
  44. package/agents/publish/ai/intent.md +182 -0
  45. package/agents/publish/check.mjs +107 -0
  46. package/agents/publish/index.yaml +9 -14
  47. package/agents/publish/publish-docs.mjs +81 -61
  48. package/agents/publish/translate-meta.mjs +79 -58
  49. package/agents/save-document/index.mjs +260 -0
  50. package/agents/structure-checker/index.mjs +307 -0
  51. package/agents/structure-checker/validate-structure.mjs +477 -0
  52. package/agents/update-image/analyze-feedback.yaml +37 -0
  53. package/agents/update-image/index.yaml +78 -0
  54. package/agents/update-image/load-existing-image.mjs +211 -0
  55. package/agents/update-image/prompts/analyze-feedback-system.md +43 -0
  56. package/agents/update-image/prompts/analyze-feedback-user.md +15 -0
  57. package/aigne.yaml +26 -139
  58. package/package.json +16 -48
  59. package/scripts/README.md +90 -0
  60. package/scripts/install.sh +86 -0
  61. package/scripts/uninstall.sh +52 -0
  62. package/skills/doc-smith/SKILL.md +285 -0
  63. package/skills/doc-smith/ai/intent/sources-improve.md +290 -0
  64. package/skills/doc-smith/references/changeset-guide.md +171 -0
  65. package/skills/doc-smith/references/document-content-guide.md +214 -0
  66. package/skills/doc-smith/references/document-structure-schema.md +138 -0
  67. package/skills/doc-smith/references/patch-guide.md +96 -0
  68. package/skills/doc-smith/references/structure-confirmation-guide.md +133 -0
  69. package/skills/doc-smith/references/structure-planning-guide.md +149 -0
  70. package/skills/doc-smith/references/update-workflow.md +108 -0
  71. package/skills/doc-smith/references/user-intent-guide.md +175 -0
  72. package/skills/doc-smith/references/workspace-initialization.md +376 -0
  73. package/skills/doc-smith-docs-detail/SKILL.md +356 -0
  74. package/skills/doc-smith-docs-detail/ai/intent.md +271 -0
  75. package/skills-entry/doc-smith/ai/intent.md +260 -0
  76. package/skills-entry/doc-smith/index.mjs +66 -0
  77. package/skills-entry/doc-smith/prompt.md +57 -0
  78. package/skills-entry/doc-smith/utils.mjs +27 -0
  79. package/skills-entry/doc-smith-docs-detail/batch.yaml +56 -0
  80. package/skills-entry/doc-smith-docs-detail/index.mjs +95 -0
  81. package/skills-entry/doc-smith-docs-detail/prompt.md +64 -0
  82. package/utils/afs-factory.mjs +183 -0
  83. package/utils/agent-constants.mjs +97 -0
  84. package/utils/{auth-utils.mjs → auth.mjs} +6 -9
  85. package/{agents/utils/update-branding.mjs → utils/branding.mjs} +3 -4
  86. package/utils/config.mjs +261 -0
  87. package/utils/constants.mjs +32 -0
  88. package/utils/deploy.mjs +3 -3
  89. package/utils/docs-converter.mjs +454 -0
  90. package/utils/docs.mjs +212 -0
  91. package/utils/document-paths.mjs +172 -0
  92. package/utils/files.mjs +74 -0
  93. package/utils/git.mjs +65 -0
  94. package/utils/{blocklet.mjs → http.mjs} +18 -0
  95. package/utils/image-slots.mjs +57 -0
  96. package/utils/image-utils.mjs +114 -0
  97. package/utils/project.mjs +95 -0
  98. package/utils/sources-path-resolver.mjs +76 -0
  99. package/utils/{upload-files.mjs → upload.mjs} +3 -3
  100. package/utils/workspace.mjs +371 -0
  101. package/agents/chat/chat-system.md +0 -38
  102. package/agents/chat/index.mjs +0 -59
  103. package/agents/chat/skills/generate-document.yaml +0 -15
  104. package/agents/chat/skills/list-documents.mjs +0 -15
  105. package/agents/chat/skills/update-document.yaml +0 -24
  106. package/agents/clear/clear-document-config.mjs +0 -36
  107. package/agents/clear/clear-document-structure.mjs +0 -102
  108. package/agents/clear/clear-generated-docs.mjs +0 -142
  109. package/agents/clear/clear-media-description.mjs +0 -129
  110. package/agents/create/aggregate-document-structure.mjs +0 -21
  111. package/agents/create/analyze-diagram-type-llm.yaml +0 -159
  112. package/agents/create/analyze-diagram-type.mjs +0 -455
  113. package/agents/create/check-document-structure.yaml +0 -30
  114. package/agents/create/check-need-generate-structure.mjs +0 -138
  115. package/agents/create/document-structure-tools/add-document.mjs +0 -85
  116. package/agents/create/document-structure-tools/delete-document.mjs +0 -116
  117. package/agents/create/document-structure-tools/move-document.mjs +0 -109
  118. package/agents/create/document-structure-tools/update-document.mjs +0 -84
  119. package/agents/create/generate-diagram-image.yaml +0 -91
  120. package/agents/create/generate-structure.yaml +0 -106
  121. package/agents/create/index.yaml +0 -45
  122. package/agents/create/refine-document-structure.yaml +0 -12
  123. package/agents/create/replace-d2-with-image.mjs +0 -610
  124. package/agents/create/update-document-structure.yaml +0 -54
  125. package/agents/create/user-add-document/add-documents-to-structure.mjs +0 -90
  126. package/agents/create/user-add-document/find-documents-to-add-links.yaml +0 -47
  127. package/agents/create/user-add-document/index.yaml +0 -46
  128. package/agents/create/user-add-document/prepare-documents-to-translate.mjs +0 -22
  129. package/agents/create/user-add-document/print-add-document-summary.mjs +0 -63
  130. package/agents/create/user-add-document/review-documents-with-new-links.mjs +0 -110
  131. package/agents/create/user-remove-document/find-documents-with-invalid-links.mjs +0 -78
  132. package/agents/create/user-remove-document/index.yaml +0 -40
  133. package/agents/create/user-remove-document/prepare-documents-to-translate.mjs +0 -22
  134. package/agents/create/user-remove-document/print-remove-document-summary.mjs +0 -53
  135. package/agents/create/user-remove-document/remove-documents-from-structure.mjs +0 -99
  136. package/agents/create/user-remove-document/review-documents-with-invalid-links.mjs +0 -115
  137. package/agents/create/user-review-document-structure.mjs +0 -139
  138. package/agents/create/utils/init-current-content.mjs +0 -34
  139. package/agents/create/utils/merge-document-structures.mjs +0 -36
  140. package/agents/evaluate/code-snippet.mjs +0 -97
  141. package/agents/evaluate/document-structure.yaml +0 -67
  142. package/agents/evaluate/document.yaml +0 -82
  143. package/agents/evaluate/generate-report.mjs +0 -85
  144. package/agents/evaluate/index.yaml +0 -46
  145. package/agents/history/index.yaml +0 -6
  146. package/agents/history/view.mjs +0 -78
  147. package/agents/init/check.mjs +0 -16
  148. package/agents/init/index.mjs +0 -643
  149. package/agents/init/validate.mjs +0 -16
  150. package/agents/localize/choose-language.mjs +0 -107
  151. package/agents/localize/record-translation-history.mjs +0 -23
  152. package/agents/localize/save-doc-translation-or-skip.mjs +0 -18
  153. package/agents/localize/set-review-content.mjs +0 -58
  154. package/agents/localize/translate-document-wrapper.mjs +0 -34
  155. package/agents/localize/translate-document.yaml +0 -24
  156. package/agents/localize/translate-multilingual.yaml +0 -57
  157. package/agents/localize/translate-or-skip-diagram.mjs +0 -52
  158. package/agents/media/batch-generate-media-description.yaml +0 -46
  159. package/agents/media/generate-media-description.yaml +0 -50
  160. package/agents/media/load-media-description.mjs +0 -454
  161. package/agents/prefs/index.mjs +0 -203
  162. package/agents/schema/document-structure-item.yaml +0 -26
  163. package/agents/schema/document-structure-refine-item.yaml +0 -23
  164. package/agents/schema/document-structure.yaml +0 -29
  165. package/agents/update/batch-generate-document.yaml +0 -27
  166. package/agents/update/batch-update-document.yaml +0 -7
  167. package/agents/update/check-diagram-flag.mjs +0 -116
  168. package/agents/update/check-document.mjs +0 -162
  169. package/agents/update/check-generate-diagram.mjs +0 -106
  170. package/agents/update/check-update-is-single.mjs +0 -53
  171. package/agents/update/document-tools/update-document-content.mjs +0 -303
  172. package/agents/update/generate-diagram.yaml +0 -80
  173. package/agents/update/generate-document.yaml +0 -70
  174. package/agents/update/handle-document-update.yaml +0 -103
  175. package/agents/update/index.yaml +0 -69
  176. package/agents/update/pre-check-generate-diagram.yaml +0 -44
  177. package/agents/update/save-and-translate-document.mjs +0 -80
  178. package/agents/update/update-document-detail.yaml +0 -71
  179. package/agents/update/update-single/update-single-document-detail.mjs +0 -322
  180. package/agents/update/update-single-document.yaml +0 -7
  181. package/agents/update/user-review-document.mjs +0 -272
  182. package/agents/utils/action-success.mjs +0 -16
  183. package/agents/utils/analyze-document-feedback-intent.yaml +0 -32
  184. package/agents/utils/analyze-feedback-intent.mjs +0 -253
  185. package/agents/utils/analyze-structure-feedback-intent.yaml +0 -29
  186. package/agents/utils/check-detail-result.mjs +0 -51
  187. package/agents/utils/check-feedback-refiner.mjs +0 -81
  188. package/agents/utils/choose-docs.mjs +0 -251
  189. package/agents/utils/document-icon-generate.yaml +0 -52
  190. package/agents/utils/document-title-streamline.yaml +0 -48
  191. package/agents/utils/ensure-document-icons.mjs +0 -129
  192. package/agents/utils/exit.mjs +0 -6
  193. package/agents/utils/feedback-refiner.yaml +0 -50
  194. package/agents/utils/find-item-by-path.mjs +0 -114
  195. package/agents/utils/find-user-preferences-by-path.mjs +0 -37
  196. package/agents/utils/format-document-structure.mjs +0 -35
  197. package/agents/utils/generate-document-or-skip.mjs +0 -41
  198. package/agents/utils/handle-diagram-operations.mjs +0 -263
  199. package/agents/utils/load-all-document-content.mjs +0 -30
  200. package/agents/utils/load-document-all-content.mjs +0 -96
  201. package/agents/utils/load-sources.mjs +0 -405
  202. package/agents/utils/map-reasoning-effort-level.mjs +0 -15
  203. package/agents/utils/post-generate.mjs +0 -133
  204. package/agents/utils/read-current-document-content.mjs +0 -46
  205. package/agents/utils/save-doc-translation.mjs +0 -30
  206. package/agents/utils/save-doc.mjs +0 -54
  207. package/agents/utils/save-output.mjs +0 -26
  208. package/agents/utils/save-sidebar.mjs +0 -38
  209. package/agents/utils/skip-if-content-exists.mjs +0 -27
  210. package/agents/utils/streamline-document-titles-if-needed.mjs +0 -88
  211. package/agents/utils/transform-detail-data-sources.mjs +0 -45
  212. package/assets/report-template/report.html +0 -198
  213. package/docs-mcp/analyze-content-relevance.yaml +0 -50
  214. package/docs-mcp/analyze-docs-relevance.yaml +0 -59
  215. package/docs-mcp/docs-search.yaml +0 -42
  216. package/docs-mcp/get-docs-detail.mjs +0 -41
  217. package/docs-mcp/get-docs-structure.mjs +0 -16
  218. package/docs-mcp/read-doc-content.mjs +0 -119
  219. package/prompts/common/document/content-rules-core.md +0 -20
  220. package/prompts/common/document/markdown-syntax-rules.md +0 -65
  221. package/prompts/common/document/media-file-list-usage-rules.md +0 -18
  222. package/prompts/common/document/openapi-usage-rules.md +0 -189
  223. package/prompts/common/document/role-and-personality.md +0 -16
  224. package/prompts/common/document/user-preferences.md +0 -9
  225. package/prompts/common/document-structure/conflict-resolution-guidance.md +0 -16
  226. package/prompts/common/document-structure/document-icon-generate.md +0 -116
  227. package/prompts/common/document-structure/document-structure-rules.md +0 -43
  228. package/prompts/common/document-structure/document-title-streamline.md +0 -86
  229. package/prompts/common/document-structure/glossary.md +0 -7
  230. package/prompts/common/document-structure/intj-traits.md +0 -5
  231. package/prompts/common/document-structure/openapi-usage-rules.md +0 -28
  232. package/prompts/common/document-structure/output-constraints.md +0 -18
  233. package/prompts/common/document-structure/user-locale-rules.md +0 -10
  234. package/prompts/common/document-structure/user-preferences.md +0 -9
  235. package/prompts/detail/custom/admonition-usage-rules.md +0 -94
  236. package/prompts/detail/custom/code-block-usage-rules.md +0 -163
  237. package/prompts/detail/custom/custom-components/x-card-usage-rules.md +0 -63
  238. package/prompts/detail/custom/custom-components/x-cards-usage-rules.md +0 -83
  239. package/prompts/detail/custom/custom-components/x-field-desc-usage-rules.md +0 -120
  240. package/prompts/detail/custom/custom-components/x-field-group-usage-rules.md +0 -80
  241. package/prompts/detail/custom/custom-components/x-field-usage-rules.md +0 -189
  242. package/prompts/detail/custom/custom-components-usage-rules.md +0 -18
  243. package/prompts/detail/diagram/generate-image-user.md +0 -81
  244. package/prompts/detail/diagram/guide.md +0 -29
  245. package/prompts/detail/diagram/official-examples.md +0 -712
  246. package/prompts/detail/diagram/pre-check.md +0 -23
  247. package/prompts/detail/diagram/role-and-personality.md +0 -2
  248. package/prompts/detail/diagram/rules.md +0 -46
  249. package/prompts/detail/diagram/system-prompt.md +0 -1139
  250. package/prompts/detail/diagram/user-prompt.md +0 -43
  251. package/prompts/detail/generate/detail-example.md +0 -457
  252. package/prompts/detail/generate/document-rules.md +0 -45
  253. package/prompts/detail/generate/system-prompt.md +0 -61
  254. package/prompts/detail/generate/user-prompt.md +0 -99
  255. package/prompts/detail/jsx/rules.md +0 -6
  256. package/prompts/detail/update/system-prompt.md +0 -121
  257. package/prompts/detail/update/user-prompt.md +0 -41
  258. package/prompts/evaluate/document-structure.md +0 -93
  259. package/prompts/evaluate/document.md +0 -149
  260. package/prompts/media/media-description/system-prompt.md +0 -43
  261. package/prompts/media/media-description/user-prompt.md +0 -17
  262. package/prompts/structure/check-document-structure.md +0 -93
  263. package/prompts/structure/document-rules.md +0 -21
  264. package/prompts/structure/find-documents-to-add-links.md +0 -52
  265. package/prompts/structure/generate/system-prompt.md +0 -13
  266. package/prompts/structure/generate/user-prompt.md +0 -137
  267. package/prompts/structure/review/structure-review-system.md +0 -81
  268. package/prompts/structure/structure-example.md +0 -89
  269. package/prompts/structure/structure-getting-started.md +0 -10
  270. package/prompts/structure/update/system-prompt.md +0 -93
  271. package/prompts/structure/update/user-prompt.md +0 -43
  272. package/prompts/translate/admonition.md +0 -20
  273. package/prompts/translate/code-block.md +0 -33
  274. package/prompts/utils/analyze-document-feedback-intent.md +0 -54
  275. package/prompts/utils/analyze-structure-feedback-intent.md +0 -43
  276. package/prompts/utils/feedback-refiner.md +0 -105
  277. package/types/document-schema.mjs +0 -55
  278. package/types/document-structure-schema.mjs +0 -261
  279. package/utils/check-document-has-diagram.mjs +0 -95
  280. package/utils/conflict-detector.mjs +0 -149
  281. package/utils/constants/index.mjs +0 -620
  282. package/utils/constants/linter.mjs +0 -102
  283. package/utils/d2-utils.mjs +0 -205
  284. package/utils/debug.mjs +0 -3
  285. package/utils/delete-diagram-images.mjs +0 -99
  286. package/utils/diagram-version-utils.mjs +0 -14
  287. package/utils/docs-finder-utils.mjs +0 -548
  288. package/utils/evaluate/report-utils.mjs +0 -132
  289. package/utils/extract-api.mjs +0 -32
  290. package/utils/file-utils.mjs +0 -960
  291. package/utils/history-utils.mjs +0 -203
  292. package/utils/icon-map.mjs +0 -26
  293. package/utils/image-compress.mjs +0 -154
  294. package/utils/kroki-utils.mjs +0 -173
  295. package/utils/linter/index.mjs +0 -50
  296. package/utils/load-config.mjs +0 -78
  297. package/utils/markdown/index.mjs +0 -26
  298. package/utils/markdown-checker.mjs +0 -694
  299. package/utils/mermaid-validator.mjs +0 -140
  300. package/utils/mermaid-worker-pool.mjs +0 -250
  301. package/utils/mermaid-worker.mjs +0 -233
  302. package/utils/openapi/index.mjs +0 -28
  303. package/utils/preferences-utils.mjs +0 -175
  304. package/utils/request.mjs +0 -10
  305. package/utils/sync-diagram-to-translations.mjs +0 -272
  306. package/utils/translate-diagram-images.mjs +0 -807
  307. package/utils/utils.mjs +0 -1354
  308. /package/{prompts/translate → agents/localize/prompts}/glossary.md +0 -0
@@ -0,0 +1,172 @@
1
+ import { readFile, access } from "node:fs/promises";
2
+ import { constants } from "node:fs";
3
+ import { parse as yamlParse } from "yaml";
4
+ import { PATHS, ERROR_CODES } from "./agent-constants.mjs";
5
+
6
+ /**
7
+ * Normalize path
8
+ * @param {string} rawPath - Raw path (may or may not have leading slash)
9
+ * @returns {Object} - { filePath: path without slash, displayPath: path with slash }
10
+ */
11
+ export function normalizePath(rawPath) {
12
+ if (!rawPath || typeof rawPath !== "string") {
13
+ throw new Error("Path must be a non-empty string");
14
+ }
15
+
16
+ let normalized = rawPath.trim();
17
+
18
+ // Remove leading slash
19
+ if (normalized.startsWith("/")) {
20
+ normalized = normalized.slice(1);
21
+ }
22
+
23
+ // Remove trailing slash
24
+ if (normalized.endsWith("/")) {
25
+ normalized = normalized.slice(0, -1);
26
+ }
27
+
28
+ return {
29
+ filePath: normalized, // "overview" or "api/authentication"
30
+ displayPath: `/${normalized}`, // "/overview" or "/api/authentication"
31
+ };
32
+ }
33
+
34
+ /**
35
+ * Recursively collect document paths
36
+ * @param {Array} docs - Document array
37
+ * @param {Object} options - Collection options
38
+ * @param {boolean} options.includeBothFormats - Whether to include both formats with and without leading slash
39
+ * @param {boolean} options.collectMetadata - Whether to collect additional metadata
40
+ * @returns {Set|Array} - Path set or path object array
41
+ */
42
+ export function collectDocumentPaths(docs, options = {}) {
43
+ const { includeBothFormats = false, collectMetadata = false } = options;
44
+
45
+ const paths = collectMetadata ? [] : new Set();
46
+
47
+ function collect(documents) {
48
+ for (const doc of documents) {
49
+ if (doc.path) {
50
+ // Normalize path
51
+ const normalized = doc.path.startsWith("/") ? doc.path.slice(1) : doc.path;
52
+
53
+ if (collectMetadata) {
54
+ // Collect path and metadata
55
+ paths.push({
56
+ path: normalized,
57
+ displayPath: `/${normalized}`,
58
+ title: doc.title || "",
59
+ description: doc.description || "",
60
+ });
61
+ } else {
62
+ // Only collect paths
63
+ paths.add(normalized);
64
+ if (includeBothFormats) {
65
+ paths.add(`/${normalized}`);
66
+ }
67
+ }
68
+ }
69
+
70
+ // Recursively process child documents
71
+ if (doc.children && Array.isArray(doc.children)) {
72
+ collect(doc.children);
73
+ }
74
+ }
75
+ }
76
+
77
+ collect(docs);
78
+ return paths;
79
+ }
80
+
81
+ /**
82
+ * Load all paths from document structure
83
+ * @param {Object} options - Loading options
84
+ * @param {string} options.yamlPath - YAML file path
85
+ * @param {boolean} options.includeBothFormats - Whether to include both formats with and without leading slash
86
+ * @param {boolean} options.collectMetadata - Whether to collect additional metadata
87
+ * @param {boolean} options.throwOnInvalid - Whether to throw error when document format is invalid
88
+ * @returns {Promise<Set|Array>} - Set of all valid paths or path object array
89
+ */
90
+ export async function loadDocumentPaths(options = {}) {
91
+ const {
92
+ yamlPath = PATHS.DOCUMENT_STRUCTURE,
93
+ includeBothFormats = false,
94
+ collectMetadata = false,
95
+ throwOnInvalid = true,
96
+ } = options;
97
+
98
+ // Check if file exists
99
+ try {
100
+ await access(yamlPath, constants.F_OK | constants.R_OK);
101
+ } catch (_error) {
102
+ throw new Error(ERROR_CODES.MISSING_STRUCTURE_FILE);
103
+ }
104
+
105
+ // Read and parse YAML
106
+ const content = await readFile(yamlPath, "utf8");
107
+ const data = yamlParse(content);
108
+
109
+ if (!data.documents || !Array.isArray(data.documents)) {
110
+ if (throwOnInvalid) {
111
+ throw new Error(ERROR_CODES.INVALID_STRUCTURE_FILE);
112
+ } else {
113
+ throw new Error(ERROR_CODES.MISSING_STRUCTURE_FILE);
114
+ }
115
+ }
116
+
117
+ // Recursively collect all paths
118
+ return collectDocumentPaths(data.documents, {
119
+ includeBothFormats,
120
+ collectMetadata,
121
+ });
122
+ }
123
+
124
+ /**
125
+ * Validate whether a path exists in the document structure
126
+ * @param {string} path - The path to validate
127
+ * @param {Set|Array} validPaths - Set of valid paths
128
+ * @returns {boolean} - Whether the path is valid
129
+ */
130
+ export function isValidDocumentPath(path, validPaths) {
131
+ if (!path) return false;
132
+
133
+ const { filePath, displayPath } = normalizePath(path);
134
+
135
+ if (validPaths instanceof Set) {
136
+ return validPaths.has(filePath) || validPaths.has(displayPath);
137
+ }
138
+
139
+ if (Array.isArray(validPaths)) {
140
+ return validPaths.some((p) => {
141
+ const pathValue = typeof p === "string" ? p : p.path;
142
+ return pathValue === filePath || pathValue === displayPath;
143
+ });
144
+ }
145
+
146
+ return false;
147
+ }
148
+
149
+ /**
150
+ * Filter valid paths from a path array
151
+ * @param {string[]} paths - Array of paths to validate
152
+ * @param {Set|Array} validPaths - Set of valid paths
153
+ * @returns {Object} - { validPaths: [], invalidPaths: [] }
154
+ */
155
+ export function filterValidPaths(paths, validPaths) {
156
+ const result = {
157
+ validPaths: [],
158
+ invalidPaths: [],
159
+ };
160
+
161
+ for (const path of paths) {
162
+ const { filePath } = normalizePath(path);
163
+
164
+ if (isValidDocumentPath(path, validPaths)) {
165
+ result.validPaths.push(filePath);
166
+ } else {
167
+ result.invalidPaths.push(path);
168
+ }
169
+ }
170
+
171
+ return result;
172
+ }
@@ -0,0 +1,74 @@
1
+ import { existsSync, mkdirSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { PATHS } from "./agent-constants.mjs";
4
+
5
+ // Shared extension → MIME type mapping table
6
+ const EXT_TO_MIME = {
7
+ ".jpg": "image/jpeg",
8
+ ".jpeg": "image/jpeg",
9
+ ".png": "image/png",
10
+ ".gif": "image/gif",
11
+ ".bmp": "image/bmp",
12
+ ".webp": "image/webp",
13
+ ".svg": "image/svg+xml",
14
+ ".heic": "image/heic",
15
+ ".heif": "image/heif",
16
+ ".mp4": "video/mp4",
17
+ ".mpeg": "video/mpeg",
18
+ ".mpg": "video/mpg",
19
+ ".mov": "video/mov",
20
+ ".avi": "video/avi",
21
+ ".flv": "video/x-flv",
22
+ ".mkv": "video/x-matroska",
23
+ ".webm": "video/webm",
24
+ ".wmv": "video/wmv",
25
+ ".m4v": "video/x-m4v",
26
+ ".3gpp": "video/3gpp",
27
+ ".mp3": "audio/mpeg",
28
+ ".wav": "audio/wav",
29
+ ".pdf": "application/pdf",
30
+ ".doc": "application/msword",
31
+ ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
32
+ ".xls": "application/vnd.ms-excel",
33
+ ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
34
+ ".ppt": "application/vnd.ms-powerpoint",
35
+ ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
36
+ ".txt": "text/plain",
37
+ ".json": "application/json",
38
+ ".xml": "application/xml",
39
+ ".html": "text/html",
40
+ ".css": "text/css",
41
+ ".js": "application/javascript",
42
+ ".zip": "application/zip",
43
+ ".rar": "application/x-rar-compressed",
44
+ ".7z": "application/x-7z-compressed",
45
+ };
46
+
47
+ /**
48
+ * Get MIME type from file path based on extension
49
+ * @param {string} filePath - File path
50
+ * @returns {string} MIME type
51
+ */
52
+ export function getMimeType(filePath) {
53
+ const ext = path.extname(filePath || "").toLowerCase();
54
+ return EXT_TO_MIME[ext] || "application/octet-stream";
55
+ }
56
+
57
+ /**
58
+ * Ensure temporary directory exists
59
+ * @returns {Promise<void>}
60
+ */
61
+ export async function ensureTmpDir() {
62
+ if (!existsSync(PATHS.TMP_DIR)) {
63
+ mkdirSync(PATHS.TMP_DIR, { recursive: true });
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Check if a file is a remote URL
69
+ * @param {string} file - File path or URL
70
+ * @returns {boolean}
71
+ */
72
+ export function isRemoteFile(file) {
73
+ return file && (file.startsWith("http://") || file.startsWith("https://"));
74
+ }
package/utils/git.mjs ADDED
@@ -0,0 +1,65 @@
1
+ import { execSync } from "node:child_process";
2
+
3
+ /**
4
+ * Validate if a URL is a valid GitHub repository URL
5
+ * @param {string} url - The URL to validate
6
+ * @returns {boolean} - True if valid GitHub URL
7
+ */
8
+ export function isValidGithubUrl(url) {
9
+ if (!url || typeof url !== "string") return false;
10
+ // Match both HTTPS and SSH GitHub URLs
11
+ return /^(https:\/\/github\.com\/|git@github\.com:)[^/]+\/[^/]+/.test(url);
12
+ }
13
+
14
+ /**
15
+ * Get GitHub repository URL
16
+ * @returns {string} - GitHub repository URL or empty string
17
+ */
18
+ export function getGithubRepoUrl() {
19
+ try {
20
+ const gitRemote = execSync("git remote get-url origin", {
21
+ encoding: "utf8",
22
+ stdio: ["pipe", "pipe", "ignore"],
23
+ }).trim();
24
+
25
+ if (isValidGithubUrl(gitRemote)) {
26
+ return gitRemote;
27
+ }
28
+
29
+ return "";
30
+ } catch {
31
+ return "";
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Get GitHub repository information
37
+ * @param {string} repoUrl - The repository URL
38
+ * @returns {Promise<Object>} - Repository information
39
+ */
40
+ export async function getGitHubRepoInfo(repoUrl) {
41
+ try {
42
+ const match = repoUrl.match(/github\.com[/:]([^/]+)\/([^/]+?)(?:\.git)?$/);
43
+ if (!match) return null;
44
+
45
+ const [, owner, repo] = match;
46
+ const apiUrl = `https://api.github.com/repos/${owner}/${repo}`;
47
+
48
+ const response = await fetch(apiUrl);
49
+
50
+ if (!response.ok) {
51
+ console.warn("Failed to fetch GitHub repository info:", repoUrl, response.statusText);
52
+ return null;
53
+ }
54
+
55
+ const data = await response.json();
56
+ return {
57
+ name: data.name,
58
+ description: data.description || "",
59
+ icon: data.owner?.avatar_url || "",
60
+ };
61
+ } catch (error) {
62
+ console.warn("Failed to fetch GitHub repository info:", error.message);
63
+ return null;
64
+ }
65
+ }
@@ -102,3 +102,21 @@ export async function getComponentInfoWithMountPoint(appUrl, did) {
102
102
  mountPoint: component.mountPoint,
103
103
  };
104
104
  }
105
+
106
+ /**
107
+ * Perform HTTP request with authentication token
108
+ * @param {string} url - Request URL
109
+ * @param {Object} options - Fetch options
110
+ * @param {string} authToken - Authentication token
111
+ * @returns {Promise<Object>} Response JSON
112
+ */
113
+ export async function requestWithAuthToken(url, options, authToken) {
114
+ if (!authToken) {
115
+ console.error("No authentication token provided");
116
+ }
117
+ const response = await fetch(url, {
118
+ ...options,
119
+ headers: { ...options.headers, Authorization: `Bearer ${authToken}` },
120
+ });
121
+ return response.json();
122
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * AFS Image Slot utility functions
3
+ * For parsing and processing image slots in documents
4
+ */
5
+
6
+ /**
7
+ * Slot regex (for replacement operations)
8
+ * Supports the following formats:
9
+ * - <!-- afs:image id="..." key="..." desc="..." -->
10
+ * - <!-- afs:image id="..." desc="..." -->
11
+ * - <!-- afs:image id=\"...\" key=\"...\" desc=\"...\" -->
12
+ * - <!-- afs:image id=\"...\" desc=\"...\" -->
13
+ */
14
+ export const SLOT_REGEX =
15
+ /<!--\s*afs:image\s+id=\\?"([^\\"]+)\\?"(?:\s+key=\\?"([^\\"]+)\\?")?\s+desc=\\?"([^\\"]+)\\?"\s*-->/g;
16
+
17
+ /**
18
+ * Generate key (if slot doesn't provide one)
19
+ * @param {string} docPath - Document path (e.g., "/overview")
20
+ * @param {string} id - slot id
21
+ * @returns {string} - Generated key
22
+ */
23
+ export function generateKey(docPath, id) {
24
+ // Remove leading /
25
+ const normalizedPath = docPath.startsWith("/") ? docPath.slice(1) : docPath;
26
+ // Replace / with -
27
+ const pathPart = normalizedPath.replace(/\//g, "-");
28
+ return `${pathPart}-${id}`;
29
+ }
30
+
31
+ /**
32
+ * Parse AFS image slots from document
33
+ * @param {string} content - Document content
34
+ * @param {string} docPath - Document path (used for generating key)
35
+ * @returns {Array<{id: string, key: string, desc: string, raw: string}>} - Slot array
36
+ */
37
+ export function parseSlots(content, docPath) {
38
+ // Slot format: <!-- afs:image id="..." key="..." desc="..." -->
39
+ // key is optional
40
+ const slotRegex = SLOT_REGEX;
41
+
42
+ const slots = [];
43
+
44
+ for (const match of content.matchAll(slotRegex)) {
45
+ const id = match[1];
46
+ const userKey = match[2]; // May be undefined
47
+ const desc = match[3];
48
+ const raw = match[0]; // Complete slot string
49
+
50
+ // Auto-generate key if user didn't provide one
51
+ const key = userKey || generateKey(docPath, id);
52
+
53
+ slots.push({ id, key, desc, raw });
54
+ }
55
+
56
+ return slots;
57
+ }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Common utility functions for image processing
3
+ */
4
+
5
+ import { readFile } from "node:fs/promises";
6
+ import { createHash } from "node:crypto";
7
+ import { join } from "node:path";
8
+ import fs from "fs-extra";
9
+
10
+ /**
11
+ * Calculate SHA256 hash of a file
12
+ * @param {string} filePath - File path
13
+ * @returns {Promise<string>} - SHA256 hash (hex)
14
+ */
15
+ export async function calculateFileHash(filePath) {
16
+ const content = await readFile(filePath);
17
+ return createHash("sha256").update(content).digest("hex");
18
+ }
19
+
20
+ /**
21
+ * Calculate SHA256 hash of string content
22
+ * @param {string} content - String content
23
+ * @returns {string} - SHA256 hash (hex)
24
+ */
25
+ export function calculateContentHash(content) {
26
+ return createHash("sha256").update(content, "utf8").digest("hex");
27
+ }
28
+
29
+ /**
30
+ * Supported image extensions
31
+ */
32
+ export const IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png", ".gif", ".webp"];
33
+
34
+ /**
35
+ * Find image file (supports multiple extensions)
36
+ * @param {string} imagesDir - Image directory path
37
+ * @param {string} locale - Language code
38
+ * @param {string[]} extensions - Supported extension list (optional, defaults to IMAGE_EXTENSIONS)
39
+ * @returns {Promise<string|null>} - Image file path, returns null if not found
40
+ */
41
+ export async function findImageFile(imagesDir, locale, extensions = IMAGE_EXTENSIONS) {
42
+ for (const ext of extensions) {
43
+ const imagePath = join(imagesDir, `${locale}${ext}`);
44
+ if (await fs.pathExists(imagePath)) {
45
+ return imagePath;
46
+ }
47
+ }
48
+ return null;
49
+ }
50
+
51
+ /**
52
+ * Find image file (with language fallback)
53
+ * @param {string} key - Image key
54
+ * @param {string} locale - Current language code
55
+ * @param {string} mainLocale - Primary language code (for fallback)
56
+ * @param {string} assetsDir - Assets directory path
57
+ * @returns {Promise<string|null>} - Image relative path (relative to assets), returns null if not found
58
+ */
59
+ export async function findImageWithFallback(key, locale, mainLocale, assetsDir = "./assets") {
60
+ const keyDir = join(assetsDir, key, "images");
61
+
62
+ // 1. Try to find image for current language
63
+ const currentLocaleImage = await findImageFile(keyDir, locale);
64
+ if (currentLocaleImage) {
65
+ // Return path relative to assets
66
+ const filename = currentLocaleImage.split("/").pop();
67
+ return join(key, "images", filename);
68
+ }
69
+
70
+ // 2. If current language doesn't exist, fall back to primary language
71
+ if (mainLocale && locale !== mainLocale) {
72
+ const mainLocaleImage = await findImageFile(keyDir, mainLocale);
73
+ if (mainLocaleImage) {
74
+ const filename = mainLocaleImage.split("/").pop();
75
+ return join(key, "images", filename);
76
+ }
77
+ }
78
+
79
+ // 3. Image doesn't exist
80
+ return null;
81
+ }
82
+
83
+ /**
84
+ * Get image MIME type
85
+ * @param {string} filePath - Image file path
86
+ * @returns {string} - MIME type
87
+ */
88
+ export function getImageMimeType(filePath) {
89
+ const ext = filePath.toLowerCase().split(".").pop();
90
+ const mimeTypes = {
91
+ jpg: "image/jpeg",
92
+ jpeg: "image/jpeg",
93
+ png: "image/png",
94
+ gif: "image/gif",
95
+ webp: "image/webp",
96
+ };
97
+ return mimeTypes[ext] || "image/jpeg";
98
+ }
99
+
100
+ /**
101
+ * Get file extension from MIME type
102
+ * @param {string} mimeType - MIME type
103
+ * @returns {string} - File extension
104
+ */
105
+ export function getExtensionFromMimeType(mimeType) {
106
+ const mimeToExt = {
107
+ "image/jpeg": "jpg",
108
+ "image/jpg": "jpg",
109
+ "image/png": "png",
110
+ "image/gif": "gif",
111
+ "image/webp": "webp",
112
+ };
113
+ return mimeToExt[mimeType] || "png";
114
+ }
@@ -0,0 +1,95 @@
1
+ import { execSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { SUPPORTED_LANGUAGES } from "./constants.mjs";
5
+ import { getGitHubRepoInfo, isValidGithubUrl } from "./git.mjs";
6
+
7
+ /**
8
+ * Get project information from Git or directory
9
+ * @returns {Promise<Object>} - Project information
10
+ */
11
+ export async function getProjectInfo() {
12
+ let repoInfo = null;
13
+ let defaultName = path.basename(process.cwd());
14
+ let defaultDescription = "";
15
+ let defaultIcon = "";
16
+ let fromGitHub = false;
17
+ let sourceDir = null;
18
+
19
+ try {
20
+ // Get the first folder in sources directory
21
+ const sourcesPath = path.join(process.cwd(), "sources");
22
+
23
+ if (fs.existsSync(sourcesPath)) {
24
+ const entries = fs.readdirSync(sourcesPath, { withFileTypes: true });
25
+ const firstFolder = entries.find((entry) => entry.isDirectory());
26
+
27
+ if (firstFolder) {
28
+ sourceDir = path.join(sourcesPath, firstFolder.name);
29
+ defaultName = firstFolder.name;
30
+ }
31
+ }
32
+
33
+ // If no source directory found, fall back to current directory
34
+ const targetDir = sourceDir || process.cwd();
35
+
36
+ const gitRemote = execSync("git remote get-url origin", {
37
+ encoding: "utf8",
38
+ cwd: targetDir,
39
+ stdio: ["pipe", "pipe", "ignore"],
40
+ }).trim();
41
+
42
+ const repoName = gitRemote.split("/").pop().replace(".git", "");
43
+ defaultName = repoName;
44
+
45
+ if (isValidGithubUrl(gitRemote)) {
46
+ repoInfo = await getGitHubRepoInfo(gitRemote);
47
+ if (repoInfo) {
48
+ defaultDescription = repoInfo.description;
49
+ defaultIcon = repoInfo.icon;
50
+ fromGitHub = true;
51
+ }
52
+ }
53
+ } catch (_error) {
54
+ console.warn("No git repository found, using current directory name");
55
+ }
56
+
57
+ return {
58
+ name: defaultName,
59
+ description: defaultDescription,
60
+ icon: defaultIcon,
61
+ fromGitHub,
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Detect system language
67
+ * @returns {string} - Language code (e.g., 'en', 'zh')
68
+ */
69
+ export function detectSystemLanguage() {
70
+ try {
71
+ let systemLocale = null;
72
+
73
+ systemLocale = process.env.LANG || process.env.LANGUAGE || process.env.LC_ALL;
74
+
75
+ if (!systemLocale) {
76
+ try {
77
+ systemLocale = Intl.DateTimeFormat().resolvedOptions().locale;
78
+ } catch (_error) {
79
+ // Intl API failed
80
+ }
81
+ }
82
+
83
+ if (!systemLocale) {
84
+ return "en";
85
+ }
86
+
87
+ const languageCode = systemLocale.split(/[_.-]/)[0].toLowerCase();
88
+ const supportedCode = SUPPORTED_LANGUAGES.find((lang) => lang.code === languageCode);
89
+
90
+ return supportedCode ? supportedCode.code : "en";
91
+ } catch (error) {
92
+ console.warn("Failed to detect system language:", error.message);
93
+ return "en";
94
+ }
95
+ }
@@ -0,0 +1,76 @@
1
+ import { resolve } from "node:path";
2
+ import fs from "fs-extra";
3
+
4
+ /**
5
+ * Check if path is a /sources/... absolute path
6
+ * @param {string} imagePath - Image path
7
+ * @returns {boolean}
8
+ */
9
+ export function isSourcesAbsolutePath(imagePath) {
10
+ return imagePath.startsWith("/sources/");
11
+ }
12
+
13
+ /**
14
+ * Parse /sources/... absolute path and extract relative path portion
15
+ * @param {string} absolutePath - Absolute path, format: /sources/<relativePath>
16
+ * @returns {string | null} - Relative path, returns null if parsing fails or path is unsafe
17
+ */
18
+ export function parseSourcesPath(absolutePath) {
19
+ // /sources/assets/screenshot.png → assets/screenshot.png
20
+ const match = absolutePath.match(/^\/sources\/(.+)$/);
21
+ if (!match) return null;
22
+
23
+ const relativePath = match[1];
24
+
25
+ // Security check: reject paths containing path traversal sequences
26
+ // Prevent accessing files outside sources directory via paths like /sources/../../../etc/passwd
27
+ if (relativePath.includes("..")) {
28
+ return null;
29
+ }
30
+
31
+ return relativePath;
32
+ }
33
+
34
+ /**
35
+ * Resolve virtual absolute path to physical path based on config.yaml sources configuration
36
+ * Searches each source in order and returns the first existing path
37
+ *
38
+ * Execution layer perspective (after AFS mount):
39
+ * modules/workspace/ and modules/sources/ are at same level
40
+ * Documents reference using /sources/<path> format
41
+ *
42
+ * Physical disk perspective:
43
+ * - local-path: path relative to workspace
44
+ * - git-clone: workspace/sources/<name>/ directory
45
+ *
46
+ * @param {string} absolutePath - Virtual absolute path, format: /sources/<relativePath>
47
+ * @param {Array} sourcesConfig - sources configuration array from config.yaml
48
+ * @param {string} workspaceBase - Workspace physical root directory
49
+ * @returns {Promise<{physicalPath: string, sourceName: string} | null>} - Physical path and source name, returns null if resolution fails
50
+ */
51
+ export async function resolveSourcesPath(absolutePath, sourcesConfig, workspaceBase) {
52
+ const relativePath = parseSourcesPath(absolutePath);
53
+ if (!relativePath) return null;
54
+
55
+ // Search in each source in order
56
+ for (const source of sourcesConfig) {
57
+ let physicalPath;
58
+
59
+ if (source.type === "local-path") {
60
+ // local-path: path relative to workspace
61
+ physicalPath = resolve(workspaceBase, source.path, relativePath);
62
+ } else if (source.type === "git-clone") {
63
+ // git-clone: cloned to workspace/sources/<name>/ directory
64
+ physicalPath = resolve(workspaceBase, "sources", source.name, relativePath);
65
+ } else {
66
+ continue;
67
+ }
68
+
69
+ // Check if file exists
70
+ if (await fs.pathExists(physicalPath)) {
71
+ return { physicalPath, sourceName: source.name };
72
+ }
73
+ }
74
+
75
+ return null;
76
+ }
@@ -5,9 +5,9 @@ import path from "node:path";
5
5
  import pLimit from "p-limit";
6
6
  import pRetry from "p-retry";
7
7
 
8
- import { getComponentMountPoint } from "./blocklet.mjs";
9
- import { DISCUSS_KIT_DID, MEDIA_KIT_DID } from "./constants/index.mjs";
10
- import { getMimeType } from "./file-utils.mjs";
8
+ import { getComponentMountPoint } from "./http.mjs";
9
+ import { DISCUSS_KIT_DID, MEDIA_KIT_DID } from "./constants.mjs";
10
+ import { getMimeType } from "./files.mjs";
11
11
 
12
12
  /**
13
13
  * Perform single file upload