dsh-browser-playwright-codex 0.7.1 → 0.7.2
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/CHANGELOG.md +9 -0
- package/lib/client.js +85 -91
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## 0.7.2 — 2026-10-19
|
|
2
|
+
|
|
3
|
+
一句话摘要:**设置卡推倒重做** —— 用户要求按 DSH 设置页规范重新设计:首屏只留「主开关 + 三个分区摘要」,所有长说明收进折叠。0.7.1 因卡在 npm staged 状态作废(版本号让给这一版)。
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **设置卡重做**:主开关一行;「页面读取方式」两个紧凑单选项 + 一个「两种模式到底差在哪」折叠;「工具权限」三个开关(任意 JS / 原始 CDP / 诊断工具)+ 一个「这些开关具体放行什么」折叠,标题行直接显示「N / 3 已开启」;「已登录的站点」默认收起,标题行只给「N 个域 · M 个 cookie」,展开后仍按公司分组折叠。名单每行只保留域名 + 说明 + cookie 数,逐个站点的四个 OWASP 信号不再塞在主视图里。
|
|
8
|
+
- 卡片文案统一为"人话",不再出现 evaluate / cdp / 引擎 / legacy / aria 这些词。
|
|
9
|
+
|
|
1
10
|
# Changelog
|
|
2
11
|
|
|
3
12
|
Bilingual by intent: the version headings and the summary of each release are given in both
|
package/lib/client.js
CHANGED
|
@@ -372,18 +372,26 @@ window.__ModuleLoader__.load({
|
|
|
372
372
|
const statusLabel = enabled === null ? '读取中…' : (enabled ? '已启用' : '已禁用');
|
|
373
373
|
const current = ENGINE_OPTIONS.find((o) => o.id === engine) || null;
|
|
374
374
|
|
|
375
|
+
const GATE_SHORT = {
|
|
376
|
+
allowEvaluate: '页面里的任意 JavaScript',
|
|
377
|
+
allowCdp: '原始 CDP 调试协议',
|
|
378
|
+
diagnostics: '控制台消息 / 网络请求',
|
|
379
|
+
};
|
|
380
|
+
const ENGINE_SHORT = { legacy: '最稳,奇怪网站也不容易出错', aria: '更准,复杂页面优先' };
|
|
381
|
+
|
|
375
382
|
return h('div', { style: S.page }, [
|
|
376
383
|
h('h2', { key: 'title', style: S.title }, '浏览器'),
|
|
377
384
|
h('p', { key: 'intro', style: S.intro },
|
|
378
|
-
'让 AI
|
|
385
|
+
'让 AI 用你的登录状态替你上网办事。这里的改动立刻生效,不需要重启。'),
|
|
386
|
+
|
|
387
|
+
// ---- 主开关 ----
|
|
379
388
|
h('div', { key: 'row', style: S.row }, [
|
|
380
389
|
h(Dot, { key: 'dot', state }),
|
|
381
390
|
h('div', { key: 'text', style: { flex: 1, minWidth: 0 } }, [
|
|
382
391
|
h('div', { key: 'label', style: S.rowTitle }, '允许 AI 操作网页'),
|
|
383
|
-
h('div', { key: 'desc', style: S.rowDesc },
|
|
392
|
+
h('div', { key: 'desc', style: S.rowDesc },
|
|
384
393
|
h(Tag, { key: 'status' }, statusLabel),
|
|
385
|
-
'
|
|
386
|
-
]),
|
|
394
|
+
'关闭后窗口会关掉,登录状态保留。'),
|
|
387
395
|
]),
|
|
388
396
|
h(Toggle, {
|
|
389
397
|
key: 'toggle',
|
|
@@ -393,37 +401,40 @@ window.__ModuleLoader__.load({
|
|
|
393
401
|
label: '允许 AI 操作网页',
|
|
394
402
|
}),
|
|
395
403
|
]),
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
404
|
+
|
|
405
|
+
// ---- 页面读取方式 ----
|
|
406
|
+
h('div', { key: 'engineGroup', style: S.group }, [
|
|
407
|
+
'页面读取方式',
|
|
408
|
+
h('span', { key: 'cur', style: S.groupCount },
|
|
409
|
+
current ? ' 当前:' + current.label : ' 读取中…'),
|
|
410
|
+
]),
|
|
411
|
+
h('div', { key: 'engines', style: { ...S.list, marginTop: 8 } },
|
|
412
|
+
ENGINE_OPTIONS.map((o) => h(RadioRow, {
|
|
402
413
|
key: o.id,
|
|
403
|
-
p: o,
|
|
414
|
+
p: { id: o.id, label: o.label, hint: ENGINE_SHORT[o.id] || '' },
|
|
404
415
|
selected: engine === o.id,
|
|
405
416
|
disabled: engine === null || busy,
|
|
406
417
|
onPick: chooseEngine,
|
|
407
418
|
}))),
|
|
408
|
-
h(
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
419
|
+
h(Notes, {
|
|
420
|
+
key: 'engineNotes',
|
|
421
|
+
title: '两种模式到底差在哪',
|
|
422
|
+
children: h('div', null, ENGINE_OPTIONS.map((o) => h('p', { key: o.id, style: S.note },
|
|
423
|
+
o.label + ':' + o.hint))),
|
|
424
|
+
}),
|
|
425
|
+
|
|
426
|
+
// ---- 工具权限 ----
|
|
427
|
+
h('div', { key: 'gateGroup', style: S.group }, [
|
|
428
|
+
'工具权限',
|
|
429
|
+
h('span', { key: 'sum', style: S.groupCount },
|
|
430
|
+
gatesOn === 0 ? ' 全部关闭' : ' ' + gatesOn + ' / ' + GATE_ROWS.length + ' 已开启'),
|
|
414
431
|
]),
|
|
415
|
-
h('div', { key: '
|
|
416
|
-
|
|
417
|
-
h('p', { style: S.note },
|
|
418
|
-
'这两个开关控制的是"能力",不是效果微调:打开"任意 JavaScript"就等于把页面里的东西都交给 AI。'
|
|
419
|
-
+ '插件包里默认关闭 —— 因为包是发给每个安装者的,默认打开等于替所有人做了这个决定;'
|
|
420
|
-
+ '这里打开的是你自己机器上的选择,记在你自己机器的配置层里。')),
|
|
421
|
-
h('div', { key: 'gates', style: { ...S.list, marginTop: 8 } }, GATE_ROWS.map((row) =>
|
|
422
|
-
h('div', { key: row.key, style: S.row }, [
|
|
432
|
+
h('div', { key: 'gates', style: { ...S.list, marginTop: 8 } },
|
|
433
|
+
GATE_ROWS.map((row) => h('div', { key: row.key, style: S.row }, [
|
|
423
434
|
h(Dot, { key: 'dot', state: gateState(row.key) }),
|
|
424
435
|
h('div', { key: 'text', style: { flex: 1, minWidth: 0 } }, [
|
|
425
436
|
h('div', { key: 'label', style: S.rowTitle }, row.label),
|
|
426
|
-
h('div', { key: 'desc', style: S.rowDesc }, row.hint),
|
|
437
|
+
h('div', { key: 'desc', style: S.rowDesc }, GATE_SHORT[row.key] || row.hint),
|
|
427
438
|
]),
|
|
428
439
|
h(Toggle, {
|
|
429
440
|
key: 'toggle',
|
|
@@ -433,26 +444,40 @@ window.__ModuleLoader__.load({
|
|
|
433
444
|
label: row.label,
|
|
434
445
|
}),
|
|
435
446
|
]))),
|
|
436
|
-
h(
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
447
|
+
h(Notes, {
|
|
448
|
+
key: 'gateNotes',
|
|
449
|
+
title: '这些开关具体放行什么',
|
|
450
|
+
children: h('div', null, [
|
|
451
|
+
h('p', { key: 'evaluate', style: S.note },
|
|
452
|
+
'页面里执行任意 JavaScript:能读走页面内容,也能用你的登录状态去点、去填、去提交。真正需要时才打开。'),
|
|
453
|
+
h('p', { key: 'cdp', style: S.note },
|
|
454
|
+
'原始 CDP 协议:只放行查看类和模拟输入类命令;读 Cookie/存储、改网络、动安全设置一律被拦。排查疑难页面用。'),
|
|
455
|
+
h('p', { key: 'diagnostics', style: S.note },
|
|
456
|
+
'控制台消息 + 网络请求:能读出页面报错和最近的请求(URL、状态、失败原因)。平时用不到,遇到"点了没反应"再打开。'),
|
|
457
|
+
h('p', { key: 'effect', style: S.note },
|
|
458
|
+
'工具在热重载后出现或消失(约 1-2 秒);若一直没变,重启一次 DSH。'),
|
|
459
|
+
]),
|
|
460
|
+
}),
|
|
461
|
+
|
|
462
|
+
// ---- 已登录站点(默认收起) ----
|
|
440
463
|
h('div', {
|
|
441
464
|
key: 'loginsGroup',
|
|
442
465
|
style: { ...S.group, cursor: 'pointer', userSelect: 'none' },
|
|
443
466
|
onClick: () => setLoginsOpen((open) => !open),
|
|
444
|
-
},
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
467
|
+
}, [
|
|
468
|
+
'已登录的站点',
|
|
469
|
+
h('span', { key: 'count', style: S.groupCount },
|
|
470
|
+
logins === null || logins.sites === undefined
|
|
471
|
+
? ' 读取中…'
|
|
472
|
+
: ' ' + logins.sites.length + ' 个域 · '
|
|
473
|
+
+ logins.sites.reduce((acc, site) => acc + site.cookies, 0) + ' 个 cookie'),
|
|
474
|
+
h('span', { key: 'hint', style: { ...S.groupCount, marginLeft: 8 } },
|
|
475
|
+
loginsOpen ? '点击收起' : '点击展开'),
|
|
476
|
+
]),
|
|
477
|
+
loginsOpen ? h(Notes, { key: 'loginsNotes', title: '这份名单怎么来的' },
|
|
450
478
|
h('p', { style: S.note },
|
|
451
|
-
'
|
|
452
|
-
+ '
|
|
453
|
-
+ 'httpOnly 且(会话级 或 值像令牌),名字不作为依据。'
|
|
454
|
-
+ '每一行的说明来自网页自己的标题,或一份离线对照表(认不出的就直接显示域名,不编)。'
|
|
455
|
-
+ '这里不显示 cookie 的名字和值;名单只存在于本机内存、不写磁盘;也没有任何工具能让 AI 看到它。')) : null,
|
|
479
|
+
'只列这台浏览器真的访问过的域;第三方统计/广告域已排除。判据是 OWASP 的会话特征,'
|
|
480
|
+
+ '名字不作为依据;不显示 cookie 的名字和值,名单只在本机内存,AI 看不到。')) : null,
|
|
456
481
|
loginsOpen === false
|
|
457
482
|
? null
|
|
458
483
|
: logins === null
|
|
@@ -462,15 +487,11 @@ window.__ModuleLoader__.load({
|
|
|
462
487
|
const pool = showAll ? logins.sites : logins.sites.filter((s) => s.likely);
|
|
463
488
|
const visible = pool.filter((s) => hiddenDomains.indexOf(s.domain) < 0);
|
|
464
489
|
const hiddenCount = pool.length - visible.length;
|
|
465
|
-
// 同一家公司的域折叠成一行:真实名单里京东系二十来个域各自占一行,小半屏都是它。
|
|
466
490
|
const byGroup = new Map();
|
|
467
491
|
const items = [];
|
|
468
492
|
for (const site of visible) {
|
|
469
493
|
const key = site.group ? site.group.key : null;
|
|
470
|
-
if (key === null) {
|
|
471
|
-
items.push({ kind: 'single', site });
|
|
472
|
-
continue;
|
|
473
|
-
}
|
|
494
|
+
if (key === null) { items.push({ kind: 'single', site }); continue; }
|
|
474
495
|
let group = byGroup.get(key);
|
|
475
496
|
if (group === undefined) {
|
|
476
497
|
group = { kind: 'group', key, label: site.group.label, sites: [] };
|
|
@@ -479,7 +500,6 @@ window.__ModuleLoader__.load({
|
|
|
479
500
|
}
|
|
480
501
|
group.sites.push(site);
|
|
481
502
|
}
|
|
482
|
-
// 组里只剩一个域就没有折叠的意义,退回单行。
|
|
483
503
|
for (let i = items.length - 1; i >= 0; i -= 1) {
|
|
484
504
|
if (items[i].kind === 'group' && items[i].sites.length === 1)
|
|
485
505
|
items[i] = { kind: 'single', site: items[i].sites[0] };
|
|
@@ -492,16 +512,12 @@ window.__ModuleLoader__.load({
|
|
|
492
512
|
h('div', { key: 'text', style: { flex: 1, minWidth: 0 } }, [
|
|
493
513
|
h('div', { key: 'label', style: S.rowTitle }, [
|
|
494
514
|
site.likely ? h(Tag, { key: 'tag' }, '推测登录') : null,
|
|
495
|
-
// 说明优先:光看域名认不出来是这个功能最初的痛点。
|
|
496
515
|
site.alias || site.title || site.domain,
|
|
497
516
|
]),
|
|
498
517
|
h('div', { key: 'desc', style: S.rowDesc },
|
|
499
518
|
(site.alias ? site.domain + ' ' : '')
|
|
500
519
|
+ (site.title && site.title !== site.alias ? '「' + site.title + '」 ' : '')
|
|
501
|
-
+ site.cookies + ' 个 cookie
|
|
502
|
-
+ ' / 会话 ' + site.sessionLike
|
|
503
|
-
+ ' / 值像令牌 ' + site.tokenLike + ')'
|
|
504
|
-
+ (site.signals.length > 0 ? ' ' + site.signals.join(' · ') : '')),
|
|
520
|
+
+ site.cookies + ' 个 cookie'),
|
|
505
521
|
]),
|
|
506
522
|
h(Btn, {
|
|
507
523
|
key: 'hide',
|
|
@@ -515,74 +531,52 @@ window.__ModuleLoader__.load({
|
|
|
515
531
|
const renderItem = (item) => {
|
|
516
532
|
if (item.kind === 'single') return rowFor(item.site, false);
|
|
517
533
|
const open = openGroups.indexOf(item.key) >= 0;
|
|
518
|
-
const anyLikely = item.sites.some((s) => s.likely);
|
|
519
534
|
return h('div', { key: 'group-' + item.key }, [
|
|
520
535
|
h('div', { key: 'head', style: S.row }, [
|
|
521
536
|
h('div', { key: 'text', style: { flex: 1, minWidth: 0 } }, [
|
|
522
|
-
h('div', { key: 'label', style: S.rowTitle },
|
|
523
|
-
|
|
524
|
-
item.label + '(' + item.sites.length + ' 个域)',
|
|
525
|
-
]),
|
|
537
|
+
h('div', { key: 'label', style: S.rowTitle },
|
|
538
|
+
item.label + '(' + item.sites.length + ' 个域)'),
|
|
526
539
|
h('div', { key: 'desc', style: S.rowDesc },
|
|
527
|
-
sum(item.sites, (s) => s.cookies) + ' 个 cookie
|
|
528
|
-
+ sum(item.sites, (s) => s.httpOnly) + ' 会话 '
|
|
529
|
-
+ sum(item.sites, (s) => s.sessionLike) + ' 值像令牌 '
|
|
530
|
-
+ sum(item.sites, (s) => s.tokenLike)),
|
|
540
|
+
sum(item.sites, (s) => s.cookies) + ' 个 cookie'),
|
|
531
541
|
]),
|
|
532
542
|
h(Btn, {
|
|
533
543
|
key: 'toggle',
|
|
534
|
-
onClick: () =>
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
: openGroups.concat([item.key]));
|
|
538
|
-
},
|
|
544
|
+
onClick: () => setOpenGroups(open
|
|
545
|
+
? openGroups.filter((k) => k !== item.key)
|
|
546
|
+
: openGroups.concat([item.key])),
|
|
539
547
|
}, open ? '收起' : '展开'),
|
|
540
548
|
]),
|
|
541
|
-
open
|
|
542
|
-
? h('div', { key: 'members' }, item.sites.map((s) => rowFor(s, true)))
|
|
543
|
-
: null,
|
|
549
|
+
open ? h('div', { key: 'members' }, item.sites.map((s) => rowFor(s, true))) : null,
|
|
544
550
|
]);
|
|
545
551
|
};
|
|
546
552
|
return h('div', { key: 'loginsBody' }, [
|
|
547
553
|
h('div', { key: 'list', style: { ...S.list, marginTop: 8 } },
|
|
548
554
|
visible.length === 0
|
|
549
555
|
? h('div', { key: 'none', style: S.tip },
|
|
550
|
-
hiddenCount > 0
|
|
551
|
-
? '这一页都被你隐藏了。'
|
|
552
|
-
: '还没发现登录过的站点(用这个浏览器登录过网站后,这里会出现)。')
|
|
556
|
+
hiddenCount > 0 ? '这一页都被你隐藏了。' : '还没发现登录过的站点。')
|
|
553
557
|
: items.map(renderItem)),
|
|
554
558
|
h('div', { key: 'loginsFooter', style: S.footer }, [
|
|
555
559
|
h('span', { key: 'src', style: S.tip },
|
|
556
|
-
(logins.source === 'live' ? '
|
|
557
|
-
+ '
|
|
558
|
-
+ ' 你在 DSH 里访问过 ' + logins.total + ' 个域,其中 '
|
|
559
|
-
+ logins.likelyTotal + ' 个像登录态'
|
|
560
|
-
+ (logins.thirdPartyTotal > 0
|
|
561
|
-
? ';另有 ' + logins.thirdPartyTotal + ' 个第三方域(统计/广告)已排除'
|
|
562
|
-
: '')
|
|
563
|
-
+ (logins.attribution === 'unfiltered'
|
|
564
|
-
? '(这次没拿到浏览器打开的页面列表,未做第一方过滤:下面可能有第三方域)'
|
|
565
|
-
: '')),
|
|
560
|
+
(logins.source === 'live' ? '实时' : '上次快照')
|
|
561
|
+
+ (logins.attribution === 'unfiltered' ? '(未做第一方过滤)' : '')),
|
|
566
562
|
h(Btn, {
|
|
567
563
|
key: 'all',
|
|
568
564
|
onClick: () => setShowAll(!showAll),
|
|
569
|
-
}, showAll
|
|
570
|
-
? '只看像登录态的 ' + logins.likelyTotal + ' 个'
|
|
571
|
-
: '显示其余 ' + (logins.total - logins.likelyTotal) + ' 个'),
|
|
572
|
-
|
|
565
|
+
}, showAll ? '只看像登录态的' : '显示其余 ' + (logins.total - logins.likelyTotal) + ' 个'),
|
|
573
566
|
hiddenCount > 0
|
|
574
567
|
? h(Btn, {
|
|
575
568
|
key: 'reset',
|
|
576
|
-
onClick: () => {
|
|
577
|
-
|
|
578
|
-
writeHiddenDomains([]);
|
|
579
|
-
},
|
|
580
|
-
}, '恢复显示 ' + hiddenCount + ' 个被隐藏的')
|
|
569
|
+
onClick: () => { setHiddenDomains([]); writeHiddenDomains([]); },
|
|
570
|
+
}, '恢复 ' + hiddenCount + ' 个被隐藏的')
|
|
581
571
|
: null,
|
|
582
572
|
]),
|
|
583
573
|
]);
|
|
584
574
|
})(),
|
|
575
|
+
|
|
585
576
|
msg ? h('div', { key: 'msg', style: S.msg }, msg) : null,
|
|
577
|
+
h('div', { key: 'foot', style: S.footer }, [
|
|
578
|
+
h(Btn, { key: 'refresh', onClick: refresh, disabled: busy }, '刷新状态'),
|
|
579
|
+
]),
|
|
586
580
|
]);
|
|
587
581
|
}
|
|
588
582
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-browser-playwright-codex",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Browser automation for DeepSeek Harness, one plugin: fill in forms, scrape lists and work across tabs in real pages, using the logins you already have. Runs in a visible window that stays quiet when minimised; native dialogs are parked instead of confirmed, CAPTCHA pages are flagged with a note that says stop and hand the page back, password / one-time-code / card fields are refused by browser_fill, and the Settings card (enable/disable, page-reading mode, capability gates) now lives inside this package.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|