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 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
- h('div', { key: 'group', style: S.group }, '页面识别方式'),
397
- h(Notes, { key: 'groupNotes', title: '说明' },
398
- h('p', { style: S.note },
399
- '决定它怎么"看"一个网页:页面上有哪些能点的、能填的。两种都能正常用,选哪个看你常去的网站。')),
400
- h('div', { key: 'engines', style: { ...S.list, marginTop: 8 } }, ENGINE_OPTIONS.map((o) =>
401
- h(RadioRow, {
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('div', { key: 'footer', style: S.footer }, [
409
- h(Btn, { key: 'refresh', onClick: refresh, disabled: busy }, '刷新状态'),
410
- current
411
- ? h('span', { key: 'cur', style: S.tip },
412
- '当前:' + current.label + (engineUserChosen ? '(你选的)' : '(默认)'))
413
- : null,
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: 'gatesGroup', style: S.group }, '强力工具(默认关闭)'),
416
- h(Notes, { key: 'gatesNotes', title: '说明' },
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('div', { key: 'gatesFooter', style: S.footer },
437
- gates.allowEvaluate === null && gates.allowCdp === null
438
- ? h('span', { key: 'unknown', style: S.tip }, '当前:读取不到(这个 profile 里的插件版本还没上报闸门状态)')
439
- : h('span', { key: 'sum', style: S.tip }, '当前:' + (gatesOn === 0 ? '都关着' : gatesOn + ' 个开着'))),
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
- + (logins === null || logins.sites === undefined
446
- ? ''
447
- : ' · ' + logins.sites.length + ' 个域 · ' + logins.sites.reduce((acc, site) => acc + site.cookies, 0) + ' 个 cookie')
448
- + (loginsOpen ? ' ▾ 点击收起' : ' ▸ 点击展开')),
449
- loginsOpen ? h(Notes, { key: 'loginsNotes', title: '说明' },
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
- '只列**这台浏览器真的访问过**的域(你在 DSH 里打开过的页面),第三方统计/广告域已排除 ——'
452
- + '别人的 cookie 属于你当时浏览的那个站,不代表你登录了它。判据是 OWASP 的会话特征:'
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(httpOnly ' + site.httpOnly
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
- anyLikely ? h(Tag, { key: 'tag' }, '推测登录') : null,
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 httpOnly '
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
- setOpenGroups(open
536
- ? openGroups.filter((k) => k !== item.key)
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
- + ' ' + new Date(logins.capturedAt).toLocaleTimeString()
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
- setHiddenDomains([]);
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.1",
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",