Files
arcrun-collector/index.js
T
Leo eff30bd741 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。
2026-07-07 21:13:13 +00:00

91 lines
2.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const fs = require('fs');
const path = require('path');
const chokidar = require('chokidar');
const { loadConfig } = require('./config');
const { transformFile, NotImplementedError } = require('./transform');
const { GitSync } = require('./git-sync');
function ensureDir(p) {
fs.mkdirSync(p, { recursive: true });
}
function outputPathFor(config, relOutputPath) {
return path.join(config.targetRepoDir, config.targetSubdir, relOutputPath);
}
function handleAddOrChange(config, srcPath) {
try {
const { relOutputPath, content, originalCopy } = transformFile(srcPath, config.watchDir);
const outPath = outputPathFor(config, relOutputPath);
ensureDir(path.dirname(outPath));
fs.writeFileSync(outPath, content, 'utf8');
console.log(`[collector] 寫入 ${relOutputPath}`);
if (originalCopy) {
const origOutPath = outputPathFor(config, originalCopy.relPath);
ensureDir(path.dirname(origOutPath));
fs.copyFileSync(srcPath, origOutPath);
console.log(`[collector] 原檔複製 ${originalCopy.relPath}(LFS 追蹤需部署機器已裝 git-lfs)`);
}
} catch (e) {
if (e instanceof NotImplementedError) {
console.warn(`[collector] 跳過(待 T4):${e.message}`);
} else {
console.error(`[collector] 轉檔失敗 ${srcPath}${e.message}`);
}
}
}
function handleUnlink(config, srcPath) {
const relSrc = path.relative(config.watchDir, srcPath);
const outPath = outputPathFor(config, relSrc);
if (fs.existsSync(outPath)) {
fs.unlinkSync(outPath);
console.log(`[collector] 移除 ${relSrc}(來源已刪除,deprecated 標記交給 ingest workflow`);
}
}
function startCollector(config, { git = new GitSync(config) } = {}) {
ensureDir(path.join(config.targetRepoDir, config.targetSubdir));
let pendingCount = 0;
let debounceTimer = null;
const flush = () => {
if (pendingCount === 0) return;
const n = pendingCount;
pendingCount = 0;
const result = git.commitAndPush(`collector: 同步 ${n} 項變更`);
if (result.committed) {
console.log(`[collector] 已 commit+push${n} 項變更)`);
}
};
const schedule = () => {
pendingCount += 1;
if (debounceTimer) clearTimeout(debounceTimer);
debounceTimer = setTimeout(flush, config.debounceMs);
};
const watcher = chokidar.watch(config.watchDir, {
ignoreInitial: false, // 首次啟動=首灌,掃過整個資料夾
persistent: true,
});
watcher
.on('add', (p) => { handleAddOrChange(config, p); schedule(); })
.on('change', (p) => { handleAddOrChange(config, p); schedule(); })
.on('unlink', (p) => { handleUnlink(config, p); schedule(); });
console.log(`[collector] watch 中:${config.watchDir}${config.targetRepoDir}/${config.targetSubdir}`);
return watcher;
}
if (require.main === module) {
const config = loadConfig();
startCollector(config);
}
module.exports = { startCollector };