T4: Markitdown adapter——docx/pptx/pdf 轉檔 + 原檔進 assets/originals(design §4)
transform.js:MARKITDOWN_EXT(docx/pptx/pdf)走 markitdown CLI 子行程轉出 md,輸出路徑 副檔名換 .md;回傳 originalCopy 供 index.js 把原檔複製進 assets/originals/<相對路徑>。 git-sync.js:ensureGitAttributes() 啟動時冪等寫入 LFS 宣告(三種格式 filter=lfs)。 已驗(雲端 sandbox,兩輪煙測):用 python-docx 生真實 docx(含標題+段落)丟進 watch 資料夾, collector 呼叫 markitdown 轉出真實 md 內容(人工核對與原檔一致)、原檔複製進 assets/originals/、.gitattributes 正確含三種格式 LFS 宣告、commit+push 送達 bare remote。 誠實邊界:sandbox 無 git-lfs 二進位,驗不到真實 LFS smudge/clean filter 生效(只驗到 .gitattributes 內容正確);真實 Gitea remote push/憑證、真實客戶檔案樣本仍待本機/客戶環境。 其餘格式(xlsx/圖片)不在第一波承諾範圍,仍誠實丟 NotImplementedError。
This commit is contained in:
+37
-6
@@ -1,14 +1,23 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execFileSync } = require('child_process');
|
||||
|
||||
class NotImplementedError extends Error {}
|
||||
|
||||
const MARKDOWN_EXT = new Set(['.md', '.markdown']);
|
||||
|
||||
// design.md §4 明列的第一波轉檔格式:docx/pptx/pdf。其餘格式仍走 NotImplementedError,
|
||||
// 不擴大承諾範圍(品質不承諾是 design 原話,但格式範圍先照 SDD 講定的三種)。
|
||||
const MARKITDOWN_EXT = new Set(['.docx', '.pptx', '.pdf']);
|
||||
|
||||
/**
|
||||
* 檔案 → md 轉換(T3 骨架只做 .md passthrough;T4 補 Markitdown 邏輯)。
|
||||
* 檔案 → md 轉換(T4:Markitdown adapter)。
|
||||
* srcPath: 來源檔案絕對路徑(客戶知識資料夾內)
|
||||
* 回傳:{ relOutputPath, content }(相對 target repo 子目錄的輸出路徑 + md 內容)
|
||||
* 回傳:{ relOutputPath, content, originalCopy? }
|
||||
* - originalCopy(僅 Markitdown 轉檔路徑):{ relPath },供 index.js 把原檔複製進
|
||||
* target repo 的 assets/originals/(design.md §4:「原檔進 LFS」——LFS 追蹤本身
|
||||
* 依賴部署機器裝 git-lfs 並 `git lfs track`,本模組只管把原檔放進約定路徑,
|
||||
* 不假裝在雲端 sandbox 驗過真實 LFS push,見 README「待本機驗」)。
|
||||
*/
|
||||
function transformFile(srcPath, watchDir) {
|
||||
const ext = path.extname(srcPath).toLowerCase();
|
||||
@@ -17,16 +26,38 @@ function transformFile(srcPath, watchDir) {
|
||||
if (MARKDOWN_EXT.has(ext)) {
|
||||
const raw = fs.readFileSync(srcPath, 'utf8');
|
||||
const content = stampSourcePath(raw, relSrc);
|
||||
const relOutputPath = relSrc;
|
||||
return { relOutputPath, content };
|
||||
return { relOutputPath: relSrc, content };
|
||||
}
|
||||
|
||||
// 非 md(docx/pptx/pdf...)— T4 待補 Markitdown 轉檔,這裡先誠實丟未實作,不假裝轉好。
|
||||
if (MARKITDOWN_EXT.has(ext)) {
|
||||
const md = convertWithMarkitdown(srcPath);
|
||||
const content = stampSourcePath(md, relSrc);
|
||||
const relOutputPath = relSrc.slice(0, -ext.length) + '.md';
|
||||
return {
|
||||
relOutputPath,
|
||||
content,
|
||||
originalCopy: { relPath: path.join('assets/originals', relSrc) },
|
||||
};
|
||||
}
|
||||
|
||||
// 其餘格式(xlsx/圖片/...)— 不在 design.md §4 第一波承諾範圍,誠實丟未實作。
|
||||
throw new NotImplementedError(
|
||||
`${relSrc}:非 .md 格式轉檔待 T4(Markitdown adapter)補上,本骨架先跳過`,
|
||||
`${relSrc}:格式 ${ext} 不在第一波 Markitdown 承諾範圍(docx/pptx/pdf),本骨架先跳過`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 呼叫 markitdown CLI 轉檔(品質不承諾,design.md §4 原話)。 */
|
||||
function convertWithMarkitdown(srcPath) {
|
||||
try {
|
||||
return execFileSync('markitdown', [srcPath], {
|
||||
encoding: 'utf8',
|
||||
maxBuffer: 20 * 1024 * 1024,
|
||||
});
|
||||
} catch (e) {
|
||||
throw new Error(`markitdown 轉檔失敗(${srcPath}):${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/** md frontmatter 補 source_path(溯源用,design.md §4:「md frontmatter 記原檔路徑」)。 */
|
||||
function stampSourcePath(raw, relSrc) {
|
||||
const stamp = `source_path: "${relSrc}"`;
|
||||
|
||||
Reference in New Issue
Block a user