@melaya/runner 1.0.69 → 1.0.70
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/dist/linkedinLogin.js +41 -15
- package/dist/lumaBrowserBridge.js +21 -7
- package/dist/lumaLogin.js +36 -10
- package/package.json +6 -6
package/dist/linkedinLogin.js
CHANGED
|
@@ -143,26 +143,52 @@ export async function runLinkedInLoginFlow(progress) {
|
|
|
143
143
|
catch (e) {
|
|
144
144
|
return { ok: false, error: "playwright_unavailable", detail: e?.message };
|
|
145
145
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
146
|
+
// 2. Launch a headed browser window. Prefer the user's REAL system Chrome
|
|
147
|
+
// (channel:"chrome") — validated 2026-06-22 as the only reliable way to
|
|
148
|
+
// render LinkedIn's login page (the bundled Chromium served a blank /
|
|
149
|
+
// never-painting page; system Chrome renders the form, accepts password
|
|
150
|
+
// + 2FA, and reaches /feed/). System Chrome also needs no 140 MB download.
|
|
151
|
+
// Fall back to bundled Chromium only if Chrome isn't installed.
|
|
152
|
+
// Fresh context (no profile reuse) so the user always sees a clean login.
|
|
153
|
+
//
|
|
154
|
+
// We KEEP the fixed Chrome/127 UA override below: LinkedIn's anti-bot binds
|
|
155
|
+
// cookie issuance to the client fingerprint (UA among others), and every
|
|
156
|
+
// Voyager call in shared/tools/social_linkedin.py sends that exact UA
|
|
157
|
+
// constant. Minting li_at/JSESSIONID under a DIFFERENT UA than the tools
|
|
158
|
+
// later replay triggers a redirect-loop on /voyager/api/me
|
|
159
|
+
// (aiohttp.TooManyRedirects) — verified 2026-06-22 when an interim
|
|
160
|
+
// native-UA attempt broke list_connections / search / resolve_profile /
|
|
161
|
+
// list_conversations while session_status still worked. Keep this string
|
|
162
|
+
// in lock-step with social_linkedin.py USER_AGENT.
|
|
155
163
|
let browser = null;
|
|
156
164
|
try {
|
|
157
165
|
progress("Opening LinkedIn login window…");
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
"
|
|
162
|
-
|
|
163
|
-
|
|
166
|
+
try {
|
|
167
|
+
browser = await playwright.chromium.launch({
|
|
168
|
+
headless: false,
|
|
169
|
+
channel: "chrome",
|
|
170
|
+
args: ["--disable-blink-features=AutomationControlled"],
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
catch (chromeErr) {
|
|
174
|
+
progress(`System Chrome not available (${(chromeErr?.message || chromeErr).toString().split("\n")[0]}); ` +
|
|
175
|
+
`falling back to the bundled browser.`);
|
|
176
|
+
try {
|
|
177
|
+
await _installChromium(progress);
|
|
178
|
+
}
|
|
179
|
+
catch (e) {
|
|
180
|
+
return { ok: false, error: "chromium_install_failed", detail: e?.message };
|
|
181
|
+
}
|
|
182
|
+
browser = await playwright.chromium.launch({
|
|
183
|
+
headless: false,
|
|
184
|
+
args: ["--disable-blink-features=AutomationControlled"],
|
|
185
|
+
});
|
|
186
|
+
}
|
|
164
187
|
_activeLoginBrowser = browser;
|
|
165
188
|
const context = await browser.newContext({
|
|
189
|
+
// MUST stay byte-for-byte equal to shared/tools/social_linkedin.py
|
|
190
|
+
// USER_AGENT (Chrome/127). See the fingerprint-binding note above —
|
|
191
|
+
// a mismatch causes aiohttp.TooManyRedirects on Voyager endpoints.
|
|
166
192
|
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " +
|
|
167
193
|
"(KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
|
|
168
194
|
viewport: { width: 1280, height: 820 },
|
|
@@ -128,13 +128,23 @@ export async function startLumaBrowserBridge(opts) {
|
|
|
128
128
|
if (browserLaunching)
|
|
129
129
|
return browserLaunching;
|
|
130
130
|
browserLaunching = (async () => {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
131
|
+
// Prefer the user's real system Chrome (channel:"chrome"). Its TLS/JA3
|
|
132
|
+
// fingerprint matches the headed login that minted cf_clearance, so the
|
|
133
|
+
// Cloudflare-gated POST /event/register is far more likely to pass — and
|
|
134
|
+
// it skips the 140 MB bundled-Chromium download. Fall back to bundled
|
|
135
|
+
// Chromium when Chrome isn't installed.
|
|
136
|
+
const launchArgs = [
|
|
137
|
+
"--disable-blink-features=AutomationControlled",
|
|
138
|
+
"--disable-dev-shm-usage",
|
|
139
|
+
];
|
|
140
|
+
let b;
|
|
141
|
+
try {
|
|
142
|
+
b = await playwright.chromium.launch({ headless: true, channel: "chrome", args: launchArgs });
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
opts.log("Luma browser bridge: system Chrome unavailable, using bundled Chromium.");
|
|
146
|
+
b = await playwright.chromium.launch({ headless: true, args: launchArgs });
|
|
147
|
+
}
|
|
138
148
|
browser = b;
|
|
139
149
|
browserLaunching = null;
|
|
140
150
|
return b;
|
|
@@ -168,6 +178,10 @@ export async function startLumaBrowserBridge(opts) {
|
|
|
168
178
|
const storageState = await _readStorageState();
|
|
169
179
|
ctx = await b.newContext({
|
|
170
180
|
storageState: storageState,
|
|
181
|
+
// MUST stay in lock-step with lumaLogin.ts and shared/tools/luma.py
|
|
182
|
+
// USER_AGENT. cf_clearance is UA-bound: it was minted under this exact
|
|
183
|
+
// string at login, so every replay (here + the direct aiohttp reads)
|
|
184
|
+
// must present the same UA or Cloudflare rejects it with a 403.
|
|
171
185
|
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " +
|
|
172
186
|
"(KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36",
|
|
173
187
|
viewport: { width: 1280, height: 820 },
|
package/dist/lumaLogin.js
CHANGED
|
@@ -112,21 +112,47 @@ export async function runLumaLoginFlow(progress) {
|
|
|
112
112
|
catch (e) {
|
|
113
113
|
return { ok: false, error: "playwright_unavailable", detail: e?.message };
|
|
114
114
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
115
|
+
// Prefer the user's REAL system Chrome (channel:"chrome"). Validated for the
|
|
116
|
+
// LinkedIn login flow 2026-06-22 as the only reliable way to render a headed
|
|
117
|
+
// provider login (bundled Chromium served a blank page). For Luma it's doubly
|
|
118
|
+
// important: Luma is behind Cloudflare Turnstile and only issues cf_clearance
|
|
119
|
+
// (required for POST /event/register) after a challenge passes — a genuine
|
|
120
|
+
// Chrome clears that far more reliably than bundled Chromium, and it needs no
|
|
121
|
+
// 140 MB download. Fall back to bundled Chromium only if Chrome isn't present.
|
|
122
|
+
//
|
|
123
|
+
// We DO keep the fixed UA override below: cf_clearance is bound to the exact
|
|
124
|
+
// UA that minted it, and the replay paths (lumaBrowserBridge.ts and the direct
|
|
125
|
+
// aiohttp reads in shared/tools/luma.py) both send this same fixed string.
|
|
126
|
+
// Turnstile is solved under this overridden UA, so the binding stays internally
|
|
127
|
+
// consistent across mint + every replay. Keep all three in lock-step.
|
|
121
128
|
let browser = null;
|
|
122
129
|
try {
|
|
123
130
|
progress("Opening Luma login window…");
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
131
|
+
try {
|
|
132
|
+
browser = await playwright.chromium.launch({
|
|
133
|
+
headless: false,
|
|
134
|
+
channel: "chrome",
|
|
135
|
+
args: ["--disable-blink-features=AutomationControlled"],
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
catch (chromeErr) {
|
|
139
|
+
progress(`System Chrome not available (${(chromeErr?.message || chromeErr).toString().split("\n")[0]}); ` +
|
|
140
|
+
`falling back to the bundled browser.`);
|
|
141
|
+
try {
|
|
142
|
+
await _installChromium(progress);
|
|
143
|
+
}
|
|
144
|
+
catch (e) {
|
|
145
|
+
return { ok: false, error: "chromium_install_failed", detail: e?.message };
|
|
146
|
+
}
|
|
147
|
+
browser = await playwright.chromium.launch({
|
|
148
|
+
headless: false,
|
|
149
|
+
args: ["--disable-blink-features=AutomationControlled"],
|
|
150
|
+
});
|
|
151
|
+
}
|
|
128
152
|
_activeLoginBrowser = browser;
|
|
129
153
|
const context = await browser.newContext({
|
|
154
|
+
// MUST stay in lock-step with shared/tools/luma.py USER_AGENT and
|
|
155
|
+
// lumaBrowserBridge.ts — cf_clearance is UA-bound (see comment above).
|
|
130
156
|
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " +
|
|
131
157
|
"(KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36",
|
|
132
158
|
viewport: { width: 1280, height: 820 },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@melaya/runner",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.70",
|
|
4
4
|
"description": "Run Melaya AI pipelines locally with your own LM Studio or Ollama models",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|
|
@@ -22,15 +22,15 @@
|
|
|
22
22
|
"prepublishOnly": "npm run build"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"socket.io-client": "^4.8.0",
|
|
26
|
-
"commander": "^12.0.0",
|
|
27
25
|
"chalk": "^5.3.0",
|
|
26
|
+
"commander": "^12.0.0",
|
|
28
27
|
"ora": "^8.0.0",
|
|
29
|
-
"playwright": "^1.47.0"
|
|
28
|
+
"playwright": "^1.47.0",
|
|
29
|
+
"socket.io-client": "^4.8.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"
|
|
33
|
-
"
|
|
32
|
+
"@types/node": "^20.0.0",
|
|
33
|
+
"typescript": "^5.5.0"
|
|
34
34
|
},
|
|
35
35
|
"engines": {
|
|
36
36
|
"node": ">=18"
|