@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 www to establish session context and check __INITIAL_STATE__
39
- // Do NOT navigate to creator here — the 401 redirect can clear cookies
40
- await this._cdp.send('Page.navigate', { url: 'https://www.xiaohongshu.com' });
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 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 };
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, userId, nickname } = await adapter.checkLoginStatus();
155
+ const { loggedIn, url, profileDir } = await adapter.checkLoginStatus();
156
156
  if (loggedIn) {
157
- return { content: [{ type: 'text', text: `✓ ${label} 已登录,用户: ${nickname ?? userId ?? '未知'},可以发布内容。` }] };
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}\nuserId=${userId} nickname=${nickname}\n请在「连接外部账号」中重新扫码连接。` }],
161
+ content: [{ type: 'text', text: `${label} 未登录或登录已过期。\n调试信息: url=${url}\nprofileDir=${profileDir}\n请在「连接外部账号」中重新扫码连接。` }],
162
162
  isError: true,
163
163
  };
164
164
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightcone-ai/daemon",
3
- "version": "0.9.58",
3
+ "version": "0.9.60",
4
4
  "type": "module",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -15,10 +15,12 @@ import { WebSocket } from 'ws';
15
15
 
16
16
  export const PLATFORM_CONFIGS = {
17
17
  xhs: {
18
- loginUrl: 'https://www.xiaohongshu.com',
19
- // Returns current session value (null if not present)
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=800,900',
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, then record baseline session cookie value.
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: 50, clip: { x: 0, y: 0, width: 800, height: 900, scale: 0.75 } }, 10_000);
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 =>