dsh-social-trace 0.1.2 → 0.1.13
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/README.md +18 -18
- package/THIRD_PARTY_NOTICES +2655 -0
- package/lib/agent.js +3 -2
- package/lib/author-index.js +17 -0
- package/lib/business.js +48 -0
- package/lib/chat-context.js +32 -0
- package/lib/chat-model.js +57 -0
- package/lib/chat.js +356 -0
- package/lib/client.js +219 -14
- package/lib/index.js +37 -20
- package/lib/worker.js +68 -3
- package/package.json +17 -6
package/lib/index.js
CHANGED
|
@@ -9,6 +9,9 @@ import { dataDirectory, workspaceDirectory, saveWorkspaceDirectory, chooseDirect
|
|
|
9
9
|
import { Documents } from './documents.js';
|
|
10
10
|
import { Avatars } from './avatars.js';
|
|
11
11
|
import { SocialAgent } from './agent.js';
|
|
12
|
+
import { hostModel } from './chat-model.js';
|
|
13
|
+
import { BusinessChat } from './chat.js';
|
|
14
|
+
import { createBusiness } from './business.js';
|
|
12
15
|
import { CaptureError } from './capture-error.js';
|
|
13
16
|
import { platformImage } from './text.js';
|
|
14
17
|
import { backup } from 'node:sqlite';
|
|
@@ -44,7 +47,7 @@ function localRequest(req) {
|
|
|
44
47
|
return local && ['127.0.0.1', '::1', '::ffff:127.0.0.1'].includes(address);
|
|
45
48
|
}
|
|
46
49
|
|
|
47
|
-
export function createRuntime(directory = dataDirectory(), { adapterFactories = { weibo: WeiboBrowser, xueqiu: XueqiuBrowser }, chooseFolder = chooseDirectory, avatarFetch = fetch, getIM = () => null, updates = createUpdates() } = {}) {
|
|
50
|
+
export function createRuntime(directory = dataDirectory(), { adapterFactories = { weibo: WeiboBrowser, xueqiu: XueqiuBrowser }, chooseFolder = chooseDirectory, avatarFetch = fetch, getIM = () => null, model = hostModel(() => null), updates = createUpdates() } = {}) {
|
|
48
51
|
let workspace = realpathSync(workspaceDirectory(directory));
|
|
49
52
|
let store = new Store(workspace);
|
|
50
53
|
let documents = new Documents(store, workspace);
|
|
@@ -54,7 +57,7 @@ export function createRuntime(directory = dataDirectory(), { adapterFactories =
|
|
|
54
57
|
let workspaceTask = null;
|
|
55
58
|
let exporting = 0;
|
|
56
59
|
let serviceError = '';
|
|
57
|
-
const agent = new SocialAgent(() => store, getIM, () => changingWorkspace || applyingUpdate || closing);
|
|
60
|
+
const agent = new SocialAgent(() => store, getIM, () => changingWorkspace || applyingUpdate || closing, () => model.available());
|
|
58
61
|
const sources = Object.fromEntries(Object.entries(adapterFactories).map(([id, Adapter]) => {
|
|
59
62
|
let worker;
|
|
60
63
|
const browser = new Adapter(directory, options => worker.beforeRequest(options), () => store.account(id).uid);
|
|
@@ -65,6 +68,8 @@ export function createRuntime(directory = dataDirectory(), { adapterFactories =
|
|
|
65
68
|
}));
|
|
66
69
|
const { browser, worker } = sources.weibo;
|
|
67
70
|
let closing = false;
|
|
71
|
+
const business = createBusiness(() => store, sources, () => documents.drainImages());
|
|
72
|
+
const chat = new BusinessChat(() => store, business, model, { paused: () => changingWorkspace || applyingUpdate || closing || !!serviceError });
|
|
68
73
|
|
|
69
74
|
function enableAuthorJob(uid, kind) {
|
|
70
75
|
if (store.getSetting('targetEnabled:' + uid, true)) return;
|
|
@@ -90,7 +95,7 @@ export function createRuntime(directory = dataDirectory(), { adapterFactories =
|
|
|
90
95
|
if (destination === workspace) { saveWorkspaceDirectory(directory, destination); await documents.sync({ backgroundImages: true }); return; }
|
|
91
96
|
const database = join(destination, 'archive.sqlite');
|
|
92
97
|
if (existsSync(database)) throw new CaptureError('storage', '所选文件夹已有资料库,为避免覆盖,请选择一个新的文件夹');
|
|
93
|
-
if (agent.busy) throw new CaptureError('storage', '
|
|
98
|
+
if (agent.busy || chat.busy) throw new CaptureError('storage', '请等当前消息投递或 Chat 执行结束后再切换信息保存位置');
|
|
94
99
|
if (exporting) throw new CaptureError('storage', '正在导出文档,请等导出完成后再切换信息保存位置');
|
|
95
100
|
if (Object.values(sources).some(source => source.loginBusy || source.historyBusy)) throw new CaptureError('storage', '请等登录窗口打开完成后再切换信息保存位置');
|
|
96
101
|
changingWorkspace = true;
|
|
@@ -141,6 +146,11 @@ export function createRuntime(directory = dataDirectory(), { adapterFactories =
|
|
|
141
146
|
const url = new URL(req.url, 'http://' + req.headers.host);
|
|
142
147
|
const path = url.pathname.slice(PREFIX.length);
|
|
143
148
|
if (req.method === 'GET') {
|
|
149
|
+
if (path === '/chat') return json(res, 200, chat.view(url.searchParams.get('sessionId') || ''));
|
|
150
|
+
if (path === '/chat/run') {
|
|
151
|
+
const run = store.db.prepare('SELECT r.session_id AS sessionId FROM chat_runs r JOIN chat_sessions s ON s.id=r.session_id WHERE r.id=? AND s.deleted_at IS NULL').get(url.searchParams.get('id') || '');
|
|
152
|
+
return run ? json(res, 200, run) : json(res, 404, { error: '报告不存在' });
|
|
153
|
+
}
|
|
144
154
|
if (path === '/update') return json(res, 200, { update: await updates.check(url.searchParams.get('fresh') === '1') });
|
|
145
155
|
if (path === '/update/state') return json(res, 200, { update: updates.view() });
|
|
146
156
|
if (path === '/assets/xueqiu.png') {
|
|
@@ -175,6 +185,7 @@ export function createRuntime(directory = dataDirectory(), { adapterFactories =
|
|
|
175
185
|
return { id, name: source.browser.name, enabled: store.getSetting('platformEnabled:' + id, true), schedule, targetDefaults,
|
|
176
186
|
frequency: { intervalMs: intervalAt(schedule, targetDefaults.intervalMs, now), nextChangeAt: Number.isFinite(change) ? change : null,
|
|
177
187
|
nextIntervalMs: Number.isFinite(change) ? intervalAt(schedule, targetDefaults.intervalMs, change) : null },
|
|
188
|
+
connectionRecovery: source.worker.recovery(),
|
|
178
189
|
health: store.monitoringHealth(id, Math.min(source.worker.requestGapMs, source.worker.monitoringGapMs)),
|
|
179
190
|
requestGapMs: source.worker.requestGapMs, profileRequestAt: store.getSetting('profileRequestAt:' + id, 0), loginBusy: source.loginBusy, stopped: source.worker.stopping, lastFailure: store.getSetting('lastFailure:' + id, null) };
|
|
180
191
|
}),
|
|
@@ -242,7 +253,17 @@ export function createRuntime(directory = dataDirectory(), { adapterFactories =
|
|
|
242
253
|
const body = await readBody(req);
|
|
243
254
|
if (workspaceTask) return json(res, 409, { error: '正在切换信息保存位置,请稍后操作' });
|
|
244
255
|
if (updates.view().phase === 'applying') return json(res, 409, { error: '插件正在更新,请稍后操作' });
|
|
256
|
+
if (path === '/chat/message' || path === '/chat/confirm' || path === '/chat/cancel' || path === '/chat/session/delete' || path === '/chat/session/restore') {
|
|
257
|
+
try {
|
|
258
|
+
if (path === '/chat/session/delete') return json(res, 200, await chat.deleteSession(body.id));
|
|
259
|
+
if (path === '/chat/session/restore') return json(res, 200, chat.restoreSession(body.id));
|
|
260
|
+
if (path === '/chat/message') return json(res, 202, chat.start(body.message, body.sessionId || ''));
|
|
261
|
+
if (path === '/chat/confirm') return json(res, 200, chat.confirm(body.id));
|
|
262
|
+
chat.cancel(body.id); return json(res, 200, { ok: true });
|
|
263
|
+
} catch (error) { return json(res, 400, { error: error.message }); }
|
|
264
|
+
}
|
|
245
265
|
if (path === '/update') {
|
|
266
|
+
if (chat.busy) return json(res, 409, { error: '请先停止或等候当前 Chat 任务结束后再更新' });
|
|
246
267
|
if (exporting || Object.values(sources).some(source => source.loginBusy || source.historyBusy)) return json(res, 409, { error: '请等当前登录、缓存设置或导出完成后再更新' });
|
|
247
268
|
try {
|
|
248
269
|
const update = updates.start(body.version, async apply => {
|
|
@@ -303,13 +324,7 @@ export function createRuntime(directory = dataDirectory(), { adapterFactories =
|
|
|
303
324
|
const platformName = platformBrowser.name;
|
|
304
325
|
if (source.historyBusy) return json(res, 409, { error: '正在调整缓存范围,请稍候' });
|
|
305
326
|
if (path === '/targets/policy') {
|
|
306
|
-
|
|
307
|
-
if (!target || target.platform !== platform || typeof body.inherit !== 'boolean') return json(res, 400, { error: '请选择有效的博主设置' });
|
|
308
|
-
const interval = Number(body.intervalMinutes);
|
|
309
|
-
if (!body.inherit && (!Number.isInteger(interval) || interval < 1 || interval > 10080)) return json(res, 400, { error: '检查间隔为 1–10080 分钟' });
|
|
310
|
-
const policy = body.inherit ? null : { intervalMs: interval * 60000, schedule: validateSchedule(body.schedule) };
|
|
311
|
-
store.setSetting('targetPolicy:' + target.uid, policy);
|
|
312
|
-
platformWorker.rescheduleUpdates();
|
|
327
|
+
business.policy({ ...body, platform });
|
|
313
328
|
} else if (path === '/history/pause') {
|
|
314
329
|
const targets = body.uid ? [store.target(body.uid)] : body.scope === 'platform' ? store.targets().filter(target => target.platform === platform) : [];
|
|
315
330
|
if (!targets.length || targets.some(target => !target || target.platform !== platform)) return json(res, 400, { error: '请选择要暂停缓存的博主' });
|
|
@@ -377,6 +392,7 @@ export function createRuntime(directory = dataDirectory(), { adapterFactories =
|
|
|
377
392
|
} else if (path === '/login') {
|
|
378
393
|
if (source.loginBusy || platformWorker.busy) return json(res, 409, { error: '正在执行任务,请稍后打开登录窗口' });
|
|
379
394
|
source.loginBusy = true;
|
|
395
|
+
store.setSetting('connectionRecovery:' + platform, null);
|
|
380
396
|
store.setAccount({ status: 'needs_login', reason: `请在${platformName}完成登录或验证,然后点击确认连接` }, platform);
|
|
381
397
|
try { await platformBrowser.login(); } finally { source.loginBusy = false; }
|
|
382
398
|
} else if (path === '/verify') {
|
|
@@ -388,13 +404,15 @@ export function createRuntime(directory = dataDirectory(), { adapterFactories =
|
|
|
388
404
|
if (previous.uid && previous.uid !== uid) {
|
|
389
405
|
return json(res, 409, { error: '当前登录账号与此档案库绑定账号不同,请切回原账号或使用独立数据目录' });
|
|
390
406
|
}
|
|
391
|
-
|
|
407
|
+
platformWorker.confirmConnection(uid);
|
|
392
408
|
store.notice('task', `已确认${platformName}登录`);
|
|
393
409
|
} catch (error) {
|
|
394
410
|
platformWorker.failAccount(error);
|
|
395
411
|
throw error;
|
|
396
412
|
} finally { source.loginBusy = false; platformWorker.suspended = false; platformWorker.wake(); }
|
|
397
413
|
} else if (path === '/account/pause') {
|
|
414
|
+
store.setSetting('accountManualPause:' + platform, true);
|
|
415
|
+
store.setSetting('connectionRecovery:' + platform, null);
|
|
398
416
|
store.setAccount({ status: 'paused', reason: `已手动暂停${platformName}采集` }, platform);
|
|
399
417
|
} else if (path === '/targets') {
|
|
400
418
|
let uid;
|
|
@@ -442,11 +460,7 @@ export function createRuntime(directory = dataDirectory(), { adapterFactories =
|
|
|
442
460
|
if (match[2] === 'resume' && job.status === 'complete') {
|
|
443
461
|
return json(res, 409, { error: '历史任务已到可访问末尾;更新任务仍会持续检查' });
|
|
444
462
|
}
|
|
445
|
-
|
|
446
|
-
if (match[2] === 'resume' && body.enableTarget === true) enableAuthorJob(job.uid, job.kind);
|
|
447
|
-
if (match[2] === 'resume' && job.kind === 'history') store.setSetting('historyRequested:' + job.uid, true);
|
|
448
|
-
store.updateJob(job.id, { status: match[2] === 'pause' ? 'paused' : 'queued', message: match[2] === 'pause' ? '已手动暂停,断点保留' : '等待继续;账号冷却仍然有效' });
|
|
449
|
-
})();
|
|
463
|
+
business.job(job.id, match[2], body.enableTarget);
|
|
450
464
|
}
|
|
451
465
|
const clockChanges = ['/targets/start', '/monitoring', '/verify'].includes(path) || path.startsWith('/jobs/') && path.endsWith('/resume') && store.job(path.split('/')[2])?.kind === 'updates';
|
|
452
466
|
Object.values(sources).forEach(source => { if (clockChanges) source.worker.rescheduleUpdates(); source.worker.wake(); });
|
|
@@ -469,18 +483,20 @@ export function createRuntime(directory = dataDirectory(), { adapterFactories =
|
|
|
469
483
|
Object.values(sources).forEach(source => { source.worker.stopping = true; source.worker.running = false; clearTimeout(source.worker.timer); });
|
|
470
484
|
return;
|
|
471
485
|
}
|
|
486
|
+
chat.recover();
|
|
472
487
|
agent.start();
|
|
488
|
+
chat.startScheduler();
|
|
473
489
|
void documents.sync({ incremental: true }).catch(() => { documents.error = '启动文档补写中断,正文已保留,可在设置中重试'; });
|
|
474
490
|
void documents.drainImages();
|
|
475
491
|
}
|
|
476
492
|
return {
|
|
477
|
-
get store() { return store; }, get documents() { return documents; }, updates, agent, worker, browser, sources, handle, start,
|
|
493
|
+
get store() { return store; }, get documents() { return documents; }, updates, agent, chat, worker, browser, sources, handle, start,
|
|
478
494
|
async close() {
|
|
479
495
|
if (closing) return;
|
|
480
496
|
closing = true;
|
|
481
497
|
if (workspaceTask) await workspaceTask.catch(() => {});
|
|
482
498
|
documents.stopping = true;
|
|
483
|
-
await Promise.all([agent.close(), avatars.close(), ...Object.values(sources).map(source => source.worker.stop())]);
|
|
499
|
+
await Promise.all([chat.close(), agent.close(), avatars.close(), ...Object.values(sources).map(source => source.worker.stop())]);
|
|
484
500
|
if (documents.task) await documents.task;
|
|
485
501
|
if (documents.queueTask) await documents.queueTask;
|
|
486
502
|
await Promise.allSettled([...documents.imageTasks.values()]);
|
|
@@ -491,7 +507,8 @@ export function createRuntime(directory = dataDirectory(), { adapterFactories =
|
|
|
491
507
|
}
|
|
492
508
|
|
|
493
509
|
export function apply(ctx) {
|
|
494
|
-
const
|
|
510
|
+
const getService = key => { try { return ctx.get(key); } catch { return null; } };
|
|
511
|
+
const updates = createUpdates({ getService });
|
|
495
512
|
let im = null;
|
|
496
513
|
ctx.inject(['dshIm'], ready => {
|
|
497
514
|
im = ready.dshIm;
|
|
@@ -500,7 +517,7 @@ export function apply(ctx) {
|
|
|
500
517
|
ctx.effect(() => {
|
|
501
518
|
let runtime;
|
|
502
519
|
let startupError;
|
|
503
|
-
try { runtime = createRuntime(undefined, { getIM: () => im, updates }); runtime.start(); }
|
|
520
|
+
try { runtime = createRuntime(undefined, { getIM: () => im, model: hostModel(getService), updates }); runtime.start(); }
|
|
504
521
|
catch { startupError = '无法打开本地资料库,请检查信息保存位置和文件权限后重新启用插件'; }
|
|
505
522
|
const unregister = ctx.webServer.register({ kind: 'prefix', path: PREFIX,
|
|
506
523
|
handler: (req, res) => startupError ? json(res, 503, { error: startupError }) : runtime.handle(req, res) });
|
package/lib/worker.js
CHANGED
|
@@ -137,6 +137,55 @@ export class Worker {
|
|
|
137
137
|
this.store.setAccount({ next_request_at: Math.max(account.next_request_at, this.now() + gap) }, this.platform);
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
recovery() {
|
|
141
|
+
const key = 'connectionRecovery:' + this.platform;
|
|
142
|
+
let recovery = this.store.getSetting(key, null);
|
|
143
|
+
const account = this.store.account(this.platform);
|
|
144
|
+
if (this.store.getSetting('accountManualPause:' + this.platform, false) || /手动暂停/.test(account.reason || '')) return null;
|
|
145
|
+
// Upgrade previously automatic network pauses without reviving manual pauses.
|
|
146
|
+
if (!recovery && account.uid && account.status === 'paused' && account.errors >= 3 && /连续三次异常/.test(account.reason || '')) {
|
|
147
|
+
recovery = { id: randomUUID(), kind: 'network', attempts: 0, nextAt: Math.max(this.now(), account.cooldown_until) };
|
|
148
|
+
this.store.setSetting(key, recovery);
|
|
149
|
+
}
|
|
150
|
+
return recovery;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
confirmConnection(uid) {
|
|
154
|
+
const previous = this.store.account(this.platform);
|
|
155
|
+
if (previous.uid && previous.uid !== uid) throw new CaptureError('login', '当前登录账号与资料库绑定账号不同,请切回原账号');
|
|
156
|
+
this.store.transaction(() => {
|
|
157
|
+
this.store.setAccount({ uid, status: 'ready', reason: '', errors: 0 }, this.platform);
|
|
158
|
+
this.store.setSetting('connectionRecovery:' + this.platform, null);
|
|
159
|
+
this.store.setSetting('accountManualPause:' + this.platform, false);
|
|
160
|
+
})();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async recoverConnection(recovery) {
|
|
164
|
+
const key = 'connectionRecovery:' + this.platform;
|
|
165
|
+
const current = () => !this.stopping && !this.suspended && this.store.monitoringEnabled(this.platform)
|
|
166
|
+
&& this.store.getSetting(key, null)?.id === recovery.id;
|
|
167
|
+
try {
|
|
168
|
+
const uid = await this.adapter.checkLogin(); // Uses the saved HTTP session; never opens a browser.
|
|
169
|
+
if (!current()) return;
|
|
170
|
+
this.confirmConnection(uid);
|
|
171
|
+
this.prepareRestartChecks();
|
|
172
|
+
this.store.notice('task', (this.platform === 'weibo' ? '微博' : '雪球') + '连接已自动恢复,继续检查新动态', this.now());
|
|
173
|
+
} catch (error) {
|
|
174
|
+
if (!current()) return;
|
|
175
|
+
const kind = error.kind || 'internal';
|
|
176
|
+
if (['network', 'limited', 'wait'].includes(kind)) {
|
|
177
|
+
const attempts = recovery.attempts + 1;
|
|
178
|
+
const delay = Math.max(error.retryAfter || 0, Math.min(3600000, 60000 * 2 ** Math.min(attempts, 6)));
|
|
179
|
+
const until = Math.max(this.store.account(this.platform).cooldown_until, this.now() + delay);
|
|
180
|
+
this.store.setAccount({ status: 'paused', cooldown_until: until, reason: '连接暂时不可用,将自动检查恢复;无需反复点击检查连接' }, this.platform);
|
|
181
|
+
this.store.setSetting(key, { ...recovery, attempts, nextAt: until });
|
|
182
|
+
} else {
|
|
183
|
+
this.store.setSetting(key, null);
|
|
184
|
+
this.store.setAccount({ status: kind === 'login' ? 'needs_login' : kind === 'verification' ? 'needs_verification' : 'paused', reason: error.message || '自动检查未能确认授权,请检查平台登录' }, this.platform, true);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
140
189
|
async tick() {
|
|
141
190
|
if (this.stopping || this.suspended || this.busy) return;
|
|
142
191
|
if (this.running) {
|
|
@@ -144,13 +193,23 @@ export class Worker {
|
|
|
144
193
|
this.lastTickAt = this.now();
|
|
145
194
|
}
|
|
146
195
|
const account = this.store.account(this.platform);
|
|
147
|
-
if (!this.store.monitoringEnabled(this.platform)
|
|
196
|
+
if (!this.store.monitoringEnabled(this.platform)) return;
|
|
148
197
|
try { if (this.now() >= this.claimAfter) this.claim(); } catch (error) {
|
|
149
198
|
this.stopping = true;
|
|
150
199
|
this.running = false;
|
|
151
200
|
clearTimeout(this.timer);
|
|
152
201
|
throw error;
|
|
153
202
|
}
|
|
203
|
+
const recovery = this.recovery();
|
|
204
|
+
if (account.status !== 'ready') {
|
|
205
|
+
const activeTasks = this.store.jobs().some(job => job.status === 'queued' && this.store.target(job.uid)?.platform === this.platform && this.store.monitoringEnabled(this.platform, job.uid));
|
|
206
|
+
if (!activeTasks) return;
|
|
207
|
+
if (!recovery || typeof this.adapter.checkLogin !== 'function' || Math.max(recovery.nextAt, account.cooldown_until, account.next_request_at) > this.now()) return;
|
|
208
|
+
this.busy = this.recoverConnection(recovery);
|
|
209
|
+
try { await this.busy; } finally { this.busy = null; }
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (account.cooldown_until > this.now()) return;
|
|
154
213
|
if (this.resumeCheckPending) {
|
|
155
214
|
this.prepareRestartChecks();
|
|
156
215
|
this.resumeCheckPending = false;
|
|
@@ -325,14 +384,20 @@ export class Worker {
|
|
|
325
384
|
failAccount(error) {
|
|
326
385
|
const account = this.store.account(this.platform);
|
|
327
386
|
let message = error.message;
|
|
387
|
+
const manualPause = this.store.getSetting('accountManualPause:' + this.platform, false) || /手动暂停/.test(account.reason || '');
|
|
388
|
+
if (manualPause) return message;
|
|
328
389
|
if (error.kind === 'limited' || error.kind === 'network') {
|
|
329
390
|
const errors = account.errors + 1;
|
|
330
391
|
const delay = Math.max(error.retryAfter || 0, Math.min(3600000, 60000 * 2 ** Math.min(errors, 6)));
|
|
331
|
-
if (errors >= 3) message += '
|
|
392
|
+
if (errors >= 3) message += account.uid && typeof this.adapter.checkLogin === 'function' ? ';连续三次连接异常,冷却后自动检查恢复' : ';连续三次连接异常,已暂停,请检查平台授权';
|
|
332
393
|
this.store.setAccount({ errors, cooldown_until: Math.max(account.cooldown_until, this.now() + delay), reason: message,
|
|
333
394
|
status: errors >= 3 ? 'paused' : account.status }, this.platform);
|
|
395
|
+
if (errors >= 3 && account.uid && typeof this.adapter.checkLogin === 'function') this.store.setSetting('connectionRecovery:' + this.platform,
|
|
396
|
+
{ id: randomUUID(), kind: error.kind, attempts: 0, nextAt: this.store.account(this.platform).cooldown_until });
|
|
334
397
|
} else if (error.kind === 'login' || error.kind === 'verification') {
|
|
335
|
-
|
|
398
|
+
const recheck = error.kind === 'login' && account.uid && typeof this.adapter.checkLogin === 'function';
|
|
399
|
+
this.store.setSetting('connectionRecovery:' + this.platform, recheck ? { id: randomUUID(), kind: 'login', attempts: 0, nextAt: Math.max(this.now() + 60000, account.cooldown_until, account.next_request_at) } : null);
|
|
400
|
+
this.store.setAccount({ status: error.kind === 'login' ? 'needs_login' : 'needs_verification', reason: message }, this.platform, !recheck);
|
|
336
401
|
}
|
|
337
402
|
return message;
|
|
338
403
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-social-trace",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "Social Trace:微博与雪球增量监控、可视化拓扑、历史缓存与本地检索",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,18 +33,25 @@
|
|
|
33
33
|
"./package.json": "./package.json"
|
|
34
34
|
},
|
|
35
35
|
"files": [
|
|
36
|
-
"lib",
|
|
37
|
-
"
|
|
36
|
+
"lib/*.js",
|
|
37
|
+
"lib/platforms/**/*.js",
|
|
38
|
+
"assets/icon.svg",
|
|
39
|
+
"assets/weibo.ico",
|
|
40
|
+
"assets/xueqiu.png",
|
|
41
|
+
"assets/NOTICE.md",
|
|
38
42
|
"cordis.patch.yml",
|
|
39
43
|
"README.md",
|
|
40
|
-
"LICENSE"
|
|
44
|
+
"LICENSE",
|
|
45
|
+
"THIRD_PARTY_NOTICES"
|
|
41
46
|
],
|
|
42
47
|
"scripts": {
|
|
43
48
|
"start": "node scripts/preview.mjs",
|
|
44
49
|
"login": "node scripts/login.mjs",
|
|
45
50
|
"probe": "node scripts/probe.mjs",
|
|
46
51
|
"test": "node --test test/*.test.mjs",
|
|
47
|
-
"test:ui": "node --test test/client.ui.mjs"
|
|
52
|
+
"test:ui": "node --test test/client.ui.mjs",
|
|
53
|
+
"build": "node scripts/build-markdown.mjs",
|
|
54
|
+
"prepack": "npm run build"
|
|
48
55
|
},
|
|
49
56
|
"icon": "assets/icon.svg",
|
|
50
57
|
"dsh": {
|
|
@@ -63,7 +70,11 @@
|
|
|
63
70
|
"playwright": "1.64.1"
|
|
64
71
|
},
|
|
65
72
|
"devDependencies": {
|
|
73
|
+
"esbuild": "0.25.12",
|
|
66
74
|
"react": "18.3.1",
|
|
67
|
-
"react-dom": "18.3.1"
|
|
75
|
+
"react-dom": "18.3.1",
|
|
76
|
+
"react-markdown": "10.1.0",
|
|
77
|
+
"remark-cjk-friendly": "2.3.1",
|
|
78
|
+
"remark-gfm": "4.0.1"
|
|
68
79
|
}
|
|
69
80
|
}
|