@lightcone-ai/daemon 0.9.58 → 0.9.60
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.
|
@@ -35,40 +35,20 @@ export class XhsAdapter {
|
|
|
35
35
|
async checkLoginStatus() {
|
|
36
36
|
const profileDir = process.env.XHS_PROFILE_DIR ?? '(not set)';
|
|
37
37
|
|
|
38
|
-
// Navigate to
|
|
39
|
-
|
|
40
|
-
await
|
|
41
|
-
await sleep(4000);
|
|
42
|
-
|
|
43
|
-
const stateResult = await this._cdp.send('Runtime.evaluate', {
|
|
44
|
-
expression: `(function() {
|
|
45
|
-
try {
|
|
46
|
-
const state = window.__INITIAL_STATE__ || {};
|
|
47
|
-
const userInfo = state?.user?.userInfo?._rawValue || state?.user?.userInfo || {};
|
|
48
|
-
const userId = userInfo.userId || userInfo.userID || null;
|
|
49
|
-
const nickname = userInfo.nickname || userInfo.nickName || null;
|
|
50
|
-
return JSON.stringify({ userId, nickname });
|
|
51
|
-
} catch(e) { return JSON.stringify({ error: e.message }); }
|
|
52
|
-
})()`,
|
|
53
|
-
returnByValue: true,
|
|
54
|
-
});
|
|
55
|
-
const stateStr = stateResult.result?.value ?? '{}';
|
|
56
|
-
let stateData = {};
|
|
57
|
-
try { stateData = JSON.parse(stateStr); } catch {}
|
|
38
|
+
// Navigate to creator publish page directly — login was done on creator subdomain
|
|
39
|
+
await this._cdp.send('Page.navigate', { url: 'https://creator.xiaohongshu.com/publish/publish' });
|
|
40
|
+
await sleep(5000);
|
|
58
41
|
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
42
|
+
const urlResult = await this._cdp.send('Runtime.evaluate', { expression: 'window.location.href', returnByValue: true });
|
|
43
|
+
const url = urlResult.result?.value ?? '';
|
|
44
|
+
const loggedIn = url.includes('creator.xiaohongshu.com/publish/publish');
|
|
45
|
+
console.error(`[XhsAdapter] checkLoginStatus: loggedIn=${loggedIn} url=${url}`);
|
|
46
|
+
return { loggedIn, url, profileDir, userId: null, nickname: null };
|
|
62
47
|
}
|
|
63
48
|
|
|
64
49
|
async publishImageText({ title, text, tags = [], images = [] }) {
|
|
65
50
|
const cdp = this._cdp;
|
|
66
51
|
|
|
67
|
-
// Warm up session on www first — ensures session context is established
|
|
68
|
-
// before accessing creator subdomain (same flow as a real browser user)
|
|
69
|
-
await cdp.send('Page.navigate', { url: 'https://www.xiaohongshu.com' });
|
|
70
|
-
await sleep(3000);
|
|
71
|
-
|
|
72
52
|
// Navigate to publish page
|
|
73
53
|
await cdp.send('Page.navigate', { url: 'https://creator.xiaohongshu.com/publish/publish' });
|
|
74
54
|
await sleep(4000);
|
|
@@ -138,9 +118,6 @@ export class XhsAdapter {
|
|
|
138
118
|
async publishShortVideo({ title, text, tags = [], video, cover }) {
|
|
139
119
|
const cdp = this._cdp;
|
|
140
120
|
|
|
141
|
-
await cdp.send('Page.navigate', { url: 'https://www.xiaohongshu.com' });
|
|
142
|
-
await sleep(3000);
|
|
143
|
-
|
|
144
121
|
await cdp.send('Page.navigate', { url: 'https://creator.xiaohongshu.com/publish/publish?source=video' });
|
|
145
122
|
await sleep(4000);
|
|
146
123
|
|
|
@@ -152,13 +152,13 @@ server.tool(
|
|
|
152
152
|
const label = PLATFORM_LABELS[platform] ?? platform;
|
|
153
153
|
try {
|
|
154
154
|
const adapter = await getAdapter(platform);
|
|
155
|
-
const { loggedIn, url, profileDir
|
|
155
|
+
const { loggedIn, url, profileDir } = await adapter.checkLoginStatus();
|
|
156
156
|
if (loggedIn) {
|
|
157
|
-
return { content: [{ type: 'text', text: `✓ ${label}
|
|
157
|
+
return { content: [{ type: 'text', text: `✓ ${label} 已登录,可以发布内容。` }] };
|
|
158
158
|
} else {
|
|
159
159
|
closeSession(platform);
|
|
160
160
|
return {
|
|
161
|
-
content: [{ type: 'text', text: `${label} 未登录或登录已过期。\n调试信息: url=${url}\nprofileDir=${profileDir}\
|
|
161
|
+
content: [{ type: 'text', text: `${label} 未登录或登录已过期。\n调试信息: url=${url}\nprofileDir=${profileDir}\n请在「连接外部账号」中重新扫码连接。` }],
|
|
162
162
|
isError: true,
|
|
163
163
|
};
|
|
164
164
|
}
|
package/package.json
CHANGED
package/src/browser-login.js
CHANGED
|
@@ -15,10 +15,12 @@ import { WebSocket } from 'ws';
|
|
|
15
15
|
|
|
16
16
|
export const PLATFORM_CONFIGS = {
|
|
17
17
|
xhs: {
|
|
18
|
-
|
|
19
|
-
//
|
|
18
|
+
// Use creator subdomain so the QR scan establishes a creator session directly.
|
|
19
|
+
// www.xiaohongshu.com session is separate — it cannot access creator.xiaohongshu.com.
|
|
20
|
+
loginUrl: 'https://creator.xiaohongshu.com',
|
|
21
|
+
// Creator login page defaults to password login; click QR tab first
|
|
22
|
+
qrTabSelector: ['.qrcode-box', '[class*="qrcode"]', '[class*="scan-login"]', '[class*="qr-login"]'],
|
|
20
23
|
getSessionValue: (cookies) => cookies.find(c => c.name === 'web_session')?.value ?? null,
|
|
21
|
-
// Login = value exists AND has changed from baseline (real auth replaces anonymous session)
|
|
22
24
|
isLoggedIn: (cookies, baseline) => {
|
|
23
25
|
const val = cookies.find(c => c.name === 'web_session')?.value ?? null;
|
|
24
26
|
return val !== null && val !== baseline;
|
|
@@ -129,7 +131,7 @@ export class BrowserLoginSession {
|
|
|
129
131
|
'--disable-dev-shm-usage',
|
|
130
132
|
'--headless=new',
|
|
131
133
|
`--user-data-dir=${this._profileDir}`,
|
|
132
|
-
'--window-size=
|
|
134
|
+
'--window-size=1280,900',
|
|
133
135
|
'--disable-blink-features=AutomationControlled',
|
|
134
136
|
'--disable-infobars',
|
|
135
137
|
'--disable-component-extensions-with-background-pages',
|
|
@@ -177,10 +179,35 @@ export class BrowserLoginSession {
|
|
|
177
179
|
|
|
178
180
|
await this.send('Page.navigate', { url: this._config.loginUrl });
|
|
179
181
|
|
|
180
|
-
// Wait for page to settle
|
|
182
|
+
// Wait for page to settle
|
|
183
|
+
await sleep(4000);
|
|
184
|
+
|
|
185
|
+
// Try to click the QR scan login tab if present (some platforms default to password login)
|
|
186
|
+
if (this._config.qrTabSelector) {
|
|
187
|
+
try {
|
|
188
|
+
const selectors = this._config.qrTabSelector;
|
|
189
|
+
await this.send('Runtime.evaluate', {
|
|
190
|
+
expression: `(function() {
|
|
191
|
+
const sels = ${JSON.stringify(selectors)};
|
|
192
|
+
for (const s of sels) {
|
|
193
|
+
const el = document.querySelector(s);
|
|
194
|
+
if (el) { el.click(); return s; }
|
|
195
|
+
}
|
|
196
|
+
// Also try text-based search
|
|
197
|
+
const all = [...document.querySelectorAll('a,button,span,div')];
|
|
198
|
+
const el = all.find(e => e.innerText?.trim() === '扫码登录' || e.innerText?.trim() === '二维码登录');
|
|
199
|
+
if (el) { el.click(); return 'text:' + el.innerText.trim(); }
|
|
200
|
+
return null;
|
|
201
|
+
})()`,
|
|
202
|
+
returnByValue: true,
|
|
203
|
+
});
|
|
204
|
+
await sleep(1000);
|
|
205
|
+
} catch {}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Record baseline session cookie value.
|
|
181
209
|
// XHS sets an anonymous web_session on first visit — we only trigger login_complete
|
|
182
210
|
// when the value CHANGES (real login replaces it with an authenticated session).
|
|
183
|
-
await sleep(4000);
|
|
184
211
|
const baselineCookies = await this.send('Network.getAllCookies', {});
|
|
185
212
|
const baselineSession = this._config.getSessionValue(baselineCookies.cookies ?? []);
|
|
186
213
|
|
|
@@ -224,7 +251,7 @@ export class BrowserLoginSession {
|
|
|
224
251
|
}
|
|
225
252
|
|
|
226
253
|
async screenshot() {
|
|
227
|
-
const result = await this.send('Page.captureScreenshot', { format: 'jpeg', quality:
|
|
254
|
+
const result = await this.send('Page.captureScreenshot', { format: 'jpeg', quality: 60 }, 10_000);
|
|
228
255
|
return result.data;
|
|
229
256
|
}
|
|
230
257
|
|
|
@@ -256,12 +283,6 @@ export class BrowserLoginSession {
|
|
|
256
283
|
// Export decrypted cookies to cookies.json so chrome-pool can inject them
|
|
257
284
|
// without relying on platform-specific keychain encryption (fixes macOS cross-process issue)
|
|
258
285
|
try {
|
|
259
|
-
// For XHS: also visit creator subdomain so it sets its own session tokens
|
|
260
|
-
// before we snapshot cookies — otherwise creator.xiaohongshu.com rejects the session
|
|
261
|
-
if (this._platform === 'xhs') {
|
|
262
|
-
await this.send('Page.navigate', { url: 'https://creator.xiaohongshu.com' });
|
|
263
|
-
await sleep(4000);
|
|
264
|
-
}
|
|
265
286
|
const cookieResult = await this.send('Network.getAllCookies', {});
|
|
266
287
|
const baseDomain = new URL(this._config.loginUrl).hostname.split('.').slice(-2).join('.');
|
|
267
288
|
const cookies = (cookieResult.cookies ?? []).filter(c =>
|