diff --git a/src/clis/xiaohongshu/download.ts b/src/clis/xiaohongshu/download.ts index a8456d3..81f5226 100644 --- a/src/clis/xiaohongshu/download.ts +++ b/src/clis/xiaohongshu/download.ts @@ -22,12 +22,12 @@ cli({ domain: 'www.xiaohongshu.com', strategy: Strategy.COOKIE, args: [ - { name: 'note-id', required: true, help: 'Note ID (from URL)' }, + { name: 'note_id', required: true, help: 'Note ID (from URL)' }, { name: 'output', default: './xiaohongshu-downloads', help: 'Output directory' }, ], columns: ['index', 'type', 'status', 'size'], func: async (page, kwargs) => { - const noteId = kwargs['note-id']; + const noteId = kwargs.note_id; const output = kwargs.output; // Navigate to note page diff --git a/src/clis/xiaohongshu/search.ts b/src/clis/xiaohongshu/search.ts index aedfc73..2fe59b9 100644 --- a/src/clis/xiaohongshu/search.ts +++ b/src/clis/xiaohongshu/search.ts @@ -43,7 +43,7 @@ cli({ const linkEl = el.querySelector('a[href*="/explore/"], a[href*="/search_result/"], a[href*="/note/"]'); const href = linkEl?.getAttribute('href') || ''; - const noteId = href.match(/\\/(?:explore|note)\\/([a-f0-9]+)/)?.[1] || ''; + const noteId = href.match(/\\/(?:explore|note)\\/([a-zA-Z0-9]+)/)?.[1] || ''; results.push({ title: (titleEl?.textContent || '').trim(), diff --git a/src/clis/xiaohongshu/user.ts b/src/clis/xiaohongshu/user.ts index 8736ac2..1e73614 100644 --- a/src/clis/xiaohongshu/user.ts +++ b/src/clis/xiaohongshu/user.ts @@ -13,11 +13,11 @@ cli({ ], columns: ['id', 'title', 'type', 'likes', 'url'], func: async (page, kwargs) => { + await page.installInterceptor('v1/user/posted'); + await page.goto(`https://www.xiaohongshu.com/user/profile/${kwargs.id}`); await page.wait(5); - await page.installInterceptor('v1/user/posted'); - // Trigger API by scrolling await page.autoScroll({ times: 2, delayMs: 2000 }); diff --git a/src/engine.test.ts b/src/engine.test.ts index 6589827..3b7199f 100644 --- a/src/engine.test.ts +++ b/src/engine.test.ts @@ -14,6 +14,23 @@ describe('discoverClis', () => { }); describe('executeCommand', () => { + it('accepts kebab-case option names after Commander camelCases them', async () => { + const cmd = cli({ + site: 'test-engine', + name: 'kebab-arg-test', + description: 'test command with kebab-case arg', + browser: false, + strategy: Strategy.PUBLIC, + args: [ + { name: 'note-id', required: true, help: 'Note ID' }, + ], + func: async (_page, kwargs) => [{ noteId: kwargs['note-id'] }], + }); + + const result = await executeCommand(cmd, null, { 'note-id': 'abc123' }); + expect(result).toEqual([{ noteId: 'abc123' }]); + }); + it('executes a command with func', async () => { const cmd = cli({ site: 'test-engine', diff --git a/src/main.ts b/src/main.ts index df4da18..f589f48 100644 --- a/src/main.ts +++ b/src/main.ts @@ -200,7 +200,8 @@ for (const [, cmd] of registry) { // Collect named options for (const arg of cmd.args) { if (arg.positional) continue; - const v = actionOpts[arg.name]; + const camelName = arg.name.replace(/-([a-z])/g, (_m, ch: string) => ch.toUpperCase()); + const v = actionOpts[arg.name] ?? actionOpts[camelName]; if (v !== undefined) kwargs[arg.name] = v; }