import { readFileSync, writeFileSync, existsSync } from 'node:fs'; import { execSync } from 'node:child_process'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; const scriptDir = dirname(fileURLToPath(import.meta.url)); const root = join(scriptDir, '../../..'); const svc = join(root, 'java/fs-service/src/main/java/com/fs/company/service/workflow'); const srcDir = join(scriptDir, 'lobster_utf8_sources'); const commitFiles = execSync('git show --name-only --pretty=format: 5dfe60ed', { cwd: root }) .toString('utf8') .split(/\r?\n/) .filter(Boolean); function u(s) { return s.replace(/\\u([0-9a-fA-F]{4})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16))); } function writeUtf8(path, content) { let text = content.replace(/\r\n/g, '\n'); if (text.charCodeAt(0) === 0xfeff) { text = text.slice(1); } writeFileSync(path, text, 'utf8'); } function decodeFile(rel) { const path = join(root, rel); if (!existsSync(path)) return; const raw = readFileSync(path, 'utf8'); const decoded = u(raw); if (decoded === raw && !raw.charCodeAt(0)) return; writeUtf8(path, decoded); const count = (raw.match(/\\u[0-9a-fA-F]{4}/g) || []).length; if (count > 0) { console.log('decoded', rel, `(${count} escapes)`); } } function stripBom(rel) { const path = join(root, rel); if (!existsSync(path)) return; const raw = readFileSync(path); if (raw.length >= 3 && raw[0] === 0xef && raw[1] === 0xbb && raw[2] === 0xbf) { writeFileSync(path, raw.slice(3), 'utf8'); console.log('stripped BOM', rel); } } function copySource(name, subdir) { const rel = subdir ? `${subdir}/${name}` : name; writeUtf8(join(svc, rel), readFileSync(join(srcDir, name), 'utf8')); console.log('restored source', name); } const sqlPatches = [ { rel: 'java/fs-agent/src/main/resources/db/migration/tenant/V20260726_01__drop_lobster_evolution_token_menus.sql', header: u(`-- \u5220\u9664\u300cAI\u8fdb\u5316\u5f15\u64ce\u300d(29939) \u4e0e\u300cToken\u6d88\u8017\u7edf\u8ba1\u300d(29937) -- AI\u8fdb\u5316\u5f15\u64ce\u529f\u80fd\u5df2\u7531\u540e\u53f0 EvolutionScheduler +\u300c\u8bdd\u672f\u4f18\u5316\u5ba1\u6838\u300d\u627f\u63a5 -- Token\u6d88\u8017\u7edf\u8ba1\u83dc\u5355\u4e0b\u7ebf\uff0c\u7edf\u8ba1\u80fd\u529b\u4fdd\u7559\u5728\u8ba1\u8d39/\u6d41\u6c34\u4fa7 `), }, { rel: 'java/fs-agent/src/main/resources/db/migration/tenant/V20260728_01__lobster_udd_admin_menus.sql', header: u(`-- \u79df\u6237\u7ba1\u7406\u7aef\uff08sys_menu\uff09\u589e\u52a0\uff1a\u884c\u4e1a\u8bed\u6599\u77e9\u9635\u3001\u6df1\u5ea6\u5bf9\u8bdd\u8c03\u8bd5 -- \u4ec5\u7ba1\u7406\u7aef\u53ef\u89c1\uff0c\u4e0d\u540c\u6b65\u5230 company_menu\uff08\u9500\u552e\u7aef\uff09 `), }, { rel: 'java/sql/master/V20260726_01__drop_lobster_evolution_token_menus_master.sql', header: u(`-- \u4e3b\u5e93\uff1a\u5220\u9664\u300cAI\u8fdb\u5316\u5f15\u64ce\u300d(29939) \u4e0e\u300cToken\u6d88\u8017\u7edf\u8ba1\u300d(29937) `), }, { rel: 'java/sql/master/V20260728_01__lobster_udd_admin_menus_master.sql', header: u(`-- \u4e3b\u5e93\u65b0\u79df\u6237\u6a21\u677f tenant_sys_menu\uff1a\u884c\u4e1a\u8bed\u6599\u77e9\u9635\u3001\u6df1\u5ea6\u5bf9\u8bdd\u8c03\u8bd5\uff08\u4ec5\u7ba1\u7406\u7aef\u6a21\u677f\uff0c\u4e0d\u5199 tenant_company_menu\uff09 `), }, ]; for (const { rel, header } of sqlPatches) { const path = join(root, rel); if (!existsSync(path)) continue; let text = readFileSync(path, 'utf8').replace(/\r\n/g, '\n'); const bodyStart = text.indexOf('\n\n'); const body = bodyStart >= 0 ? text.slice(bodyStart + 1) : text.replace(/^[^\n]*\n?/, ''); writeUtf8(path, header + '\n' + body.replace(/^\n+/, '')); console.log('sql header fixed', rel); } { const rel = 'java/sql/lobster_menu_canonical_init.sql'; const path = join(root, rel); let text = readFileSync(path, 'utf8').replace(/\r\n/g, '\n'); text = text .replace(u('-- \u9f99\u8679\u5f15\u64ce\u83dc\u5355'), u('-- \u9f99\u867e\u5f15\u64ce\u83dc\u5355')) .replace(/^-- A\.3.*$/mu, u('-- A.3 \u91cd\u5efa sys_menu\uff08UNHEX \u4fdd\u8bc1\u4e2d\u6587\u540d\u79f0\uff09')) .replace(/^-- A\.4.*$/mu, u('-- A.4 \u540c\u6b65 company_menu\uff08\u9500\u552e\u7aef\u5b50\u96c6\uff09')) .replace(/^-- A\.5.*$/mu, u('-- A.5 \u8865\u5168\u89d2\u8272\u6388\u6743')) .replace(/^-- PART B.*tenant_sys_menu.*$/mu, u('-- PART B \uff1a\u9010\u79df\u6237 tenant_sys_menu + tenant_company_menu\uff08\u6a21\u677f\u5e93\u6267\u884c\uff09')); writeUtf8(path, text); console.log('sql fixed', rel); } for (const rel of commitFiles) { if (/\.(java|js)$/.test(rel)) { decodeFile(rel); } stripBom(rel); } for (const name of [ 'LobsterOutboundQualityGate.java', 'ProductKnowledgeBoundaryPolicy.java', 'TenantProductClaimSemanticAnalyzer.java', 'LobsterProactiveGreetingService.java', 'LobsterMessageTemplateRenderer.java', 'GeneralKnowledgeDialoguePolicy.java', 'CustomerFacingMessageSplitter.java', ]) { copySource(name, name.startsWith('General') || name.startsWith('Customer') ? 'deepdialogue' : 'execution'); } console.log('fix commit 5dfe60ed utf8 done');