| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253 |
- import fs from 'node:fs';
- import path from 'node:path';
- import crypto from 'node:crypto';
- import vm from 'node:vm';
- import { fileURLToPath } from 'node:url';
- const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
- const evidence = path.join(root, 'docs/workshop-v3-alignment-20261112');
- const target = path.join(root, 'docs/workshop-quality-r13-20261110/final-康师傅工作坊操作手册.html');
- const promptsFile = path.join(path.dirname(target), 'manual/workshop-dialogue-prompts.json');
- const sha = bytes => crypto.createHash('sha256').update(bytes).digest('hex');
- const source = fs.readFileSync(path.join(evidence, 'source-manual-v3.html'));
- const sourceSha256 = sha(source);
- if (sourceSha256 !== 'e8ed9dbd979c17f5caa88fb820f64f94a2e54518f810d7482b6b82f39d88041d') throw Error('V3_SOURCE_CHANGED');
- const url = 'https://s3.fmode.cn/user/K0xKEZANOG/kangshifu/manual-v3/index.html';
- const sourceText = source.toString('utf8');
- const promptBlock = sourceText.match(/window\.PROMPTS\s*=\s*(\{[\s\S]*?\n\});/);
- if (!promptBlock) throw Error('V3_PROMPTS_NOT_FOUND');
- const prompts = vm.runInNewContext('(' + promptBlock[1] + ')', {}, { timeout: 1000 });
- const archived = JSON.parse(fs.readFileSync(path.join(evidence, 'source-prompts-v3.json'), 'utf8'));
- if (JSON.stringify(prompts) !== JSON.stringify(archived.prompts) || Object.keys(prompts).length !== 13) throw Error('V3_PROMPTS_DIFFER');
- // Preserve in-page anchors; resolve only the overview links for the local final filename.
- const linked = sourceText.replaceAll('href="../overview/index.html"',
- 'href="https://s3.fmode.cn/user/K0xKEZANOG/kangshifu/overview/index.html"');
- if (linked === sourceText) throw Error('OVERVIEW_LINK_NOT_FOUND');
- // Some source tables lack the existing scroll wrapper; preserve their content and columns.
- const responsiveTables = `<script id="ksf-v3-responsive-tables">
- document.querySelectorAll('table.tbl').forEach(function(table) {
- if (table.closest('.tbl-scroll')) return;
- var wrapper = document.createElement('div');
- wrapper.className = 'tbl-scroll';
- wrapper.tabIndex = 0;
- wrapper.setAttribute('role', 'region');
- wrapper.setAttribute('aria-label', '表格,可横向滚动');
- table.before(wrapper);
- wrapper.appendChild(table);
- });
- </script>\n`;
- const html = linked.replace('</body>', responsiveTables + '</body>');
- const backup = path.join(evidence, 'retired-manual-before-v3');
- fs.mkdirSync(backup, { recursive: true });
- for (const file of [target, promptsFile]) {
- const name = path.join(backup, path.basename(file));
- if (!fs.existsSync(name)) fs.copyFileSync(file, name);
- }
- fs.writeFileSync(target, html);
- fs.writeFileSync(promptsFile, JSON.stringify({ schema: 'ksf.workshop-prompts.v3', source: url, sourceSha256, prompts }, null, 2) + '\n');
- const receipt = { ok: true, source: url, sourceSha256, finalManualSha256: sha(Buffer.from(html)),
- finalManualBytes: Buffer.byteLength(html), promptCount: 13, teachingContentChanged: false,
- adjustment: 'absolute overview links and scroll wrappers for unwrapped tables; all teaching text and prompts preserved',
- oldManualArchived: true, deployed: false };
- fs.writeFileSync(path.join(evidence, 'manual-sync.json'), JSON.stringify(receipt, null, 2) + '\n');
- console.log(JSON.stringify(receipt, null, 2));
|