@lightcone-ai/daemon 0.15.19 → 0.15.22
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.
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { randomUUID } from 'crypto';
|
|
3
3
|
import { spawn } from 'child_process';
|
|
4
|
-
import { accessSync, constants as fsConstants } from 'fs';
|
|
4
|
+
import { accessSync, constants as fsConstants, existsSync, readFileSync, rmSync } from 'fs';
|
|
5
5
|
import http from 'http';
|
|
6
6
|
import net from 'net';
|
|
7
|
+
import path from 'path';
|
|
7
8
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
8
9
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
9
10
|
import { z } from 'zod';
|
|
@@ -86,6 +87,12 @@ class CdpSession {
|
|
|
86
87
|
if (!profileDir) throw new Error('LOGIN_REQUIRED:WECHAT_MP_PROFILE_DIR is not configured');
|
|
87
88
|
const chrome = detectChrome();
|
|
88
89
|
this._port = await getFreePort();
|
|
90
|
+
|
|
91
|
+
for (const lockFile of ['SingletonLock', 'SingletonCookie', 'SingletonSocket']) {
|
|
92
|
+
const lockPath = path.join(profileDir, lockFile);
|
|
93
|
+
try { if (existsSync(lockPath)) rmSync(lockPath, { force: true }); } catch {}
|
|
94
|
+
}
|
|
95
|
+
|
|
89
96
|
this._proc = spawn(chrome, [
|
|
90
97
|
`--remote-debugging-port=${this._port}`,
|
|
91
98
|
'--no-sandbox',
|
|
@@ -96,7 +103,7 @@ class CdpSession {
|
|
|
96
103
|
'--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',
|
|
97
104
|
`--user-data-dir=${profileDir}`,
|
|
98
105
|
'--window-size=1280,900',
|
|
99
|
-
|
|
106
|
+
'about:blank',
|
|
100
107
|
], { stdio: 'ignore' });
|
|
101
108
|
|
|
102
109
|
for (let i = 0; i < 50; i += 1) {
|
|
@@ -112,6 +119,8 @@ class CdpSession {
|
|
|
112
119
|
await this.cdp('Page.enable');
|
|
113
120
|
await this.cdp('Network.enable');
|
|
114
121
|
await this.cdp('Runtime.enable');
|
|
122
|
+
await this.injectCookies(profileDir);
|
|
123
|
+
await this.navigate(`${WECHAT_MP_ORIGIN}/cgi-bin/home?t=home/index&lang=zh_CN`);
|
|
115
124
|
}
|
|
116
125
|
|
|
117
126
|
_connectWs(wsUrl) {
|
|
@@ -160,6 +169,42 @@ class CdpSession {
|
|
|
160
169
|
await sleep(1500);
|
|
161
170
|
}
|
|
162
171
|
|
|
172
|
+
async injectCookies(profileDir) {
|
|
173
|
+
const cookiesFile = path.join(profileDir, 'cookies.json');
|
|
174
|
+
if (!existsSync(cookiesFile)) {
|
|
175
|
+
console.error(`[wechat-mp-fetch] cookies.json not found at ${cookiesFile}`);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
const rawCookies = JSON.parse(readFileSync(cookiesFile, 'utf8'));
|
|
180
|
+
const cookies = rawCookies.map(({
|
|
181
|
+
name,
|
|
182
|
+
value,
|
|
183
|
+
domain,
|
|
184
|
+
path: cookiePath,
|
|
185
|
+
secure,
|
|
186
|
+
httpOnly,
|
|
187
|
+
sameSite,
|
|
188
|
+
expires,
|
|
189
|
+
priority,
|
|
190
|
+
}) => {
|
|
191
|
+
const cookie = { name, value };
|
|
192
|
+
if (domain !== undefined) cookie.domain = domain;
|
|
193
|
+
if (cookiePath !== undefined) cookie.path = cookiePath;
|
|
194
|
+
if (secure !== undefined) cookie.secure = secure;
|
|
195
|
+
if (httpOnly !== undefined) cookie.httpOnly = httpOnly;
|
|
196
|
+
if (sameSite !== undefined) cookie.sameSite = sameSite;
|
|
197
|
+
if (expires !== undefined && expires !== -1) cookie.expires = expires;
|
|
198
|
+
if (priority !== undefined) cookie.priority = priority;
|
|
199
|
+
return cookie;
|
|
200
|
+
});
|
|
201
|
+
await this.cdp('Network.setCookies', { cookies });
|
|
202
|
+
console.error(`[wechat-mp-fetch] Injected ${cookies.length} cookies from cookies.json`);
|
|
203
|
+
} catch (error) {
|
|
204
|
+
console.error(`[wechat-mp-fetch] Failed to inject cookies: ${error?.message ?? error}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
163
208
|
async evalValue(expression) {
|
|
164
209
|
const result = await this.cdp('Runtime.evaluate', {
|
|
165
210
|
expression,
|
|
@@ -191,7 +236,13 @@ async function ensureLoggedIn() {
|
|
|
191
236
|
await cdp.navigate(`${WECHAT_MP_ORIGIN}/cgi-bin/home?t=home/index&lang=zh_CN`);
|
|
192
237
|
href = await cdp.currentUrl();
|
|
193
238
|
}
|
|
194
|
-
|
|
239
|
+
let token = '';
|
|
240
|
+
for (let i = 0; i < 12; i += 1) {
|
|
241
|
+
href = await cdp.currentUrl().catch(() => href);
|
|
242
|
+
token = new URL(String(href)).searchParams.get('token') ?? '';
|
|
243
|
+
if (token) break;
|
|
244
|
+
await sleep(500);
|
|
245
|
+
}
|
|
195
246
|
if (!token) {
|
|
196
247
|
throw new Error('LOGIN_REQUIRED:WeChat MP backend login is required');
|
|
197
248
|
}
|
package/package.json
CHANGED
package/src/browser-login.js
CHANGED
|
@@ -104,6 +104,59 @@ export const PLATFORM_CONFIGS = {
|
|
|
104
104
|
?? null;
|
|
105
105
|
return val !== null && val !== baseline;
|
|
106
106
|
},
|
|
107
|
+
async isPageLoggedIn(session) {
|
|
108
|
+
const result = await session.send('Runtime.evaluate', {
|
|
109
|
+
expression: `(async () => {
|
|
110
|
+
const href = location.href;
|
|
111
|
+
const text = document.body?.innerText || '';
|
|
112
|
+
const token = new URL(href).searchParams.get('token') || '';
|
|
113
|
+
if (!/[?&]token=\\d+/.test(href) || text.includes('登录超时') || text.includes('重新登录')) {
|
|
114
|
+
return {
|
|
115
|
+
href,
|
|
116
|
+
hasToken: Boolean(token),
|
|
117
|
+
loginTimedOut: text.includes('登录超时') || text.includes('重新登录'),
|
|
118
|
+
apiOk: false
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const url = new URL('/cgi-bin/searchbiz', location.origin);
|
|
123
|
+
url.searchParams.set('action', 'search_biz');
|
|
124
|
+
url.searchParams.set('begin', '0');
|
|
125
|
+
url.searchParams.set('count', '1');
|
|
126
|
+
url.searchParams.set('query', '腾讯');
|
|
127
|
+
url.searchParams.set('token', token);
|
|
128
|
+
url.searchParams.set('lang', 'zh_CN');
|
|
129
|
+
url.searchParams.set('f', 'json');
|
|
130
|
+
url.searchParams.set('ajax', '1');
|
|
131
|
+
|
|
132
|
+
let apiOk = false;
|
|
133
|
+
try {
|
|
134
|
+
const response = await fetch(url.toString(), {
|
|
135
|
+
credentials: 'include',
|
|
136
|
+
headers: {
|
|
137
|
+
'Accept': 'application/json, text/javascript, */*; q=0.01',
|
|
138
|
+
'X-Requested-With': 'XMLHttpRequest'
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
const payload = await response.json();
|
|
142
|
+
apiOk = response.ok && Number(payload?.base_resp?.ret ?? 0) === 0 && Array.isArray(payload?.list);
|
|
143
|
+
} catch {
|
|
144
|
+
apiOk = false;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
href,
|
|
149
|
+
hasToken: Boolean(token),
|
|
150
|
+
loginTimedOut: false,
|
|
151
|
+
apiOk
|
|
152
|
+
};
|
|
153
|
+
})()`,
|
|
154
|
+
returnByValue: true,
|
|
155
|
+
awaitPromise: true,
|
|
156
|
+
}, 5000);
|
|
157
|
+
const value = result?.result?.value ?? {};
|
|
158
|
+
return Boolean(value.hasToken) && !value.loginTimedOut && Boolean(value.apiOk);
|
|
159
|
+
},
|
|
107
160
|
},
|
|
108
161
|
};
|
|
109
162
|
|
|
@@ -400,7 +453,12 @@ export class BrowserLoginSession {
|
|
|
400
453
|
|
|
401
454
|
async isLoggedIn(baseline) {
|
|
402
455
|
const result = await this.send('Network.getAllCookies', {});
|
|
403
|
-
|
|
456
|
+
const cookieLoggedIn = this._config.isLoggedIn(result.cookies ?? [], baseline);
|
|
457
|
+
if (!cookieLoggedIn) return false;
|
|
458
|
+
if (typeof this._config.isPageLoggedIn === 'function') {
|
|
459
|
+
return this._config.isPageLoggedIn(this);
|
|
460
|
+
}
|
|
461
|
+
return true;
|
|
404
462
|
}
|
|
405
463
|
|
|
406
464
|
_startPolling(connection, baselineSession) {
|