@pomelolabs/leads 0.1.5 → 0.2.2

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/client/bundle.js CHANGED
@@ -122,7 +122,7 @@
122
122
  // package.json
123
123
  var package_default = {
124
124
  name: "@pomelolabs/leads",
125
- version: "0.1.5",
125
+ version: "0.2.2",
126
126
  description: "\u67DA\u5BA2 Pomelo\uFF1A\u5185\u5BB9\u83B7\u5BA2\u5DE5\u4F5C\u53F0\uFF08DSH \u63D2\u4EF6\uFF09",
127
127
  license: "UNLICENSED",
128
128
  type: "module",
@@ -155,8 +155,7 @@
155
155
  ],
156
156
  scripts: {
157
157
  build: "esbuild client/src/index.jsx --bundle --format=iife --outfile=client/bundle.js --loader:.css=text --loader:.svg=text --jsx=transform --external:react --target=es2022 --log-level=warning",
158
- dev: "npm run build -- --watch",
159
- "browser:install": "node ./node_modules/puppeteer/lib/puppeteer/node/cli.js browsers install chrome"
158
+ dev: "npm run build -- --watch"
160
159
  },
161
160
  devDependencies: {
162
161
  "@phosphor-icons/core": "^2.1.1",
@@ -176,7 +175,8 @@
176
175
  },
177
176
  dependencies: {
178
177
  "playwright-core": "^1.64.1",
179
- puppeteer: "^25.16.0"
178
+ "puppeteer-core": "^25.16.0",
179
+ "@puppeteer/browsers": "^3.2.6"
180
180
  },
181
181
  publishConfig: {
182
182
  access: "public",
package/host/browser.js CHANGED
@@ -10,7 +10,8 @@
10
10
  import fs from 'node:fs/promises';
11
11
  import os from 'node:os';
12
12
  import path from 'node:path';
13
- import puppeteer from 'puppeteer';
13
+ import puppeteer from 'puppeteer-core';
14
+ import { Browser as BrowserType, detectBrowserPlatform, install as installBrowserBinary, resolveBuildId } from '@puppeteer/browsers';
14
15
 
15
16
  const PLATFORM_URLS = {
16
17
  xhs: {
@@ -106,6 +107,62 @@ async function findPlaywrightChromium() {
106
107
  return '';
107
108
  }
108
109
 
110
+ async function findCachedChromium() {
111
+ const roots = [
112
+ path.join(homeDir(), 'pomelo', 'browser-cache', 'chrome'),
113
+ path.join(os.homedir(), '.cache', 'puppeteer', 'chrome'),
114
+ ];
115
+ for (const root of roots) {
116
+ let names = [];
117
+ try { names = await fs.readdir(root); } catch { continue; }
118
+ for (const name of names.sort().reverse()) {
119
+ const base = path.join(root, name);
120
+ const candidates = process.platform === 'darwin'
121
+ ? [
122
+ path.join(base, 'chrome-mac-arm64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing'),
123
+ path.join(base, 'chrome-mac-x64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing'),
124
+ path.join(base, 'chrome-mac/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing'),
125
+ ]
126
+ : process.platform === 'win32'
127
+ ? [path.join(base, 'chrome-win64', 'chrome.exe'), path.join(base, 'chrome-win32', 'chrome.exe')]
128
+ : [path.join(base, 'chrome-linux64', 'chrome'), path.join(base, 'chrome-linux', 'chrome')];
129
+ for (const candidate of candidates) {
130
+ if (await executableAt(candidate)) return candidate;
131
+ }
132
+ }
133
+ }
134
+ return '';
135
+ }
136
+
137
+ let browserInstallPromise = null;
138
+ export async function ensureBrowser(log = async () => {}) {
139
+ const existing = await resolveBrowserExecutable();
140
+ if (existing) return existing;
141
+ if (browserInstallPromise) return browserInstallPromise;
142
+ browserInstallPromise = (async () => {
143
+ const platform = detectBrowserPlatform();
144
+ if (!platform) throw new Error('无法识别当前系统平台');
145
+ await log('未找到采集浏览器,开始下载 Chromium(Chrome for Testing)');
146
+ const buildId = await resolveBuildId(BrowserType.CHROME, platform, 'stable');
147
+ const installed = await installBrowserBinary({
148
+ browser: BrowserType.CHROME,
149
+ buildId,
150
+ cacheDir: path.join(homeDir(), 'pomelo', 'browser-cache'),
151
+ platform,
152
+ });
153
+ const executable = installed?.executablePath || '';
154
+ if (!executable) throw new Error('浏览器下载完成,但没有找到可执行文件');
155
+ process.env.PUPPETEER_EXECUTABLE_PATH = executable;
156
+ await log(`Chromium 下载完成:${executable}`);
157
+ return { executable, source: 'downloaded' };
158
+ })();
159
+ try {
160
+ return await browserInstallPromise;
161
+ } finally {
162
+ browserInstallPromise = null;
163
+ }
164
+ }
165
+
109
166
  async function resolveBrowserExecutable() {
110
167
  const explicit = process.env.PUPPETEER_EXECUTABLE_PATH || process.env.CHROME_PATH;
111
168
  if (explicit && await executableAt(explicit)) {
@@ -119,8 +176,12 @@ async function resolveBrowserExecutable() {
119
176
  if (chromium) {
120
177
  return { executable: chromium, source: 'playwright-chromium' };
121
178
  }
179
+ const cached = await findCachedChromium();
180
+ if (cached) {
181
+ return { executable: cached, source: 'downloaded' };
182
+ }
122
183
  try {
123
- const bundled = await puppeteer.executablePath();
184
+ const bundled = typeof puppeteer.executablePath === 'function' ? await puppeteer.executablePath() : '';
124
185
  if (bundled && await executableAt(bundled)) {
125
186
  return { executable: bundled, source: 'puppeteer' };
126
187
  }
package/host/index.js CHANGED
@@ -9,7 +9,7 @@ import { promisify } from 'node:util';
9
9
  import fs from 'node:fs/promises';
10
10
  import os from 'node:os';
11
11
  import path from 'node:path';
12
- import { browserStatus, openLink, openLogin, searchPeerProfile, searchPublic } from './browser.js';
12
+ import { browserStatus, ensureBrowser, openLink, openLogin, searchPeerProfile, searchPublic } from './browser.js';
13
13
 
14
14
  const execFileAsync = promisify(execFile);
15
15
 
@@ -564,21 +564,14 @@ ${comparisonText}
564
564
  async function installBrowser() {
565
565
  const first = await browserStatus();
566
566
  if (first.installed) return first;
567
- const pluginRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..');
568
- await store.log('开始安装 Chromium(puppeteer)');
569
567
  try {
570
- await execFileAsync('npx', ['puppeteer', 'browsers', 'install', 'chrome'], {
571
- cwd: pluginRoot,
572
- timeout: 15 * 60 * 1000,
573
- maxBuffer: 4 * 1024 * 1024,
574
- env: process.env,
575
- });
568
+ await ensureBrowser((message) => store.log(message));
576
569
  } catch (error) {
577
- await store.log(`Chromium 安装失败:${error?.message || error}`);
578
- throw rpcError('BROWSER_INSTALL_FAILED', 'Chromium 安装失败,请在项目目录运行 npm run browser:install 后重试');
570
+ await store.log(`Chromium 下载失败:${error?.message || error}`);
571
+ throw rpcError('BROWSER_INSTALL_FAILED', '采集浏览器下载失败,请检查网络后重试');
579
572
  }
580
573
  const next = await browserStatus();
581
- await store.log(`Chromium 安装完成:${next.executable || '未找到路径'}`);
574
+ await store.log(`Chromium 就绪:${next.executable || '未找到路径'}`);
582
575
  return next;
583
576
  }
584
577
 
@@ -1214,12 +1207,15 @@ ${state.profile.answers[key] || '(未填写)'}
1214
1207
  case 'browser.status':
1215
1208
  return browserStatus();
1216
1209
  case 'browser.login':
1210
+ await installBrowser();
1217
1211
  return openLogin(payload?.platform || '小红书');
1218
1212
  case 'browser.install':
1219
1213
  return installBrowser();
1220
1214
  case 'browser.open':
1215
+ await installBrowser();
1221
1216
  return openLink(payload?.url);
1222
1217
  case 'search.run': {
1218
+ await installBrowser();
1223
1219
  const platform = payload?.platform === '抖音' ? '抖音' : '小红书';
1224
1220
  const keyword = cleanText(payload?.keyword, 80);
1225
1221
  if (!keyword) throw rpcError('EMPTY_KEYWORD', '请输入关键词');
@@ -1241,6 +1237,7 @@ ${state.profile.answers[key] || '(未填写)'}
1241
1237
  case 'peer.history':
1242
1238
  return store.snapshot().peerHistory || [];
1243
1239
  case 'peer.run': {
1240
+ await installBrowser();
1244
1241
  const platform = payload?.platform === '抖音' ? '抖音' : '小红书';
1245
1242
  const account = cleanText(payload?.account, 120);
1246
1243
  if (!account) throw rpcError('EMPTY_ACCOUNT', '先粘贴同行账号或 ID');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pomelolabs/leads",
3
- "version": "0.1.5",
3
+ "version": "0.2.2",
4
4
  "description": "柚客 Pomelo:内容获客工作台(DSH 插件)",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -33,8 +33,7 @@
33
33
  ],
34
34
  "scripts": {
35
35
  "build": "esbuild client/src/index.jsx --bundle --format=iife --outfile=client/bundle.js --loader:.css=text --loader:.svg=text --jsx=transform --external:react --target=es2022 --log-level=warning",
36
- "dev": "npm run build -- --watch",
37
- "browser:install": "node ./node_modules/puppeteer/lib/puppeteer/node/cli.js browsers install chrome"
36
+ "dev": "npm run build -- --watch"
38
37
  },
39
38
  "devDependencies": {
40
39
  "@phosphor-icons/core": "^2.1.1",
@@ -54,7 +53,8 @@
54
53
  },
55
54
  "dependencies": {
56
55
  "playwright-core": "^1.64.1",
57
- "puppeteer": "^25.16.0"
56
+ "puppeteer-core": "^25.16.0",
57
+ "@puppeteer/browsers": "^3.2.6"
58
58
  },
59
59
  "publishConfig": {
60
60
  "access": "public",