@skills-store/rednote 0.1.9 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -333,6 +333,7 @@ Use these shapes as the success model when a command returns JSON.
333
333
  "loginClicked": true,
334
334
  "pageUrl": "string",
335
335
  "waitingForPhoneLogin": true,
336
+ "qrCodePath": "string|null",
336
337
  "message": "string"
337
338
  }
338
339
  }
@@ -490,7 +491,7 @@ If a command fails, check these in order:
490
491
 
491
492
  ## Repository
492
493
 
493
- - Homepage: https://github.com/skills-router/skills-store/tree/main/packages/rednote
494
+ - Homepage: https://github.com/skills-router/skills-store/tree/main/packages/rednote-cli
494
495
  - Issues: https://github.com/skills-router/skills-store/issues
495
496
 
496
497
  ## License
@@ -1,8 +1,79 @@
1
1
  #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
2
4
  import { parseArgs } from 'node:util';
5
+ import { fileURLToPath } from 'node:url';
3
6
  import { printJson, runCli } from '../utils/browser-cli.js';
4
7
  import { resolveStatusTarget } from './status.js';
5
8
  import { checkRednoteLogin, createRednoteSession, disconnectRednoteSession } from './checkLogin.js';
9
+ const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
10
+ const REDNOTE_ROOT = path.resolve(SCRIPT_DIR, '../..');
11
+ function timestampForFilename() {
12
+ return new Date().toISOString().replaceAll(':', '').replaceAll('.', '').replace('T', '-').replace('Z', 'Z');
13
+ }
14
+ function resolveQrCodePath() {
15
+ return path.join(REDNOTE_ROOT, 'output', `login-qrcode-${timestampForFilename()}.png`);
16
+ }
17
+ function parseQrCodeDataUrl(src) {
18
+ const match = src.match(/^data:(image\/[a-zA-Z0-9.+-]+);base64,(.+)$/);
19
+ if (!match) {
20
+ return null;
21
+ }
22
+ return {
23
+ mimeType: match[1],
24
+ buffer: Buffer.from(match[2], 'base64')
25
+ };
26
+ }
27
+ async function refreshExpiredQrCode(page) {
28
+ const statusText = page.locator('.qrcode .status-text').first();
29
+ const refreshButton = page.locator('.qrcode .status-desc.refresh').first();
30
+ const isExpiredVisible = await statusText.isVisible().catch(()=>false);
31
+ if (!isExpiredVisible) {
32
+ return false;
33
+ }
34
+ const text = (await statusText.textContent().catch(()=>null))?.trim() ?? '';
35
+ if (!text.includes('过期')) {
36
+ return false;
37
+ }
38
+ if (await refreshButton.isVisible().catch(()=>false)) {
39
+ await refreshButton.click({
40
+ timeout: 2_000
41
+ }).catch(()=>{});
42
+ await page.waitForTimeout(800);
43
+ return true;
44
+ }
45
+ return false;
46
+ }
47
+ async function saveQrCodeImage(page) {
48
+ const qrImage = page.locator('.qrcode .qrcode-img').first();
49
+ for(let attempt = 0; attempt < 3; attempt += 1){
50
+ await qrImage.waitFor({
51
+ state: 'visible',
52
+ timeout: 5_000
53
+ });
54
+ const refreshed = await refreshExpiredQrCode(page);
55
+ if (refreshed) {
56
+ continue;
57
+ }
58
+ const filePath = resolveQrCodePath();
59
+ fs.mkdirSync(path.dirname(filePath), {
60
+ recursive: true
61
+ });
62
+ const src = await qrImage.getAttribute('src');
63
+ if (src) {
64
+ const parsed = parseQrCodeDataUrl(src);
65
+ if (parsed?.mimeType === 'image/png') {
66
+ fs.writeFileSync(filePath, parsed.buffer);
67
+ return filePath;
68
+ }
69
+ }
70
+ await qrImage.screenshot({
71
+ path: filePath
72
+ });
73
+ return filePath;
74
+ }
75
+ throw new Error('未检测到可用的小红书登录二维码,请确认登录弹窗是否已打开。');
76
+ }
6
77
  function printLoginHelp() {
7
78
  process.stdout.write(`rednote login
8
79
 
@@ -36,6 +107,7 @@ export async function openRednoteLogin(target, session) {
36
107
  loginClicked: false,
37
108
  pageUrl: session.page.url(),
38
109
  waitingForPhoneLogin: false,
110
+ qrCodePath: null,
39
111
  message: '当前实例已登录,无需重复执行登录操作。'
40
112
  }
41
113
  };
@@ -50,6 +122,7 @@ export async function openRednoteLogin(target, session) {
50
122
  loginClicked: false,
51
123
  pageUrl: page.url(),
52
124
  waitingForPhoneLogin: false,
125
+ qrCodePath: null,
53
126
  message: '未检测到登录按钮,当前实例可能已经登录。'
54
127
  }
55
128
  };
@@ -58,13 +131,15 @@ export async function openRednoteLogin(target, session) {
58
131
  timeout: 2000
59
132
  });
60
133
  await page.waitForTimeout(500);
134
+ const qrCodePath = await saveQrCodeImage(page);
61
135
  return {
62
136
  ok: true,
63
137
  rednote: {
64
138
  loginClicked: true,
65
139
  pageUrl: page.url(),
66
140
  waitingForPhoneLogin: true,
67
- message: '已点击登录按钮,请在浏览器中继续输入手机号并完成登录。'
141
+ qrCodePath,
142
+ message: '已点击登录按钮并导出二维码图片,请扫码完成登录。'
68
143
  }
69
144
  };
70
145
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skills-store/rednote",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
@@ -50,9 +50,9 @@
50
50
  "repository": {
51
51
  "type": "git",
52
52
  "url": "git+https://github.com/skills-router/skills-store.git",
53
- "directory": "packages/rednote"
53
+ "directory": "packages/rednote-cli"
54
54
  },
55
- "homepage": "https://github.com/skills-router/skills-store/tree/main/packages/rednote",
55
+ "homepage": "https://github.com/skills-router/skills-store/tree/main/packages/rednote-cli",
56
56
  "bugs": {
57
57
  "url": "https://github.com/skills-router/skills-store/issues"
58
58
  },
File without changes