openyida 2026.4.2-beta.7 → 2026.4.2-beta.8
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/lib/auth/login.js +6 -8
- package/lib/core/doctor.js +8 -57
- package/package.json +1 -1
- package/yida-skills/SKILL.md +1 -1
- package/lib/auth/login-cdp.js +0 -315
package/lib/auth/login.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* login.js -
|
|
2
|
+
* login.js - 宜搭登录态管理(Playwright 扫码登录)
|
|
3
3
|
*
|
|
4
4
|
* 登录策略(按优先级):
|
|
5
5
|
* 1. 本地 Cookie 缓存(最快)
|
|
6
|
-
* 2.
|
|
7
|
-
* 3. Playwright 内置 Chromium
|
|
6
|
+
* 2. 本地 Chrome(channel: 'chrome')
|
|
7
|
+
* 3. Playwright 内置 Chromium(兜底)
|
|
8
8
|
*
|
|
9
9
|
* 导出函数:
|
|
10
|
-
* ensureLogin() -
|
|
10
|
+
* ensureLogin() - 确保有效登录态(优先缓存,否则扫码)
|
|
11
11
|
* checkLoginOnly() - 仅检查登录态,不触发登录
|
|
12
12
|
* refreshCsrfFromCache() - 从缓存 Cookie 重新提取 csrf_token
|
|
13
|
-
* interactiveLogin() -
|
|
13
|
+
* interactiveLogin() - 打开浏览器扫码登录(需要 playwright)
|
|
14
14
|
* saveCookieCache() - 保存 Cookie 到本地缓存(供 qr-login.js 使用)
|
|
15
15
|
* logout() - 退出登录,清空 Cookie 缓存
|
|
16
16
|
*/
|
|
@@ -156,9 +156,7 @@ function ensureLogin() {
|
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
|
|
160
|
-
const { loginWithCdpOrPlaywright } = require('./login-cdp');
|
|
161
|
-
return loginWithCdpOrPlaywright({ loginUrl: loadConfig().loginUrl });
|
|
159
|
+
return interactiveLogin();
|
|
162
160
|
}
|
|
163
161
|
|
|
164
162
|
// ── Playwright 扫码登录 ───────────────────────────────
|
package/lib/core/doctor.js
CHANGED
|
@@ -195,9 +195,15 @@ class EnvironmentChecker {
|
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
checkPlaywrightInstalled() {
|
|
198
|
-
// 1. 检查 playwright npm 包是否安装
|
|
199
198
|
try {
|
|
200
199
|
require.resolve('playwright');
|
|
200
|
+
return {
|
|
201
|
+
id: 'env-playwright',
|
|
202
|
+
label: 'Playwright 已安装',
|
|
203
|
+
passed: true,
|
|
204
|
+
severity: Severity.INFO,
|
|
205
|
+
fixType: null,
|
|
206
|
+
};
|
|
201
207
|
} catch {
|
|
202
208
|
return {
|
|
203
209
|
id: 'env-playwright',
|
|
@@ -206,40 +212,7 @@ class EnvironmentChecker {
|
|
|
206
212
|
severity: Severity.ERROR,
|
|
207
213
|
message: 'Playwright 未安装',
|
|
208
214
|
fixType: FixType.COMMAND,
|
|
209
|
-
fixCommand: 'npm install playwright
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// 2. 检查 Chromium 二进制是否存在于用户目录(postinstall 已自动安装)
|
|
214
|
-
try {
|
|
215
|
-
const { chromium } = require('playwright');
|
|
216
|
-
const chromiumPath = chromium.executablePath();
|
|
217
|
-
if (chromiumPath && fs.existsSync(chromiumPath)) {
|
|
218
|
-
return {
|
|
219
|
-
id: 'env-playwright',
|
|
220
|
-
label: `Playwright + Chromium 已就绪`,
|
|
221
|
-
passed: true,
|
|
222
|
-
severity: Severity.INFO,
|
|
223
|
-
fixType: null,
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
return {
|
|
227
|
-
id: 'env-playwright',
|
|
228
|
-
label: 'Chromium 浏览器检测',
|
|
229
|
-
passed: false,
|
|
230
|
-
severity: Severity.ERROR,
|
|
231
|
-
message: `Chromium 未安装(期望路径:${chromiumPath || '未知'})`,
|
|
232
|
-
fixType: FixType.AUTO,
|
|
233
|
-
fixAction: 'install-chromium',
|
|
234
|
-
};
|
|
235
|
-
} catch {
|
|
236
|
-
// playwright API 不可用,降级为仅检查包安装
|
|
237
|
-
return {
|
|
238
|
-
id: 'env-playwright',
|
|
239
|
-
label: 'Playwright 已安装',
|
|
240
|
-
passed: true,
|
|
241
|
-
severity: Severity.INFO,
|
|
242
|
-
fixType: null,
|
|
215
|
+
fixCommand: 'npm install playwright',
|
|
243
216
|
};
|
|
244
217
|
}
|
|
245
218
|
}
|
|
@@ -742,28 +715,6 @@ class FixEngine {
|
|
|
742
715
|
};
|
|
743
716
|
}
|
|
744
717
|
|
|
745
|
-
case 'install-chromium': {
|
|
746
|
-
try {
|
|
747
|
-
const { execSync } = require('child_process');
|
|
748
|
-
console.log('\n 🌐 正在安装 Chromium 浏览器(请稍候)...');
|
|
749
|
-
execSync('npx playwright install chromium', {
|
|
750
|
-
stdio: 'inherit',
|
|
751
|
-
timeout: 300_000,
|
|
752
|
-
});
|
|
753
|
-
return {
|
|
754
|
-
id: issue.id,
|
|
755
|
-
fixed: true,
|
|
756
|
-
message: 'Chromium 安装完成',
|
|
757
|
-
};
|
|
758
|
-
} catch (error) {
|
|
759
|
-
return {
|
|
760
|
-
id: issue.id,
|
|
761
|
-
fixed: false,
|
|
762
|
-
message: `Chromium 安装失败,请手动运行:npx playwright install chromium(${error.message})`,
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
|
|
767
718
|
case 'init-project': {
|
|
768
719
|
try {
|
|
769
720
|
const copy = require('./copy');
|
package/package.json
CHANGED
package/yida-skills/SKILL.md
CHANGED
package/lib/auth/login-cdp.js
DELETED
|
@@ -1,315 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* login-cdp.js - 优先 CDP 协议登录,降级 Playwright 扫码登录
|
|
3
|
-
*
|
|
4
|
-
* 登录策略(按优先级):
|
|
5
|
-
* 1. CDP 模式:启动本地已安装的 Chrome,通过 CDP 协议连接,无需下载 Chromium
|
|
6
|
-
* 2. Playwright 模式:CDP 不可用时,降级为 playwright 内置 Chromium 扫码登录
|
|
7
|
-
*
|
|
8
|
-
* 导出函数:
|
|
9
|
-
* loginWithCdpOrPlaywright() - 执行完整登录流程(CDP 优先)
|
|
10
|
-
* findLocalChrome() - 查找本地 Chrome 可执行文件路径
|
|
11
|
-
* launchChromeWithCdp() - 启动 Chrome 并开启 CDP 调试端口
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
'use strict';
|
|
15
|
-
|
|
16
|
-
const fs = require('fs');
|
|
17
|
-
const path = require('path');
|
|
18
|
-
const os = require('os');
|
|
19
|
-
const { execSync, spawn } = require('child_process');
|
|
20
|
-
const { extractInfoFromCookies } = require('../core/utils');
|
|
21
|
-
const { saveCookieCache, interactiveLogin } = require('./login');
|
|
22
|
-
const { t } = require('../core/i18n');
|
|
23
|
-
|
|
24
|
-
const DEFAULT_LOGIN_URL = 'https://www.aliwork.com/workPlatform';
|
|
25
|
-
const CDP_PORT = 19222;
|
|
26
|
-
const CDP_CONNECT_TIMEOUT_MS = 8000;
|
|
27
|
-
const LOGIN_POLL_INTERVAL_MS = 2000;
|
|
28
|
-
const LOGIN_TIMEOUT_MS = 600_000; // 10 分钟
|
|
29
|
-
|
|
30
|
-
// ── 本地 Chrome 路径查找 ──────────────────────────────
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* 按平台查找本地已安装的 Chrome / Chromium 可执行文件。
|
|
34
|
-
* @returns {string|null} 可执行文件路径,找不到返回 null
|
|
35
|
-
*/
|
|
36
|
-
function findLocalChrome() {
|
|
37
|
-
const platform = os.platform();
|
|
38
|
-
|
|
39
|
-
const candidates = {
|
|
40
|
-
darwin: [
|
|
41
|
-
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
42
|
-
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
|
43
|
-
'/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',
|
|
44
|
-
path.join(os.homedir(), 'Applications/Google Chrome.app/Contents/MacOS/Google Chrome'),
|
|
45
|
-
],
|
|
46
|
-
win32: [
|
|
47
|
-
'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
|
|
48
|
-
'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
|
|
49
|
-
path.join(os.homedir(), 'AppData\\Local\\Google\\Chrome\\Application\\chrome.exe'),
|
|
50
|
-
],
|
|
51
|
-
linux: [
|
|
52
|
-
'/usr/bin/google-chrome',
|
|
53
|
-
'/usr/bin/google-chrome-stable',
|
|
54
|
-
'/usr/bin/chromium-browser',
|
|
55
|
-
'/usr/bin/chromium',
|
|
56
|
-
'/snap/bin/chromium',
|
|
57
|
-
],
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const platformCandidates = candidates[platform] || candidates.linux;
|
|
61
|
-
for (const chromePath of platformCandidates) {
|
|
62
|
-
if (fs.existsSync(chromePath)) {
|
|
63
|
-
return chromePath;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// 尝试从 PATH 中查找
|
|
68
|
-
try {
|
|
69
|
-
const whichResult = execSync('which google-chrome || which chromium-browser || which chromium', {
|
|
70
|
-
encoding: 'utf-8',
|
|
71
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
72
|
-
}).trim().split('\n')[0];
|
|
73
|
-
if (whichResult && fs.existsSync(whichResult)) {
|
|
74
|
-
return whichResult;
|
|
75
|
-
}
|
|
76
|
-
} catch {
|
|
77
|
-
// 找不到,忽略
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// ── CDP 连接工具 ──────────────────────────────────────
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* 等待 CDP 调试端口就绪(轮询 /json/version)。
|
|
87
|
-
* @param {number} port
|
|
88
|
-
* @param {number} timeoutMs
|
|
89
|
-
* @returns {Promise<boolean>}
|
|
90
|
-
*/
|
|
91
|
-
function waitForCdpPort(port, timeoutMs) {
|
|
92
|
-
return new Promise((resolve) => {
|
|
93
|
-
const deadline = Date.now() + timeoutMs;
|
|
94
|
-
const http = require('http');
|
|
95
|
-
|
|
96
|
-
function poll() {
|
|
97
|
-
if (Date.now() > deadline) {
|
|
98
|
-
resolve(false);
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
const req = http.get(`http://127.0.0.1:${port}/json/version`, (res) => {
|
|
102
|
-
resolve(res.statusCode === 200);
|
|
103
|
-
});
|
|
104
|
-
req.on('error', () => {
|
|
105
|
-
setTimeout(poll, 300);
|
|
106
|
-
});
|
|
107
|
-
req.setTimeout(500, () => {
|
|
108
|
-
req.destroy();
|
|
109
|
-
setTimeout(poll, 300);
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
poll();
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* 启动本地 Chrome 并开启 CDP 调试端口。
|
|
119
|
-
* @param {string} chromePath - Chrome 可执行文件路径
|
|
120
|
-
* @param {string} loginUrl - 登录页 URL
|
|
121
|
-
* @returns {{ process: ChildProcess, userDataDir: string } | null}
|
|
122
|
-
*/
|
|
123
|
-
function launchChromeWithCdp(chromePath, loginUrl) {
|
|
124
|
-
const userDataDir = path.join(os.tmpdir(), `yidacli-chrome-${Date.now()}`);
|
|
125
|
-
fs.mkdirSync(userDataDir, { recursive: true });
|
|
126
|
-
|
|
127
|
-
const args = [
|
|
128
|
-
`--remote-debugging-port=${CDP_PORT}`,
|
|
129
|
-
`--user-data-dir=${userDataDir}`,
|
|
130
|
-
'--no-first-run',
|
|
131
|
-
'--no-default-browser-check',
|
|
132
|
-
'--disable-default-apps',
|
|
133
|
-
loginUrl,
|
|
134
|
-
];
|
|
135
|
-
|
|
136
|
-
try {
|
|
137
|
-
const chromeProcess = spawn(chromePath, args, {
|
|
138
|
-
detached: false,
|
|
139
|
-
stdio: 'ignore',
|
|
140
|
-
});
|
|
141
|
-
return { process: chromeProcess, userDataDir };
|
|
142
|
-
} catch {
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// ── Cookie 轮询等待 ───────────────────────────────────
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* 通过 playwright CDP 连接,轮询等待 tianshu_csrf_token Cookie 出现。
|
|
151
|
-
* @param {object} context - playwright BrowserContext
|
|
152
|
-
* @returns {Promise<{ cookies: Array, baseUrl: string } | null>}
|
|
153
|
-
*/
|
|
154
|
-
async function pollForLoginCookies(context) {
|
|
155
|
-
const deadline = Date.now() + LOGIN_TIMEOUT_MS;
|
|
156
|
-
|
|
157
|
-
while (Date.now() < deadline) {
|
|
158
|
-
await new Promise((resolve) => setTimeout(resolve, LOGIN_POLL_INTERVAL_MS));
|
|
159
|
-
|
|
160
|
-
const cookies = await context.cookies();
|
|
161
|
-
const csrfCookie = cookies.find((cookie) => cookie.name === 'tianshu_csrf_token' && cookie.value);
|
|
162
|
-
|
|
163
|
-
if (!csrfCookie) {
|
|
164
|
-
continue;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// 提取 baseUrl(优先用 yida_user_cookie 的 domain)
|
|
168
|
-
let baseUrl = 'https://www.aliwork.com';
|
|
169
|
-
const yidaCookie = cookies.find((cookie) => cookie.name === 'yida_user_cookie');
|
|
170
|
-
if (yidaCookie && yidaCookie.domain && yidaCookie.domain.includes('aliwork.com')) {
|
|
171
|
-
baseUrl = 'https://' + yidaCookie.domain.replace(/^\./, '');
|
|
172
|
-
} else if (csrfCookie.domain && csrfCookie.domain !== '.aliwork.com') {
|
|
173
|
-
baseUrl = 'https://' + csrfCookie.domain.replace(/^\./, '');
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return { cookies, baseUrl };
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// ── CDP 登录主流程 ────────────────────────────────────
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* 尝试通过 CDP 协议连接本地 Chrome 完成登录。
|
|
186
|
-
* @param {string} loginUrl
|
|
187
|
-
* @returns {Promise<object|null>} loginResult 或 null(失败时)
|
|
188
|
-
*/
|
|
189
|
-
async function loginViaCdp(loginUrl) {
|
|
190
|
-
const chromePath = findLocalChrome();
|
|
191
|
-
if (!chromePath) {
|
|
192
|
-
console.error(' ℹ️ 未找到本地 Chrome,将使用 Playwright 内置 Chromium');
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
console.error(` 🌐 检测到本地 Chrome:${chromePath}`);
|
|
197
|
-
console.error(` 🔗 正在通过 CDP 协议连接(端口 ${CDP_PORT})...`);
|
|
198
|
-
|
|
199
|
-
// 检查端口是否已被占用(Chrome 已在运行)
|
|
200
|
-
const portAlreadyOpen = await waitForCdpPort(CDP_PORT, 500);
|
|
201
|
-
let chromeProcess = null;
|
|
202
|
-
let userDataDir = null;
|
|
203
|
-
|
|
204
|
-
if (!portAlreadyOpen) {
|
|
205
|
-
const launched = launchChromeWithCdp(chromePath, loginUrl);
|
|
206
|
-
if (!launched) {
|
|
207
|
-
console.error(' ⚠️ Chrome 启动失败,降级为 Playwright 模式');
|
|
208
|
-
return null;
|
|
209
|
-
}
|
|
210
|
-
chromeProcess = launched.process;
|
|
211
|
-
userDataDir = launched.userDataDir;
|
|
212
|
-
|
|
213
|
-
const ready = await waitForCdpPort(CDP_PORT, CDP_CONNECT_TIMEOUT_MS);
|
|
214
|
-
if (!ready) {
|
|
215
|
-
console.error(' ⚠️ CDP 端口未就绪,降级为 Playwright 模式');
|
|
216
|
-
if (chromeProcess) { chromeProcess.kill(); }
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
} else {
|
|
220
|
-
console.error(' ℹ️ 检测到 Chrome 已在运行,直接连接');
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
let playwright;
|
|
224
|
-
try {
|
|
225
|
-
playwright = require('playwright');
|
|
226
|
-
} catch {
|
|
227
|
-
console.error(' ⚠️ playwright 未安装,降级为扫码模式');
|
|
228
|
-
if (chromeProcess) { chromeProcess.kill(); }
|
|
229
|
-
return null;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
let browser = null;
|
|
233
|
-
try {
|
|
234
|
-
browser = await playwright.chromium.connectOverCDP(`http://127.0.0.1:${CDP_PORT}`);
|
|
235
|
-
const contexts = browser.contexts();
|
|
236
|
-
const context = contexts.length > 0 ? contexts[0] : await browser.newContext();
|
|
237
|
-
|
|
238
|
-
// 导航到登录页(如果当前页面不是登录页)
|
|
239
|
-
const pages = context.pages();
|
|
240
|
-
const loginPage = pages.length > 0 ? pages[0] : await context.newPage();
|
|
241
|
-
const currentUrl = loginPage.url();
|
|
242
|
-
if (!currentUrl.includes('aliwork.com')) {
|
|
243
|
-
await loginPage.goto(loginUrl, { timeout: 30000 });
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
console.error(' ⏳ 请在浏览器中完成宜搭登录(最多等待 10 分钟)...');
|
|
247
|
-
|
|
248
|
-
const result = await pollForLoginCookies(context);
|
|
249
|
-
if (!result) {
|
|
250
|
-
console.error(' ⏰ 登录超时(10 分钟),请重试');
|
|
251
|
-
return null;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
console.error(' ✅ CDP 登录成功!');
|
|
255
|
-
return result;
|
|
256
|
-
} catch (error) {
|
|
257
|
-
console.error(` ⚠️ CDP 连接失败(${error.message}),降级为 Playwright 模式`);
|
|
258
|
-
return null;
|
|
259
|
-
} finally {
|
|
260
|
-
if (browser) {
|
|
261
|
-
try { await browser.disconnect(); } catch { /* ignore */ }
|
|
262
|
-
}
|
|
263
|
-
if (chromeProcess) {
|
|
264
|
-
try { chromeProcess.kill(); } catch { /* ignore */ }
|
|
265
|
-
}
|
|
266
|
-
if (userDataDir) {
|
|
267
|
-
try { fs.rmSync(userDataDir, { recursive: true, force: true }); } catch { /* ignore */ }
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// ── 对外入口 ──────────────────────────────────────────
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* 登录主入口:优先 CDP 连接本地 Chrome,失败时降级为 Playwright 扫码登录。
|
|
276
|
-
*
|
|
277
|
-
* @param {object} [options]
|
|
278
|
-
* @param {string} [options.loginUrl] - 登录页 URL,默认读取 config.json
|
|
279
|
-
* @returns {object} loginResult { csrf_token, corp_id, user_id, base_url, cookies }
|
|
280
|
-
*/
|
|
281
|
-
async function loginWithCdpOrPlaywright(options = {}) {
|
|
282
|
-
const loginUrl = options.loginUrl || DEFAULT_LOGIN_URL;
|
|
283
|
-
|
|
284
|
-
console.error('\n🔐 正在打开浏览器,请扫码登录...');
|
|
285
|
-
|
|
286
|
-
// 策略 1:CDP 连接本地 Chrome
|
|
287
|
-
const cdpResult = await loginViaCdp(loginUrl);
|
|
288
|
-
if (cdpResult) {
|
|
289
|
-
const { csrfToken, corpId, userId } = extractInfoFromCookies(cdpResult.cookies);
|
|
290
|
-
if (!csrfToken) {
|
|
291
|
-
console.error(' ⚠️ CDP 登录后未找到 csrf_token,降级为 Playwright 模式');
|
|
292
|
-
} else {
|
|
293
|
-
saveCookieCache(cdpResult.cookies, cdpResult.baseUrl);
|
|
294
|
-
console.error(` 🔑 csrf_token: ${csrfToken.slice(0, 16)}...`);
|
|
295
|
-
if (corpId) { console.error(` 🏢 组织 ID: ${corpId}`); }
|
|
296
|
-
return {
|
|
297
|
-
csrf_token: csrfToken,
|
|
298
|
-
corp_id: corpId,
|
|
299
|
-
user_id: userId,
|
|
300
|
-
base_url: cdpResult.baseUrl,
|
|
301
|
-
cookies: cdpResult.cookies,
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// 策略 2:降级为 Playwright 扫码登录
|
|
307
|
-
console.error(' 📱 使用 Playwright 内置 Chromium 登录...');
|
|
308
|
-
return interactiveLogin();
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
module.exports = {
|
|
312
|
-
loginWithCdpOrPlaywright,
|
|
313
|
-
findLocalChrome,
|
|
314
|
-
launchChromeWithCdp,
|
|
315
|
-
};
|