@lightcone-ai/daemon 0.9.56 → 0.9.58
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,11 +35,11 @@ export class XhsAdapter {
|
|
|
35
35
|
async checkLoginStatus() {
|
|
36
36
|
const profileDir = process.env.XHS_PROFILE_DIR ?? '(not set)';
|
|
37
37
|
|
|
38
|
-
//
|
|
38
|
+
// Navigate to www to establish session context and check __INITIAL_STATE__
|
|
39
|
+
// Do NOT navigate to creator here — the 401 redirect can clear cookies
|
|
39
40
|
await this._cdp.send('Page.navigate', { url: 'https://www.xiaohongshu.com' });
|
|
40
41
|
await sleep(4000);
|
|
41
42
|
|
|
42
|
-
// Step 2: check login via window.__INITIAL_STATE__ (auto-agent approach — more reliable than URL check)
|
|
43
43
|
const stateResult = await this._cdp.send('Runtime.evaluate', {
|
|
44
44
|
expression: `(function() {
|
|
45
45
|
try {
|
|
@@ -56,30 +56,22 @@ export class XhsAdapter {
|
|
|
56
56
|
let stateData = {};
|
|
57
57
|
try { stateData = JSON.parse(stateStr); } catch {}
|
|
58
58
|
|
|
59
|
-
const
|
|
60
|
-
console.error(`[XhsAdapter]
|
|
61
|
-
|
|
62
|
-
// Step 3: if logged in via state, also verify creator access
|
|
63
|
-
let creatorUrl = '';
|
|
64
|
-
if (loggedInViaState) {
|
|
65
|
-
await this._cdp.send('Page.navigate', { url: 'https://creator.xiaohongshu.com/publish/publish' });
|
|
66
|
-
await sleep(5000);
|
|
67
|
-
const urlResult = await this._cdp.send('Runtime.evaluate', { expression: 'window.location.href', returnByValue: true });
|
|
68
|
-
creatorUrl = urlResult.result?.value ?? '';
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const loggedIn = loggedInViaState;
|
|
72
|
-
const url = creatorUrl || 'www check only';
|
|
73
|
-
console.error(`[XhsAdapter] checkLoginStatus: loggedIn=${loggedIn} creatorUrl=${creatorUrl} profileDir=${profileDir}`);
|
|
74
|
-
return { loggedIn, url, profileDir, userId: stateData.userId, nickname: stateData.nickname };
|
|
59
|
+
const loggedIn = !!(stateData.userId || stateData.nickname);
|
|
60
|
+
console.error(`[XhsAdapter] checkLoginStatus: loggedIn=${loggedIn} state=${stateStr}`);
|
|
61
|
+
return { loggedIn, url: 'www.xiaohongshu.com', profileDir, userId: stateData.userId, nickname: stateData.nickname };
|
|
75
62
|
}
|
|
76
63
|
|
|
77
64
|
async publishImageText({ title, text, tags = [], images = [] }) {
|
|
78
65
|
const cdp = this._cdp;
|
|
79
66
|
|
|
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
|
+
|
|
80
72
|
// Navigate to publish page
|
|
81
73
|
await cdp.send('Page.navigate', { url: 'https://creator.xiaohongshu.com/publish/publish' });
|
|
82
|
-
await sleep(
|
|
74
|
+
await sleep(4000);
|
|
83
75
|
|
|
84
76
|
// Check if we got redirected away (login expired)
|
|
85
77
|
const urlAfterNav = await this._getUrl();
|
|
@@ -146,9 +138,12 @@ export class XhsAdapter {
|
|
|
146
138
|
async publishShortVideo({ title, text, tags = [], video, cover }) {
|
|
147
139
|
const cdp = this._cdp;
|
|
148
140
|
|
|
149
|
-
await cdp.send('Page.navigate', { url: 'https://
|
|
141
|
+
await cdp.send('Page.navigate', { url: 'https://www.xiaohongshu.com' });
|
|
150
142
|
await sleep(3000);
|
|
151
143
|
|
|
144
|
+
await cdp.send('Page.navigate', { url: 'https://creator.xiaohongshu.com/publish/publish?source=video' });
|
|
145
|
+
await sleep(4000);
|
|
146
|
+
|
|
152
147
|
const urlAfterNav = await this._getUrl();
|
|
153
148
|
if (!urlAfterNav.includes('creator.xiaohongshu.com/publish/publish')) {
|
|
154
149
|
throw new Error(`LOGIN_EXPIRED: 跳转到了 ${urlAfterNav},请重新扫码连接小红书`);
|
|
@@ -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}`);
|