@pomelolabs/leads 0.2.1 → 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.2.1",
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",
package/host/browser.js CHANGED
@@ -11,6 +11,7 @@ import fs from 'node:fs/promises';
11
11
  import os from 'node:os';
12
12
  import path from 'node:path';
13
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,6 +176,10 @@ 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
184
  const bundled = typeof puppeteer.executablePath === 'function' ? await puppeteer.executablePath() : '';
124
185
  if (bundled && await executableAt(bundled)) {
package/host/index.js CHANGED
@@ -9,8 +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 { Browser as BrowserType, detectBrowserPlatform, install as installBrowserBinary, resolveBuildId } from '@puppeteer/browsers';
13
- import { browserStatus, openLink, openLogin, searchPeerProfile, searchPublic } from './browser.js';
12
+ import { browserStatus, ensureBrowser, openLink, openLogin, searchPeerProfile, searchPublic } from './browser.js';
14
13
 
15
14
  const execFileAsync = promisify(execFile);
16
15
 
@@ -565,23 +564,14 @@ ${comparisonText}
565
564
  async function installBrowser() {
566
565
  const first = await browserStatus();
567
566
  if (first.installed) return first;
568
- await store.log('开始安装 Chrome(@puppeteer/browsers,无 postinstall)');
569
567
  try {
570
- const platform = detectBrowserPlatform();
571
- if (!platform) throw new Error('无法识别当前系统平台');
572
- const buildId = await resolveBuildId(BrowserType.CHROME, platform, 'stable');
573
- await installBrowserBinary({
574
- browser: BrowserType.CHROME,
575
- buildId,
576
- cacheDir: path.join(os.homedir(), '.cache', 'puppeteer'),
577
- platform,
578
- });
568
+ await ensureBrowser((message) => store.log(message));
579
569
  } catch (error) {
580
- await store.log(`Chrome 安装失败:${error?.message || error}`);
581
- throw rpcError('BROWSER_INSTALL_FAILED', '采集浏览器安装失败,请手动安装 Chrome 或 Edge 后重试');
570
+ await store.log(`Chromium 下载失败:${error?.message || error}`);
571
+ throw rpcError('BROWSER_INSTALL_FAILED', '采集浏览器下载失败,请检查网络后重试');
582
572
  }
583
573
  const next = await browserStatus();
584
- await store.log(`Chrome 安装完成:${next.executable || '未找到路径'}`);
574
+ await store.log(`Chromium 就绪:${next.executable || '未找到路径'}`);
585
575
  return next;
586
576
  }
587
577
 
@@ -1217,12 +1207,15 @@ ${state.profile.answers[key] || '(未填写)'}
1217
1207
  case 'browser.status':
1218
1208
  return browserStatus();
1219
1209
  case 'browser.login':
1210
+ await installBrowser();
1220
1211
  return openLogin(payload?.platform || '小红书');
1221
1212
  case 'browser.install':
1222
1213
  return installBrowser();
1223
1214
  case 'browser.open':
1215
+ await installBrowser();
1224
1216
  return openLink(payload?.url);
1225
1217
  case 'search.run': {
1218
+ await installBrowser();
1226
1219
  const platform = payload?.platform === '抖音' ? '抖音' : '小红书';
1227
1220
  const keyword = cleanText(payload?.keyword, 80);
1228
1221
  if (!keyword) throw rpcError('EMPTY_KEYWORD', '请输入关键词');
@@ -1244,6 +1237,7 @@ ${state.profile.answers[key] || '(未填写)'}
1244
1237
  case 'peer.history':
1245
1238
  return store.snapshot().peerHistory || [];
1246
1239
  case 'peer.run': {
1240
+ await installBrowser();
1247
1241
  const platform = payload?.platform === '抖音' ? '抖音' : '小红书';
1248
1242
  const account = cleanText(payload?.account, 120);
1249
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.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "柚客 Pomelo:内容获客工作台(DSH 插件)",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",