@lightcone-ai/daemon 0.9.57 → 0.9.59
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
|
|
|
@@ -120,11 +120,13 @@ async function _spawnChrome(profileDir, port) {
|
|
|
120
120
|
}
|
|
121
121
|
} catch {}
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
// On macOS with a display, run non-headless for best site compatibility
|
|
124
|
+
// (headless Chrome has detectable fingerprints that some sites block)
|
|
125
|
+
const isHeadless = process.platform !== 'darwin' || !!process.env.FORCE_HEADLESS;
|
|
126
|
+
const chromeArgs = [
|
|
124
127
|
`--remote-debugging-port=${port}`,
|
|
125
128
|
'--no-sandbox',
|
|
126
129
|
'--disable-dev-shm-usage',
|
|
127
|
-
'--headless=new',
|
|
128
130
|
`--user-data-dir=${profileDir}`,
|
|
129
131
|
'--window-size=1280,900',
|
|
130
132
|
'--disable-blink-features=AutomationControlled',
|
|
@@ -135,10 +137,13 @@ async function _spawnChrome(profileDir, port) {
|
|
|
135
137
|
'--disable-hang-monitor',
|
|
136
138
|
'--use-mock-keychain',
|
|
137
139
|
'--password-store=basic',
|
|
138
|
-
'--disable-gpu',
|
|
139
140
|
'--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
140
141
|
'about:blank',
|
|
141
|
-
]
|
|
142
|
+
];
|
|
143
|
+
if (isHeadless) {
|
|
144
|
+
chromeArgs.splice(3, 0, '--headless=new', '--disable-gpu');
|
|
145
|
+
}
|
|
146
|
+
const proc = spawn(CHROME_BIN, chromeArgs, { stdio: 'ignore', detached: false });
|
|
142
147
|
|
|
143
148
|
proc.on('error', (err) => {
|
|
144
149
|
console.error(`[ChromePool] Spawn error: ${err.message}`);
|
|
@@ -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,10 @@ 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',
|
|
20
21
|
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
22
|
isLoggedIn: (cookies, baseline) => {
|
|
23
23
|
const val = cookies.find(c => c.name === 'web_session')?.value ?? null;
|
|
24
24
|
return val !== null && val !== baseline;
|
|
@@ -256,12 +256,6 @@ export class BrowserLoginSession {
|
|
|
256
256
|
// Export decrypted cookies to cookies.json so chrome-pool can inject them
|
|
257
257
|
// without relying on platform-specific keychain encryption (fixes macOS cross-process issue)
|
|
258
258
|
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
259
|
const cookieResult = await this.send('Network.getAllCookies', {});
|
|
266
260
|
const baseDomain = new URL(this._config.loginUrl).hostname.split('.').slice(-2).join('.');
|
|
267
261
|
const cookies = (cookieResult.cookies ?? []).filter(c =>
|