// 冒烟测试:验证包结构和基础导出 import { resolveIdentity, identitySummary, makeLogKey } from '../lib/index.mjs'; import { ENDPOINTS } from '../lib/upload.mjs'; import { existsSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const root = path.join(__dirname, '..'); let pass = 0, fail = 0; function ok(label, cond) { if (cond) { console.log(` ✓ ${label}`); pass++; } else { console.error(` ✗ ${label}`); fail++; } } console.log('smoke test — skill-agent-log-s3 v2.0.0\n'); // 1. 必要文件存在 console.log('[1] 文件结构'); ok('package.json', existsSync(path.join(root, 'package.json'))); ok('LICENSE', existsSync(path.join(root, 'LICENSE'))); ok('README.md', existsSync(path.join(root, 'README.md'))); ok('SKILL.md', existsSync(path.join(root, 'SKILL.md'))); ok('CHANGELOG.md', existsSync(path.join(root, 'CHANGELOG.md'))); ok('skill-package-manifest', existsSync(path.join(root, 'skill-package-manifest.json'))); ok('lib/index.mjs', existsSync(path.join(root, 'lib/index.mjs'))); ok('lib/identity.mjs', existsSync(path.join(root, 'lib/identity.mjs'))); ok('lib/upload.mjs', existsSync(path.join(root, 'lib/upload.mjs'))); ok('lib/collect.py', existsSync(path.join(root, 'lib/collect.py'))); ok('bin/agent-log.mjs', existsSync(path.join(root, 'bin/agent-log.mjs'))); ok('skills/.../SKILL.md', existsSync(path.join(root, 'skills/skill-agent-log-s3/SKILL.md'))); // 2. package.json 关键字段 console.log('\n[2] package.json'); import { readFileSync } from 'node:fs'; const pkg = JSON.parse(readFileSync(path.join(root, 'package.json'), 'utf-8')); ok('type=module', pkg.type === 'module'); ok('version=2.0.0', pkg.version === '2.0.0'); ok('main=./lib/index.mjs', pkg.main === './lib/index.mjs'); ok('exports[.] import', pkg.exports?.['.']?.import === './lib/index.mjs'); ok('exports[.] default', pkg.exports?.['.']?.default === './lib/index.mjs'); ok('bin agent-log', pkg.bin?.['agent-log'] === './bin/agent-log.mjs'); ok('node>=18', pkg.engines?.node === '>=18'); ok('no dependencies', Object.keys(pkg.dependencies || {}).length === 0); // 3. SDK 导出 console.log('\n[3] SDK 导出'); ok('resolveIdentity is fn', typeof resolveIdentity === 'function'); ok('identitySummary is fn', typeof identitySummary === 'function'); ok('makeLogKey is fn', typeof makeLogKey === 'function'); ok('ENDPOINTS.functions', typeof ENDPOINTS.functions === 'string' && ENDPOINTS.functions.includes('server.fmode.cn')); ok('ENDPOINTS.publicBase', typeof ENDPOINTS.publicBase === 'string' && ENDPOINTS.publicBase.includes('s3.fmode.cn')); // 4. 身份解析逻辑 console.log('\n[4] 身份解析'); const id = resolveIdentity(); ok('返回对象', typeof id === 'object' && id !== null); ok('有 agentId 字段', 'agentId' in id); ok('agentId 非空', typeof id.agentId === 'string' && id.agentId.length > 0); const summary = identitySummary(id); ok('summary 不含完整 token', !summary.sessionToken?.match(/^r:[a-f0-9]{20}/)); // 5. makeLogKey 格式 console.log('\n[5] 日志键格式'); const key = makeLogKey(); ok('以 log/ 开头', key.startsWith('log/')); ok('格式 log/YYYYMMDD/HHMM.json', /^log\/\d{8}\/\d{4}\.json$/.test(key)); // 6. 没有硬编码禁用端点 console.log('\n[6] 禁用端点检查'); const uploadSrc = readFileSync(path.join(root, 'lib/upload.mjs'), 'utf-8'); ok('不含 /api/storage/credentials', !uploadSrc.includes('/api/storage/credentials')); ok('不含 /api/storage/upload', !uploadSrc.includes('/api/storage/upload')); ok('不含 S3_AK', !uploadSrc.includes('S3_AK')); ok('不含 S3_SK', !uploadSrc.includes('S3_SK')); console.log(`\n结果: ${pass} 通过 / ${fail} 失败`); if (fail > 0) process.exit(1);