mirror of
https://github.com/jackwener/OpenCLI.git
synced 2026-09-14 18:25:42 +08:00
c370bd0582
* feat(xiaohongshu): add 4 creator analytics commands Add creator backend support for Xiaohongshu (小红书), enabling creators to access their analytics data from the command line. New commands: - creator-profile: account info (followers, likes, creator level) - creator-stats: 7-day/30-day overview (views, likes, collects, comments, shares, new followers) with daily trend data - creator-notes: note list with per-note metrics from note manager - creator-note-detail: single note analytics breakdown (organic vs promoted vs video traffic) API discovery: - /api/galaxy/creator/home/personal_info (cookie auth, 200 OK) - /api/galaxy/creator/data/note_detail_new (cookie auth, 200 OK) - /api/galaxy/creator/data/note_detail?note_id=xxx (cookie auth, 200 OK) - Note manager DOM extraction for note list (bypasses v2 signature) All endpoints verified working with real creator account. Screenshots (redacted) included in docs/screenshots/. Requires: Chrome logged into creator.xiaohongshu.com * chore: remove screenshots from repo (will host externally for PR) * review: fix creator analytics CLI integration Co-authored-by: stone16 <stone2paul@gmail.com> * test: add site-scoped test runner Co-authored-by: stone16 <stone2paul@gmail.com> * review: ignore publish timestamps in creator note metrics --------- Co-authored-by: jackwener <jakevingoo@gmail.com>
71 lines
1.7 KiB
JavaScript
71 lines
1.7 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { spawnSync } from 'node:child_process';
|
|
import * as fs from 'node:fs';
|
|
import * as path from 'node:path';
|
|
|
|
const site = process.argv[2]?.trim();
|
|
|
|
if (!site) {
|
|
console.error('Usage: npm run test:site -- <site>');
|
|
process.exit(1);
|
|
}
|
|
|
|
const repoRoot = path.resolve(new URL('..', import.meta.url).pathname);
|
|
const srcDir = path.join(repoRoot, 'src');
|
|
|
|
function runStep(label, command, args) {
|
|
console.log(`\n==> ${label}`);
|
|
const result = spawnSync(command, args, {
|
|
cwd: repoRoot,
|
|
stdio: 'inherit',
|
|
env: process.env,
|
|
});
|
|
|
|
if (result.status !== 0) {
|
|
process.exit(result.status ?? 1);
|
|
}
|
|
}
|
|
|
|
function walk(dir) {
|
|
const files = [];
|
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
const fullPath = path.join(dir, entry.name);
|
|
if (entry.isDirectory()) {
|
|
files.push(...walk(fullPath));
|
|
} else {
|
|
files.push(fullPath);
|
|
}
|
|
}
|
|
return files;
|
|
}
|
|
|
|
function toPosix(filePath) {
|
|
return filePath.split(path.sep).join('/');
|
|
}
|
|
|
|
function findSiteTests() {
|
|
return walk(srcDir)
|
|
.filter(filePath => filePath.endsWith('.test.ts'))
|
|
.filter(filePath => {
|
|
const normalized = toPosix(path.relative(repoRoot, filePath));
|
|
return normalized.includes(`/clis/${site}/`) || normalized.includes(`/${site}.test.ts`);
|
|
})
|
|
.sort();
|
|
}
|
|
|
|
runStep('Typecheck', 'npm', ['run', 'typecheck']);
|
|
runStep('Targeted verify', 'npx', ['tsx', 'src/main.ts', 'verify', site]);
|
|
|
|
const testFiles = findSiteTests();
|
|
if (testFiles.length === 0) {
|
|
console.log(`\nNo site-specific vitest files found for "${site}". Skipping full vitest run.`);
|
|
process.exit(0);
|
|
}
|
|
|
|
runStep(
|
|
`Site tests (${site})`,
|
|
'npx',
|
|
['vitest', 'run', ...testFiles.map(filePath => path.relative(repoRoot, filePath))],
|
|
);
|