@sovovs/bycli 2.1.42 → 2.1.44
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.
- package/clis/weixin/_wechat/article-index.js +1 -1
- package/clis/weixin/_wechat/article-link.js +1 -1
- package/clis/weixin/_wechat/auth-session.js +1 -1
- package/clis/weixin/_wechat/sogou-search.js +1 -1
- package/clis/weixin/download.js +1 -1
- package/clis/weixin/save-articles.js +1 -1
- package/dist/src/adapter-scheduler.d.ts +5 -0
- package/dist/src/adapter-scheduler.js +80 -37
- package/package.json +1 -1
|
@@ -129,7 +129,7 @@ function transportError(error, credentials) {
|
|
|
129
129
|
if (isWechatVerificationResponse(redactedMessage, redactedHint)) {
|
|
130
130
|
return new AuthRequiredError(
|
|
131
131
|
DOMAIN,
|
|
132
|
-
'WeChat article index requires environment verification. Complete it in the
|
|
132
|
+
'WeChat article index requires environment verification. Complete it in the retained browser tab and explicitly confirm completion to the caller.',
|
|
133
133
|
);
|
|
134
134
|
}
|
|
135
135
|
return new CommandExecutionError(
|
|
@@ -71,7 +71,7 @@ export async function resolveWechatArticleUrl(page, rawUrl) {
|
|
|
71
71
|
if (/验证码|安全验证|异常访问|访问过于频繁|请输入验证码/.test(text)) {
|
|
72
72
|
throw new AuthRequiredError(
|
|
73
73
|
'weixin.sogou.com',
|
|
74
|
-
'Sogou Weixin requires verification. Complete it in the
|
|
74
|
+
'Sogou Weixin requires verification. Complete it in the retained browser tab and explicitly confirm completion to the caller.',
|
|
75
75
|
);
|
|
76
76
|
}
|
|
77
77
|
if (!isTrustedWechatArticleUrl(result?.finalUrl)) {
|
|
@@ -131,7 +131,7 @@ export async function resolveBrowserCredentials(page, options = {}) {
|
|
|
131
131
|
|
|
132
132
|
throw new AuthRequiredError(
|
|
133
133
|
DOMAIN,
|
|
134
|
-
'WeChat login is required.
|
|
134
|
+
'WeChat login is required. Complete QR-code login in the retained browser tab and explicitly confirm completion to the caller.',
|
|
135
135
|
);
|
|
136
136
|
}
|
|
137
137
|
}
|
|
@@ -144,7 +144,7 @@ export async function searchSogouArticlePage(page, { query, pageNo, preloadedUrl
|
|
|
144
144
|
if (payload.blocked) {
|
|
145
145
|
throw new AuthRequiredError(
|
|
146
146
|
SOGOU_WEIXIN_DOMAIN,
|
|
147
|
-
'Sogou Weixin requires verification. Complete it in the
|
|
147
|
+
'Sogou Weixin requires verification. Complete it in the retained browser tab and explicitly confirm completion to the caller.',
|
|
148
148
|
);
|
|
149
149
|
}
|
|
150
150
|
if (payload.invalidCount > 0) {
|
package/clis/weixin/download.js
CHANGED
|
@@ -233,7 +233,7 @@ cli({
|
|
|
233
233
|
if (data?.errorHint === 'environment verification required') {
|
|
234
234
|
throw new AuthRequiredError(
|
|
235
235
|
'mp.weixin.qq.com',
|
|
236
|
-
'WeChat article page requires environment verification. Complete it in the
|
|
236
|
+
'WeChat article page requires environment verification. Complete it in the retained browser tab and explicitly confirm completion to the caller.',
|
|
237
237
|
);
|
|
238
238
|
}
|
|
239
239
|
const rows = await downloadArticle({
|
|
@@ -120,7 +120,7 @@ export async function fetchArticleHtmlInBrowser(article, page) {
|
|
|
120
120
|
if (result?.accessIssue) {
|
|
121
121
|
throw new AuthRequiredError(
|
|
122
122
|
DOMAIN,
|
|
123
|
-
'WeChat article page requires environment verification. Complete it in the
|
|
123
|
+
'WeChat article page requires environment verification. Complete it in the retained browser tab and explicitly confirm completion to the caller.',
|
|
124
124
|
);
|
|
125
125
|
}
|
|
126
126
|
if (!isTrustedWechatArticleUrl(result?.finalUrl)) {
|
|
@@ -40,12 +40,14 @@ export interface AdapterSchedulerOptions {
|
|
|
40
40
|
leaseExpiryMs?: number;
|
|
41
41
|
runtimeCeiling?: number;
|
|
42
42
|
releasedLeaseRetentionMs?: number;
|
|
43
|
+
startIntervalMs?: number;
|
|
43
44
|
}
|
|
44
45
|
export declare class AdapterScheduler {
|
|
45
46
|
private readonly now;
|
|
46
47
|
private readonly leaseExpiryMs;
|
|
47
48
|
private readonly runtimeCeiling;
|
|
48
49
|
private readonly releasedLeaseRetentionMs;
|
|
50
|
+
private readonly startIntervalMs;
|
|
49
51
|
private readonly pools;
|
|
50
52
|
private readonly releasedLeaseIds;
|
|
51
53
|
private readonly generations;
|
|
@@ -74,6 +76,9 @@ export declare class AdapterScheduler {
|
|
|
74
76
|
grants: number;
|
|
75
77
|
};
|
|
76
78
|
private schedule;
|
|
79
|
+
private rejectExpiredQueued;
|
|
80
|
+
private clearPoolTimer;
|
|
81
|
+
private armPoolTimer;
|
|
77
82
|
private scheduleResources;
|
|
78
83
|
private releaseAllResourcesForLease;
|
|
79
84
|
private pruneReleasedLeaseIds;
|
|
@@ -12,6 +12,7 @@ export class AdapterScheduler {
|
|
|
12
12
|
leaseExpiryMs;
|
|
13
13
|
runtimeCeiling;
|
|
14
14
|
releasedLeaseRetentionMs;
|
|
15
|
+
startIntervalMs;
|
|
15
16
|
pools = new Map();
|
|
16
17
|
releasedLeaseIds = new Map();
|
|
17
18
|
generations = new Map();
|
|
@@ -20,10 +21,15 @@ export class AdapterScheduler {
|
|
|
20
21
|
resourceQueue = [];
|
|
21
22
|
sequence = 0;
|
|
22
23
|
constructor(options = {}) {
|
|
24
|
+
const startIntervalMs = options.startIntervalMs ?? 5_000;
|
|
25
|
+
if (!Number.isInteger(startIntervalMs) || startIntervalMs < 0) {
|
|
26
|
+
throw new Error('Adapter start interval must be a non-negative integer');
|
|
27
|
+
}
|
|
23
28
|
this.now = options.now ?? Date.now;
|
|
24
29
|
this.leaseExpiryMs = options.leaseExpiryMs ?? 45_000;
|
|
25
30
|
this.runtimeCeiling = options.runtimeCeiling ?? 3;
|
|
26
31
|
this.releasedLeaseRetentionMs = options.releasedLeaseRetentionMs ?? 300_000;
|
|
32
|
+
this.startIntervalMs = startIntervalMs;
|
|
27
33
|
}
|
|
28
34
|
acquire(request) {
|
|
29
35
|
this.validateRequest(request);
|
|
@@ -73,6 +79,7 @@ export class AdapterScheduler {
|
|
|
73
79
|
this.releasedLeaseIds.set(lease.leaseId, this.now() + this.releasedLeaseRetentionMs);
|
|
74
80
|
if (release.reason === 'auth_gate' || release.reason === 'rate_limited') {
|
|
75
81
|
pool.closed = release.reason;
|
|
82
|
+
this.clearPoolTimer(pool);
|
|
76
83
|
const error = this.poolClosedError(release.reason);
|
|
77
84
|
for (const pending of pool.queued.splice(0)) {
|
|
78
85
|
if (pending.timer)
|
|
@@ -144,14 +151,7 @@ export class AdapterScheduler {
|
|
|
144
151
|
const now = this.now();
|
|
145
152
|
this.pruneReleasedLeaseIds(now);
|
|
146
153
|
for (const pool of [...this.pools.values()]) {
|
|
147
|
-
|
|
148
|
-
if (pending.deadline > now)
|
|
149
|
-
continue;
|
|
150
|
-
pool.queued.splice(pool.queued.indexOf(pending), 1);
|
|
151
|
-
if (pending.timer)
|
|
152
|
-
clearTimeout(pending.timer);
|
|
153
|
-
pending.reject(new AdapterSchedulerError('ADAPTER_QUEUE_TIMEOUT', 'Timed out waiting for an Adapter command lease'));
|
|
154
|
-
}
|
|
154
|
+
this.rejectExpiredQueued(pool, now);
|
|
155
155
|
for (const lease of [...pool.running.values()]) {
|
|
156
156
|
if (lease.heartbeatDeadline > now)
|
|
157
157
|
continue;
|
|
@@ -176,6 +176,7 @@ export class AdapterScheduler {
|
|
|
176
176
|
}
|
|
177
177
|
reset() {
|
|
178
178
|
for (const pool of this.pools.values()) {
|
|
179
|
+
this.clearPoolTimer(pool);
|
|
179
180
|
for (const pending of pool.queued) {
|
|
180
181
|
if (pending.timer)
|
|
181
182
|
clearTimeout(pending.timer);
|
|
@@ -209,34 +210,69 @@ export class AdapterScheduler {
|
|
|
209
210
|
};
|
|
210
211
|
}
|
|
211
212
|
schedule(pool) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
pool.
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
poolKey: pool.key,
|
|
227
|
-
contextId: eligible.request.contextId,
|
|
228
|
-
surface: 'adapter',
|
|
229
|
-
site: eligible.request.site,
|
|
230
|
-
adapterSession: eligible.request.adapterSession,
|
|
231
|
-
sessionKey: eligible.request.sessionKey,
|
|
232
|
-
generation: pool.generation,
|
|
233
|
-
grantedAt,
|
|
234
|
-
heartbeatDeadline: grantedAt + this.leaseExpiryMs,
|
|
235
|
-
};
|
|
236
|
-
pool.running.set(lease.leaseId, lease);
|
|
237
|
-
pool.activeSessions.add(lease.adapterSession);
|
|
238
|
-
eligible.resolve({ ...lease });
|
|
213
|
+
if (pool.closed)
|
|
214
|
+
return;
|
|
215
|
+
const now = this.now();
|
|
216
|
+
this.rejectExpiredQueued(pool, now);
|
|
217
|
+
if (pool.running.size >= pool.maxParallel)
|
|
218
|
+
return;
|
|
219
|
+
const eligible = pool.queued
|
|
220
|
+
.filter(entry => !pool.activeSessions.has(entry.request.adapterSession))
|
|
221
|
+
.sort((a, b) => a.enqueuedAt - b.enqueuedAt || a.sequence - b.sequence)[0];
|
|
222
|
+
if (!eligible)
|
|
223
|
+
return;
|
|
224
|
+
if (now < pool.nextGrantAt) {
|
|
225
|
+
this.armPoolTimer(pool, pool.nextGrantAt);
|
|
226
|
+
return;
|
|
239
227
|
}
|
|
228
|
+
pool.queued.splice(pool.queued.indexOf(eligible), 1);
|
|
229
|
+
if (eligible.timer)
|
|
230
|
+
clearTimeout(eligible.timer);
|
|
231
|
+
const grantedAt = now;
|
|
232
|
+
const lease = {
|
|
233
|
+
leaseId: crypto.randomUUID(),
|
|
234
|
+
requestId: eligible.request.requestId,
|
|
235
|
+
poolKey: pool.key,
|
|
236
|
+
contextId: eligible.request.contextId,
|
|
237
|
+
surface: 'adapter',
|
|
238
|
+
site: eligible.request.site,
|
|
239
|
+
adapterSession: eligible.request.adapterSession,
|
|
240
|
+
sessionKey: eligible.request.sessionKey,
|
|
241
|
+
generation: pool.generation,
|
|
242
|
+
grantedAt,
|
|
243
|
+
heartbeatDeadline: grantedAt + this.leaseExpiryMs,
|
|
244
|
+
};
|
|
245
|
+
pool.nextGrantAt = grantedAt + this.startIntervalMs;
|
|
246
|
+
pool.running.set(lease.leaseId, lease);
|
|
247
|
+
pool.activeSessions.add(lease.adapterSession);
|
|
248
|
+
eligible.resolve({ ...lease });
|
|
249
|
+
this.schedule(pool);
|
|
250
|
+
}
|
|
251
|
+
rejectExpiredQueued(pool, now) {
|
|
252
|
+
for (const pending of [...pool.queued]) {
|
|
253
|
+
if (pending.deadline > now)
|
|
254
|
+
continue;
|
|
255
|
+
pool.queued.splice(pool.queued.indexOf(pending), 1);
|
|
256
|
+
if (pending.timer)
|
|
257
|
+
clearTimeout(pending.timer);
|
|
258
|
+
pending.reject(new AdapterSchedulerError('ADAPTER_QUEUE_TIMEOUT', 'Timed out waiting for an Adapter command lease'));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
clearPoolTimer(pool) {
|
|
262
|
+
if (pool.scheduleTimer)
|
|
263
|
+
clearTimeout(pool.scheduleTimer);
|
|
264
|
+
pool.scheduleTimer = undefined;
|
|
265
|
+
}
|
|
266
|
+
armPoolTimer(pool, dueAt) {
|
|
267
|
+
if (pool.scheduleTimer)
|
|
268
|
+
return;
|
|
269
|
+
pool.scheduleTimer = setTimeout(() => {
|
|
270
|
+
pool.scheduleTimer = undefined;
|
|
271
|
+
if (this.pools.get(pool.key) !== pool)
|
|
272
|
+
return;
|
|
273
|
+
this.sweepExpired();
|
|
274
|
+
}, Math.max(0, dueAt - this.now()));
|
|
275
|
+
pool.scheduleTimer.unref?.();
|
|
240
276
|
}
|
|
241
277
|
scheduleResources() {
|
|
242
278
|
for (const pending of [...this.resourceQueue].sort((a, b) => a.sequence - b.sequence)) {
|
|
@@ -319,6 +355,7 @@ export class AdapterScheduler {
|
|
|
319
355
|
key,
|
|
320
356
|
generation,
|
|
321
357
|
maxParallel: Math.min(request.maxParallel, this.runtimeCeiling),
|
|
358
|
+
nextGrantAt: 0,
|
|
322
359
|
running: new Map(),
|
|
323
360
|
activeSessions: new Set(),
|
|
324
361
|
queued: [],
|
|
@@ -327,8 +364,14 @@ export class AdapterScheduler {
|
|
|
327
364
|
return pool;
|
|
328
365
|
}
|
|
329
366
|
removeDrainedPool(pool) {
|
|
330
|
-
if (pool.running.size
|
|
331
|
-
|
|
367
|
+
if (pool.running.size !== 0 || pool.queued.length !== 0)
|
|
368
|
+
return;
|
|
369
|
+
if (!pool.closed && this.now() < pool.nextGrantAt) {
|
|
370
|
+
this.armPoolTimer(pool, pool.nextGrantAt);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
this.clearPoolTimer(pool);
|
|
374
|
+
this.pools.delete(pool.key);
|
|
332
375
|
}
|
|
333
376
|
poolClosedError(reason) {
|
|
334
377
|
return reason === 'auth_gate'
|