@lightcone-ai/daemon 0.9.52 → 0.9.54
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.
|
@@ -33,6 +33,12 @@ export class XhsAdapter {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
async checkLoginStatus() {
|
|
36
|
+
// Check injected cookies before navigation
|
|
37
|
+
const cookiesResult = await this._cdp.send('Network.getAllCookies', {});
|
|
38
|
+
const allCookies = cookiesResult.cookies ?? [];
|
|
39
|
+
const xhsCookies = allCookies.filter(c => c.domain?.includes('xiaohongshu.com'));
|
|
40
|
+
const webSession = xhsCookies.find(c => c.name === 'web_session');
|
|
41
|
+
|
|
36
42
|
// Navigate to publish page — if redirected away, session is expired
|
|
37
43
|
await this._cdp.send('Page.navigate', { url: 'https://creator.xiaohongshu.com/publish/publish' });
|
|
38
44
|
await sleep(5000);
|
|
@@ -42,10 +48,9 @@ export class XhsAdapter {
|
|
|
42
48
|
});
|
|
43
49
|
const url = result.result?.value ?? '';
|
|
44
50
|
const profileDir = process.env.XHS_PROFILE_DIR ?? '(not set)';
|
|
45
|
-
console.error(`[XhsAdapter] checkLoginStatus url=${url}
|
|
51
|
+
console.error(`[XhsAdapter] checkLoginStatus url=${url} xhsCookies=${xhsCookies.length} web_session=${webSession ? webSession.value.slice(0, 20) + '...' : 'missing'}`);
|
|
46
52
|
const loggedIn = url.includes('creator.xiaohongshu.com/publish/publish');
|
|
47
|
-
|
|
48
|
-
return { loggedIn, url, profileDir };
|
|
53
|
+
return { loggedIn, url, profileDir, xhsCookieCount: xhsCookies.length, hasWebSession: !!webSession };
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
async publishImageText({ title, text, tags = [], images = [] }) {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* across tool calls rather than restarting Chrome each time.
|
|
5
5
|
*/
|
|
6
6
|
import { spawn, execSync } from 'child_process';
|
|
7
|
-
import { accessSync, constants as fsConstants, rmSync, existsSync } from 'fs';
|
|
7
|
+
import { accessSync, constants as fsConstants, rmSync, existsSync, readFileSync } from 'fs';
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import http from 'http';
|
|
10
10
|
import { WebSocket } from 'ws';
|
|
@@ -168,7 +168,7 @@ export async function getSession(platform, profileDir) {
|
|
|
168
168
|
await existing.cdp.send('Runtime.evaluate', { expression: '1', returnByValue: true }, 3000);
|
|
169
169
|
return existing.cdp;
|
|
170
170
|
} catch {
|
|
171
|
-
console.
|
|
171
|
+
console.error(`[ChromePool] Session for ${platform} is stale, recreating`);
|
|
172
172
|
existing.proc?.kill();
|
|
173
173
|
_sessions.delete(platform);
|
|
174
174
|
}
|
|
@@ -177,7 +177,7 @@ export async function getSession(platform, profileDir) {
|
|
|
177
177
|
if (!profileDir) throw new Error(`No profile dir provided for platform=${platform}. Has the user logged in?`);
|
|
178
178
|
|
|
179
179
|
const port = _nextPort++;
|
|
180
|
-
console.
|
|
180
|
+
console.error(`[ChromePool] Starting Chrome for ${platform} on port ${port}, profile=${profileDir}`);
|
|
181
181
|
const proc = await _spawnChrome(profileDir, port);
|
|
182
182
|
const cdp = await _connectCdp(port);
|
|
183
183
|
|
|
@@ -188,8 +188,34 @@ export async function getSession(platform, profileDir) {
|
|
|
188
188
|
source: `Object.defineProperty(navigator, 'webdriver', { get: () => undefined });`,
|
|
189
189
|
});
|
|
190
190
|
|
|
191
|
+
// Inject saved cookies from browser-login (bypasses macOS keychain cross-process encryption issue)
|
|
192
|
+
const cookiesFile = path.join(profileDir, 'cookies.json');
|
|
193
|
+
if (existsSync(cookiesFile)) {
|
|
194
|
+
try {
|
|
195
|
+
const rawCookies = JSON.parse(readFileSync(cookiesFile, 'utf8'));
|
|
196
|
+
// Strip read-only fields that Network.setCookies doesn't accept
|
|
197
|
+
const cookies = rawCookies.map(({ name, value, domain, path: p, secure, httpOnly, sameSite, expires, priority }) => {
|
|
198
|
+
const c = { name, value };
|
|
199
|
+
if (domain !== undefined) c.domain = domain;
|
|
200
|
+
if (p !== undefined) c.path = p;
|
|
201
|
+
if (secure !== undefined) c.secure = secure;
|
|
202
|
+
if (httpOnly !== undefined) c.httpOnly = httpOnly;
|
|
203
|
+
if (sameSite !== undefined) c.sameSite = sameSite;
|
|
204
|
+
if (expires !== undefined && expires !== -1) c.expires = expires;
|
|
205
|
+
if (priority !== undefined) c.priority = priority;
|
|
206
|
+
return c;
|
|
207
|
+
});
|
|
208
|
+
await cdp.send('Network.setCookies', { cookies });
|
|
209
|
+
console.error(`[ChromePool] Injected ${cookies.length} cookies from cookies.json for ${platform}`);
|
|
210
|
+
} catch (err) {
|
|
211
|
+
console.error(`[ChromePool] Failed to inject cookies: ${err.message}`);
|
|
212
|
+
}
|
|
213
|
+
} else {
|
|
214
|
+
console.error(`[ChromePool] No cookies.json found for ${platform} at ${cookiesFile}`);
|
|
215
|
+
}
|
|
216
|
+
|
|
191
217
|
proc.on('exit', () => {
|
|
192
|
-
console.
|
|
218
|
+
console.error(`[ChromePool] Chrome for ${platform} exited`);
|
|
193
219
|
_sessions.delete(platform);
|
|
194
220
|
});
|
|
195
221
|
|
|
@@ -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 } = await adapter.checkLoginStatus();
|
|
155
|
+
const { loggedIn, url, profileDir, xhsCookieCount, hasWebSession } = await adapter.checkLoginStatus();
|
|
156
156
|
if (loggedIn) {
|
|
157
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}\n请在「连接外部账号」中重新扫码连接。` }],
|
|
161
|
+
content: [{ type: 'text', text: `${label} 未登录或登录已过期。\n调试信息: 最终URL=${url}\nprofileDir=${profileDir}\nxhsCookies注入数量=${xhsCookieCount} hasWebSession=${hasWebSession}\n请在「连接外部账号」中重新扫码连接。` }],
|
|
162
162
|
isError: true,
|
|
163
163
|
};
|
|
164
164
|
}
|
package/package.json
CHANGED
package/src/browser-login.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { spawn, execSync } from 'child_process';
|
|
8
8
|
import { homedir } from 'os';
|
|
9
|
-
import { accessSync, constants as fsConstants, rmSync, existsSync, mkdirSync } from 'fs';
|
|
9
|
+
import { accessSync, constants as fsConstants, rmSync, existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
10
10
|
import path from 'path';
|
|
11
11
|
import http from 'http';
|
|
12
12
|
import { WebSocket } from 'ws';
|
|
@@ -253,6 +253,18 @@ export class BrowserLoginSession {
|
|
|
253
253
|
const loggedIn = await this.isLoggedIn(baselineSession);
|
|
254
254
|
if (loggedIn) {
|
|
255
255
|
this._stopTimers();
|
|
256
|
+
// Export decrypted cookies to cookies.json so chrome-pool can inject them
|
|
257
|
+
// without relying on platform-specific keychain encryption (fixes macOS cross-process issue)
|
|
258
|
+
try {
|
|
259
|
+
const cookieResult = await this.send('Network.getAllCookies', {});
|
|
260
|
+
const cookies = (cookieResult.cookies ?? []).filter(c =>
|
|
261
|
+
c.domain.includes(new URL(this._config.loginUrl).hostname.split('.').slice(-2).join('.'))
|
|
262
|
+
);
|
|
263
|
+
writeFileSync(path.join(this._profileDir, 'cookies.json'), JSON.stringify(cookies));
|
|
264
|
+
console.log(`[BrowserLogin][${this._platform}] Saved ${cookies.length} cookies to cookies.json`);
|
|
265
|
+
} catch (err) {
|
|
266
|
+
console.error(`[BrowserLogin][${this._platform}] Failed to save cookies: ${err.message}`);
|
|
267
|
+
}
|
|
256
268
|
connection.send({ type: 'browser:login_complete', platform: this._platform, profileDir: this._profileDir });
|
|
257
269
|
this.close();
|
|
258
270
|
}
|