package-contents-smoke.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. #!/usr/bin/env node
  2. const { spawnSync } = require('child_process');
  3. const REQUIRED_FILES = [
  4. '.claude-plugin/plugin.json',
  5. '.mcp.json',
  6. 'README.md',
  7. 'bin/tihao.js',
  8. 'install.js',
  9. 'package.json',
  10. 'skill-package-manifest.json',
  11. 'memory-templates/tihao-sourcing-profile.json',
  12. 'mcp/src/server.js',
  13. 'mcp/src/features/fmode-image-analysis/image-analysis.js',
  14. 'mcp/src/tools/fmode-image-analysis.js',
  15. 'mcp/src/tools/tihao-brief-sourcing-run.js',
  16. 'mcp/src/tools/tihao-preference-update.js',
  17. 'skills/fmode-image-analysis/SKILL.md',
  18. 'skills/tihao/SKILL.md',
  19. 'skills/tihao/references/user-workflow.md',
  20. 'skills/tihao/references/output-format.md',
  21. 'docs/install-guide.md',
  22. 'docs/skill-database-record.json',
  23. 'docs/payment-package-links.md',
  24. 'docs/business-proof-action-order.zh-CN.md',
  25. 'docs/tihao-handoff-index.md'
  26. ];
  27. const FORBIDDEN_PATH_PATTERNS = [
  28. /^outputs?\//,
  29. /^node_modules\//,
  30. /^\.git\//,
  31. /^\.codex-run\//,
  32. /^\.npm-cache/,
  33. /^\.claude\//,
  34. /^\.vscode\//,
  35. /^dist\//,
  36. /^tmp\//,
  37. /^coverage\//,
  38. /(^|\/)\.env($|\.)/,
  39. /(^|\/)\.npmrc$/,
  40. /(^|\/)package-lock\.json$/,
  41. /\.(log|tgz|zip)$/i
  42. ];
  43. function main() {
  44. const result = runNpmPackJson();
  45. if (result.status !== 0) {
  46. process.stderr.write(result.stderr || result.stdout || '');
  47. throw new Error('npm pack --dry-run --json failed');
  48. }
  49. const pack = parsePackJson(result.stdout);
  50. assert(pack.name === '@vocmarket/tihao', 'package name should match public package');
  51. assert(pack.version === require('../package.json').version, 'pack version should match package.json');
  52. const files = (pack.files || []).map(file => normalizePath(file.path));
  53. const fileSet = new Set(files);
  54. for (const file of REQUIRED_FILES) {
  55. assert(fileSet.has(file), `package should include ${file}`);
  56. }
  57. const forbidden = files.filter(file => FORBIDDEN_PATH_PATTERNS.some(pattern => pattern.test(file)));
  58. assert(forbidden.length === 0, `package should not include forbidden files: ${forbidden.join(', ')}`);
  59. assert(files.every(file => !file.startsWith('skills/tihao-creator-sourcing/')), 'package should not ship legacy skill directory');
  60. assert(files.every(file => file !== 'bin/tihao-sourcing.js'), 'package should use tihao as the primary CLI file');
  61. assert(files.every(file => !file.includes('sessionToken')), 'package file paths should not mention sessionToken');
  62. assert(files.every(file => !file.includes('Authorization')), 'package file paths should not mention Authorization');
  63. assert(files.every(file => !file.includes('\\')), 'package paths should use npm forward-slash paths');
  64. console.log(JSON.stringify({
  65. ok: true,
  66. name: pack.name,
  67. version: pack.version,
  68. entryCount: files.length,
  69. size: pack.size,
  70. unpackedSize: pack.unpackedSize,
  71. requiredCount: REQUIRED_FILES.length
  72. }, null, 2));
  73. }
  74. function runNpmPackJson() {
  75. const args = ['pack', '--dry-run', '--json'];
  76. if (process.env.npm_execpath) {
  77. return spawnSync(process.execPath, [process.env.npm_execpath, ...args], {
  78. cwd: process.cwd(),
  79. encoding: 'utf8',
  80. shell: false
  81. });
  82. }
  83. if (process.platform === 'win32') {
  84. return spawnSync('npm.cmd', args, {
  85. cwd: process.cwd(),
  86. encoding: 'utf8',
  87. shell: true
  88. });
  89. }
  90. return spawnSync('npm', args, {
  91. cwd: process.cwd(),
  92. encoding: 'utf8',
  93. shell: false
  94. });
  95. }
  96. function parsePackJson(stdout) {
  97. let parsed;
  98. try {
  99. parsed = JSON.parse(String(stdout || '').trim());
  100. } catch (error) {
  101. throw new Error(`failed to parse npm pack json: ${error.message}`);
  102. }
  103. const pack = Array.isArray(parsed) ? parsed[0] : parsed;
  104. assert(pack && Array.isArray(pack.files), 'npm pack json should include files array');
  105. return pack;
  106. }
  107. function normalizePath(file) {
  108. return String(file || '').replace(/\\/g, '/');
  109. }
  110. function assert(condition, message) {
  111. if (!condition) throw new Error(message);
  112. }
  113. if (require.main === module) main();