| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125 |
- #!/usr/bin/env node
- const { spawnSync } = require('child_process');
- const REQUIRED_FILES = [
- '.claude-plugin/plugin.json',
- '.mcp.json',
- 'README.md',
- 'bin/tihao.js',
- 'install.js',
- 'package.json',
- 'skill-package-manifest.json',
- 'memory-templates/tihao-sourcing-profile.json',
- 'mcp/src/server.js',
- 'mcp/src/features/fmode-image-analysis/image-analysis.js',
- 'mcp/src/tools/fmode-image-analysis.js',
- 'mcp/src/tools/tihao-brief-sourcing-run.js',
- 'mcp/src/tools/tihao-preference-update.js',
- 'skills/fmode-image-analysis/SKILL.md',
- 'skills/tihao/SKILL.md',
- 'skills/tihao/references/user-workflow.md',
- 'skills/tihao/references/output-format.md',
- 'docs/install-guide.md',
- 'docs/skill-database-record.json',
- 'docs/payment-package-links.md',
- 'docs/business-proof-action-order.zh-CN.md',
- 'docs/tihao-handoff-index.md'
- ];
- const FORBIDDEN_PATH_PATTERNS = [
- /^outputs?\//,
- /^node_modules\//,
- /^\.git\//,
- /^\.codex-run\//,
- /^\.npm-cache/,
- /^\.claude\//,
- /^\.vscode\//,
- /^dist\//,
- /^tmp\//,
- /^coverage\//,
- /(^|\/)\.env($|\.)/,
- /(^|\/)\.npmrc$/,
- /(^|\/)package-lock\.json$/,
- /\.(log|tgz|zip)$/i
- ];
- function main() {
- const result = runNpmPackJson();
- if (result.status !== 0) {
- process.stderr.write(result.stderr || result.stdout || '');
- throw new Error('npm pack --dry-run --json failed');
- }
- const pack = parsePackJson(result.stdout);
- assert(pack.name === '@vocmarket/tihao', 'package name should match public package');
- assert(pack.version === require('../package.json').version, 'pack version should match package.json');
- const files = (pack.files || []).map(file => normalizePath(file.path));
- const fileSet = new Set(files);
- for (const file of REQUIRED_FILES) {
- assert(fileSet.has(file), `package should include ${file}`);
- }
- const forbidden = files.filter(file => FORBIDDEN_PATH_PATTERNS.some(pattern => pattern.test(file)));
- assert(forbidden.length === 0, `package should not include forbidden files: ${forbidden.join(', ')}`);
- assert(files.every(file => !file.startsWith('skills/tihao-creator-sourcing/')), 'package should not ship legacy skill directory');
- assert(files.every(file => file !== 'bin/tihao-sourcing.js'), 'package should use tihao as the primary CLI file');
- assert(files.every(file => !file.includes('sessionToken')), 'package file paths should not mention sessionToken');
- assert(files.every(file => !file.includes('Authorization')), 'package file paths should not mention Authorization');
- assert(files.every(file => !file.includes('\\')), 'package paths should use npm forward-slash paths');
- console.log(JSON.stringify({
- ok: true,
- name: pack.name,
- version: pack.version,
- entryCount: files.length,
- size: pack.size,
- unpackedSize: pack.unpackedSize,
- requiredCount: REQUIRED_FILES.length
- }, null, 2));
- }
- function runNpmPackJson() {
- const args = ['pack', '--dry-run', '--json'];
- if (process.env.npm_execpath) {
- return spawnSync(process.execPath, [process.env.npm_execpath, ...args], {
- cwd: process.cwd(),
- encoding: 'utf8',
- shell: false
- });
- }
- if (process.platform === 'win32') {
- return spawnSync('npm.cmd', args, {
- cwd: process.cwd(),
- encoding: 'utf8',
- shell: true
- });
- }
- return spawnSync('npm', args, {
- cwd: process.cwd(),
- encoding: 'utf8',
- shell: false
- });
- }
- function parsePackJson(stdout) {
- let parsed;
- try {
- parsed = JSON.parse(String(stdout || '').trim());
- } catch (error) {
- throw new Error(`failed to parse npm pack json: ${error.message}`);
- }
- const pack = Array.isArray(parsed) ? parsed[0] : parsed;
- assert(pack && Array.isArray(pack.files), 'npm pack json should include files array');
- return pack;
- }
- function normalizePath(file) {
- return String(file || '').replace(/\\/g, '/');
- }
- function assert(condition, message) {
- if (!condition) throw new Error(message);
- }
- if (require.main === module) main();
|