import fs from 'node:fs'; import path from 'node:path'; import crypto from 'node:crypto'; import { fileURLToPath } from 'node:url'; import { buildEmployeeRead } from '../server/identity/build-employee-read.mjs'; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const sha = bytes => crypto.createHash('sha256').update(bytes).digest('hex'); const args = process.argv.slice(2); if (args.length !== 4 || args[0] !== '--base' || args[2] !== '--out' || !path.isAbsolute(args[3]) || fs.existsSync(args[3])) throw Error('Use --base PRIVATE_R5_SNAPSHOT --out NEW_ABSOLUTE_DIRECTORY'); const base = JSON.parse(fs.readFileSync(args[1], 'utf8')); if (base.objectId !== 'inPfC51rvn' || sha(base.code) !== 'd4c7e2923a13b56b60821f02c8ca4ab472fca3664cde8fcdbf3f621e51047b23') throw Error('EXPECTED_DEPLOYED_R5_BASE'); const built = buildEmployeeRead(base), skill = 'skills/ksf-workshop-runtime'; const files = fs.readFileSync(path.join(root, skill, 'FILES.sha256'), 'utf8').trim().split(/\r?\n/).map(line => { const m = line.match(/^([a-f0-9]{64}) ([\w./-]+)$/); if (!m || m[2].split('/').includes('..') || sha(fs.readFileSync(path.join(root, skill, m[2]))) !== m[1]) throw Error('SKILL_MANIFEST_MISMATCH'); return skill + '/' + m[2]; }); if (new Set(files).size !== 10) throw Error('UNEXPECTED_SKILL_FILES'); files.push(skill + '/FILES.sha256', 'scripts/install-workshop-skill.mjs', 'scripts/build-workshop-skill.mjs', 'scripts/package-employee-read-patch.mjs', 'server/identity/employee-read.mjs', 'server/identity/wecom-directory.mjs', 'server/identity/build-employee-read.mjs', 'server/identity/manage.mjs', 'server/identity/platform.example.json', 'docs/employee-read-patch-20261103.md', 'docs/wanggang-employee-read-test-20261103.md', 'docs/employee-read-validation-20261103.json'); for (const file of files) { const stat = fs.lstatSync(path.join(root, file)); if (!stat.isFile() || stat.isSymbolicLink()) throw Error('NOT_PLAIN_PACKAGE_FILE'); } const out = args[3]; fs.mkdirSync(out, { recursive: true }); for (const file of files) { const target = path.join(out, file); fs.mkdirSync(path.dirname(target), { recursive: true }); fs.copyFileSync(path.join(root, file), target, fs.constants.COPYFILE_EXCL); } const dist = path.join(out, 'server/identity/read-dist'); fs.mkdirSync(dist, { recursive: true }); for (const entry of built.functions) { const relative = 'server/identity/read-dist/' + entry.name + '.js'; fs.writeFileSync(path.join(out, relative), entry.code, { flag: 'wx' }); files.push(relative); } const manifest = 'server/identity/read-dist/manifest.json'; fs.writeFileSync(path.join(out, manifest), JSON.stringify({ ...built, functions: built.functions.map(({ code, ...meta }) => meta) }, null, 2), { flag: 'wx' }); files.push(manifest); fs.writeFileSync(path.join(out, 'FILES.sha256'), files.sort().map(file => sha(fs.readFileSync(path.join(out, file))) + ' ' + file).join('\n') + '\n', { flag: 'wx' }); console.log(JSON.stringify({ ok: true, release: 'ksf.employee-read-patch.20261103.r1', files: files.length + 1, functionsAdded: 1, existingFunctionsChanged: 0, staticFilesChanged: 0, out, deployed: false }));