Files
arcrun-collector/index.js
T
Leo 4ca94c17ca T3: collector 骨架——watch→轉檔→git commit/push(design §4)
Node 單檔服務(非 arcrun workflow,跑客戶端機器):chokidar watch 事件驅動、debounce
合併批次 commit、.md passthrough(非 md 格式誠實丟 NotImplemented 待 T4 Markitdown)、
刪檔→target repo 移除對應檔(deprecated 標記留給 ingest workflow,collector 只管檔案鏡像)。

已驗(雲端 sandbox scratch 環境,非真實客戶環境):node --check 全過;起 scratch watch dir
+ scratch git repo + bare remote,實測 add/change/delete 三種事件皆正確偵測、debounce 正確
合併成一次 commit、git push 真的送達 remote(git log 驗證)、非 md 格式正確跳過並警告。

端到端待本機/客戶環境驗(README.md 已列):真實 NAS/VM watch、真實 Gitea remote+憑證、
Gitea push webhook 真觸發 ingest、systemd 常駐穩定性。
2026-07-07 21:08:21 +00:00

84 lines
2.6 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 } = transformFile(srcPath, config.watchDir);
const outPath = outputPathFor(config, relOutputPath);
ensureDir(path.dirname(outPath));
fs.writeFileSync(outPath, content, 'utf8');
console.log(`[collector] 寫入 ${relOutputPath}`);
} 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 };