packaging-scripts.mjs 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
  1. /** Run all seven installed thin scripts against synthetic files; snapshot proves no writes. */
  2. import assert from 'node:assert/strict'
  3. import fs from 'node:fs'
  4. import path from 'node:path'
  5. import { createHash } from 'node:crypto'
  6. import { execFileSync } from 'node:child_process'
  7. const [installedPackage, book, reportPath] = process.argv.slice(2).map(value => path.resolve(value))
  8. const hash = text => createHash('sha256').update(text).digest('hex')
  9. function snapshot(dir) {
  10. return Object.fromEntries(fs.readdirSync(dir, { withFileTypes: true }).flatMap(entry => {
  11. const target = path.join(dir, entry.name)
  12. return entry.isDirectory() ? Object.entries(snapshot(target)) : [[path.relative(book, target).replaceAll('\\', '/'), hash(fs.readFileSync(target))]]
  13. }).sort(([left], [right]) => left.localeCompare(right)))
  14. }
  15. const before = snapshot(book)
  16. const chapter = ['--卷', '1', '--章', '1', '--章名', '章1']
  17. const scripts = [
  18. ['novel-review/scripts/确定性检查.mjs', chapter],
  19. ['novel-design/scripts/影响分析.mjs', ['--变更路径', '世界书/人物档案/主角.md']],
  20. ['novel-outline/scripts/影响分析.mjs', ['--变更路径', '世界书/人物档案/主角.md']],
  21. ['novel-outline/scripts/材料备料.mjs', chapter],
  22. ['novel-settle/scripts/定稿备包.mjs', chapter],
  23. ['novel-settle/scripts/卷摘要候选.mjs', ['--卷', '1']],
  24. ['novel-export/scripts/最小导出.mjs', ['--卷', '1', '--目标', path.join(path.dirname(book), '导出目标')]],
  25. ]
  26. const results = []
  27. for (const [script, args] of scripts) {
  28. const output = execFileSync(process.execPath, ['--permission', `--allow-fs-read=${installedPackage}`, `--allow-fs-read=${book}`, `--allow-fs-read=${path.dirname(book)}`, path.join(installedPackage, 'skills', script), '--book', book, ...args], { cwd: path.dirname(book), encoding: 'utf8', windowsHide: true, timeout: 30000, maxBuffer: 5 * 1024 * 1024 })
  29. const result = JSON.parse(script.includes('确定性检查') ? output.slice(output.indexOf('[\n')) : output)
  30. if (script.includes('确定性检查')) assert.ok(result.length > 0 && result.every(item => item.审读指纹 && Array.isArray(item.发现项)))
  31. if ('ok' in result && !script.includes('卷摘要候选')) assert.equal(result.ok, true, output)
  32. if (script.includes('影响分析')) assert.ok(result.references.length > 0)
  33. if (script.includes('材料备料')) assert.equal(result.段.length, 10)
  34. if (script.includes('定稿备包')) assert.ok(result.files.length >= 7)
  35. if (script.includes('卷摘要候选')) {
  36. // 卷摘要候选按书况输出候选或如实报缺,两种都属合法结果;书仓不得有写盘
  37. assert.equal(typeof result.ok, 'boolean', output)
  38. if (result.ok) assert.ok(typeof result.候选 === 'string' && result.候选.length > 0, output)
  39. else assert.ok(typeof result.reason === 'string' && result.reason.length > 0, output)
  40. }
  41. if (script.includes('最小导出')) {
  42. assert.equal(result.文件.length, 2)
  43. assert.ok(result.文件[0].content.length > 0 && result.文件[1].content.includes('SHA-256'))
  44. assert.equal(result.目标.ok, true)
  45. assert.ok(!fs.existsSync(path.join(path.dirname(book), '导出目标')), 'export wrote to disk')
  46. }
  47. assert.deepEqual(snapshot(book), before, script + ' wrote to the book')
  48. results.push({ script, ok: true, outputBytes: Buffer.byteLength(output) })
  49. }
  50. fs.writeFileSync(reportPath, JSON.stringify({ ok: true, scripts: results, filesUnchanged: Object.keys(before).length, sourceReadDenied: true, writesDenied: true }, null, 2) + '\n')
  51. console.log('Installed scripts: 7/7 passed; book snapshot unchanged')