wezard 2.4.0 → 2.5.0

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.
Files changed (101) hide show
  1. package/CLAUDE.md +1 -1
  2. package/README.md +113 -57
  3. package/cli/wezard.sh +2 -0
  4. package/config.example.jsonc +7 -4
  5. package/dist/cli/wx.js +78 -0
  6. package/dist/cli/wx.js.map +1 -0
  7. package/dist/daemon/aibot-media.js +62 -0
  8. package/dist/daemon/aibot-media.js.map +1 -0
  9. package/dist/daemon/approval.js +30 -20
  10. package/dist/daemon/approval.js.map +1 -1
  11. package/dist/daemon/ask.js +3 -2
  12. package/dist/daemon/ask.js.map +1 -1
  13. package/dist/daemon/cc-session.js +27 -4
  14. package/dist/daemon/cc-session.js.map +1 -1
  15. package/dist/daemon/chat-log.js +2 -2
  16. package/dist/daemon/chat-log.js.map +1 -1
  17. package/dist/daemon/chat-name.js +6 -2
  18. package/dist/daemon/chat-name.js.map +1 -1
  19. package/dist/daemon/config-api.js +25 -10
  20. package/dist/daemon/config-api.js.map +1 -1
  21. package/dist/daemon/direction.js +53 -0
  22. package/dist/daemon/direction.js.map +1 -0
  23. package/dist/daemon/ilink.js +162 -0
  24. package/dist/daemon/ilink.js.map +1 -0
  25. package/dist/daemon/inbound.js +64 -18
  26. package/dist/daemon/inbound.js.map +1 -1
  27. package/dist/daemon/index.js +415 -109
  28. package/dist/daemon/index.js.map +1 -1
  29. package/dist/daemon/jobs.js +4 -3
  30. package/dist/daemon/jobs.js.map +1 -1
  31. package/dist/daemon/last-response.js +6 -3
  32. package/dist/daemon/last-response.js.map +1 -1
  33. package/dist/daemon/media.js +108 -0
  34. package/dist/daemon/media.js.map +1 -0
  35. package/dist/daemon/mirror-bridge.js +111 -93
  36. package/dist/daemon/mirror-bridge.js.map +1 -1
  37. package/dist/daemon/model-select.js +159 -54
  38. package/dist/daemon/model-select.js.map +1 -1
  39. package/dist/daemon/notices.js +59 -12
  40. package/dist/daemon/notices.js.map +1 -1
  41. package/dist/daemon/outbound.js +2 -1
  42. package/dist/daemon/outbound.js.map +1 -1
  43. package/dist/daemon/peers.js +46 -27
  44. package/dist/daemon/peers.js.map +1 -1
  45. package/dist/daemon/pending-items.js +1 -1
  46. package/dist/daemon/pending-items.js.map +1 -1
  47. package/dist/daemon/reach.js +6 -0
  48. package/dist/daemon/reach.js.map +1 -0
  49. package/dist/daemon/receipts.js +149 -7
  50. package/dist/daemon/receipts.js.map +1 -1
  51. package/dist/daemon/route.js +11 -4
  52. package/dist/daemon/route.js.map +1 -1
  53. package/dist/daemon/spawn-tmux.js +157 -60
  54. package/dist/daemon/spawn-tmux.js.map +1 -1
  55. package/dist/daemon/steward-guard.js +48 -7
  56. package/dist/daemon/steward-guard.js.map +1 -1
  57. package/dist/daemon/top-only-nudge.js +29 -0
  58. package/dist/daemon/top-only-nudge.js.map +1 -0
  59. package/dist/daemon/weixin-cmd.js +268 -0
  60. package/dist/daemon/weixin-cmd.js.map +1 -0
  61. package/dist/daemon/weixin-port.js +226 -0
  62. package/dist/daemon/weixin-port.js.map +1 -0
  63. package/dist/daemon/weixin.js +772 -0
  64. package/dist/daemon/weixin.js.map +1 -0
  65. package/dist/daemon/wizard.js +56 -18
  66. package/dist/daemon/wizard.js.map +1 -1
  67. package/dist/mcp/server.js +43 -23
  68. package/dist/mcp/server.js.map +1 -1
  69. package/dist/shared/chat-http.js +61 -20
  70. package/dist/shared/chat-http.js.map +1 -1
  71. package/dist/shared/chat-render.js +9 -1
  72. package/dist/shared/chat-render.js.map +1 -1
  73. package/dist/shared/chat-view.js +16 -2
  74. package/dist/shared/chat-view.js.map +1 -1
  75. package/dist/shared/cli-backends.js +3 -0
  76. package/dist/shared/cli-backends.js.map +1 -1
  77. package/dist/shared/config.js +45 -10
  78. package/dist/shared/config.js.map +1 -1
  79. package/dist/shared/json-map-store.js +1 -0
  80. package/dist/shared/json-map-store.js.map +1 -1
  81. package/dist/shared/reminder.js +1 -0
  82. package/dist/shared/reminder.js.map +1 -1
  83. package/dist/shared/role-render.js +7 -4
  84. package/dist/shared/role-render.js.map +1 -1
  85. package/dist/shared/role-view.js +116 -37
  86. package/dist/shared/role-view.js.map +1 -1
  87. package/dist/shared/seen-log.js +26 -0
  88. package/dist/shared/seen-log.js.map +1 -0
  89. package/dist/shared/session-label.js +84 -31
  90. package/dist/shared/session-label.js.map +1 -1
  91. package/dist/shared/turn-state.js +1 -1
  92. package/dist/shared/turn-state.js.map +1 -1
  93. package/dist/shared/world.js.map +1 -1
  94. package/dist/shared/wx-text.js +101 -0
  95. package/dist/shared/wx-text.js.map +1 -0
  96. package/dist/svr/index.js +5 -2
  97. package/dist/svr/index.js.map +1 -1
  98. package/hooks/pre-tool-use.sh +9 -6
  99. package/package.json +6 -2
  100. package/web/chat.css +32 -5
  101. package/web/chat.js +160 -92
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wezard",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "Bridge Claude Code and WeCom: PreToolUse approval cards + bidirectional tmux session mirror with image paste",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -41,6 +41,8 @@
41
41
  "dist/cli/sync.js.map",
42
42
  "dist/cli/audit.js",
43
43
  "dist/cli/audit.js.map",
44
+ "dist/cli/wx.js",
45
+ "dist/cli/wx.js.map",
44
46
  "dist/daemon/**/*.js",
45
47
  "dist/daemon/**/*.js.map",
46
48
  "dist/mcp/**/*.js",
@@ -71,7 +73,7 @@
71
73
  "LICENSE"
72
74
  ],
73
75
  "scripts": {
74
- "build": "tsc -p tsconfig.json && chmod +x dist/cli/init.js dist/cli/migrate.js dist/cli/audit.js dist/daemon/index.js dist/mcp/server.js dist/svr/index.js",
76
+ "build": "tsc -p tsconfig.json && chmod +x dist/cli/init.js dist/cli/migrate.js dist/cli/audit.js dist/cli/wx.js dist/daemon/index.js dist/mcp/server.js dist/svr/index.js",
75
77
  "dev:daemon": "tsx daemon/index.ts",
76
78
  "dev:cli": "tsx cli/init.ts",
77
79
  "typecheck": "tsc --noEmit",
@@ -87,11 +89,13 @@
87
89
  "diff": "^9.0.0",
88
90
  "jsonc-parser": "^3.3.1",
89
91
  "pino": "^9.5.0",
92
+ "qrcode": "^1.5.4",
90
93
  "zod": "^3.23.8"
91
94
  },
92
95
  "devDependencies": {
93
96
  "@types/diff": "^7.0.2",
94
97
  "@types/node": "^22.0.0",
98
+ "@types/qrcode": "^1.5.6",
95
99
  "tsx": "^4.19.0",
96
100
  "typescript": "^5.6.0"
97
101
  },
package/web/chat.css CHANGED
@@ -20,6 +20,8 @@
20
20
  --priv: #9a5b10; /* 私聊 */
21
21
  --human: #12715a; /* 人名 —— 青绿, 与 wizard (墨色) 和群名 (暗金) 三分 */
22
22
  --chat: #7d6410; /* 群名 —— 暗金, 与 role 名 (墨色 + 视角色的点) 分开: 群是频道不是 role */
23
+ --wezard: #6b3fa0; /* 系统 wezard (守护进程与认不出的发话方) —— 紫金: 紫起金收, 两端都压暗到正文对比度 */
24
+ --wezard-gold: #9a6a00;
23
25
  /* 头像底: 不透明, 且由 card/accent 推出 —— 叠在选中/高亮/关系图任何底色上都不变色, 换主题时随 token 走。 */
24
26
  --av-bg: var(--card);
25
27
  --av-line: var(--line);
@@ -46,6 +48,11 @@ button { font: inherit; color: inherit }
46
48
  .nm.human::before, .nm.task::before, .nm.system::before, .nm.chat::before { content: none }
47
49
  .nm.chat, .ch-who .sub .up { color: var(--chat) }
48
50
  .nm.human { color: var(--human) }
51
+ .nm.system {
52
+ color: var(--wezard);
53
+ background: linear-gradient(90deg, var(--wezard), var(--wezard-gold));
54
+ -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
55
+ }
49
56
 
50
57
  /* ── 侧栏: 我 + 会话 ─────────────────────────────────────────────── */
51
58
  .side {
@@ -278,6 +285,12 @@ span.av.go:hover { background: var(--av-hot); box-shadow: 0 0 0 3px var(--av-hot
278
285
  font-variant-numeric: tabular-nums;
279
286
  }
280
287
 
288
+ /* 他人未读: 贴在名字右侧, 同一种红点、小一号 —— 换到那个 role 的视角会看到、这里没在数的未读。 */
289
+ .nm .pub {
290
+ display: inline-block; margin-left: 4px; min-width: 13px; height: 13px; padding: 0 4px; border-radius: 7px;
291
+ background: #e5484d; color: #fff; font-size: 9px; font-weight: 600; line-height: 13px; text-align: center;
292
+ font-variant-numeric: tabular-nums; vertical-align: 2px;
293
+ }
281
294
 
282
295
  /* ── 右栏头: 这个群聊 / 私聊是什么 ───────────────────────────────── */
283
296
  .main { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; background: var(--card) }
@@ -324,6 +337,14 @@ span.av.go:hover { background: var(--av-hot); box-shadow: 0 0 0 3px var(--av-hot
324
337
  padding: 16px 20px 28px; -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
325
338
  }
326
339
  .thread-in { display: flex; flex-direction: column; gap: 14px }
340
+ /* 换窗口时旧内容留着等新内容: 不接点击, 稍候才变暗 (快的切换看不出闪), 页头底边一道细进度条。 */
341
+ .main.switching .thread-in, .main.switching .chathead > :not(.back) { pointer-events: none }
342
+ .main.switching .thread-in { opacity: .6; transition: opacity .2s .15s }
343
+ .main.switching .chathead {
344
+ background-image: linear-gradient(var(--accent), var(--accent)); background-repeat: no-repeat;
345
+ background-size: 40% 2px; animation: swbar 1s ease-in-out infinite;
346
+ }
347
+ @keyframes swbar { from { background-position: -67% 100% } to { background-position: 167% 100% } }
327
348
  .empty { color: var(--faint); text-align: center; padding: 60px 0; font-size: 13px }
328
349
  .more-btn {
329
350
  align-self: center; background: var(--card); border: 1px solid var(--line); border-radius: 16px;
@@ -345,6 +366,9 @@ span.av.go:hover { background: var(--av-hot); box-shadow: 0 0 0 3px var(--av-hot
345
366
  .mrow.mine .mcol { align-items: flex-end }
346
367
  .mwho { font-size: 11.5px; color: var(--mute); display: flex; gap: 6px; align-items: center; flex-wrap: wrap }
347
368
  .mwho .nm { min-width: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
369
+ /* 发言头 `🦊.a:` (WZ.said): 头像贴着名字、冒号贴着名字, 不吃 .mwho 的 gap。 */
370
+ .mwho .said { display: inline-flex; align-items: center; min-width: 0; max-width: 100% }
371
+ .mwho .said .av { margin-right: 2px }
348
372
  .mwho .ch { font-size: 10.5px; border-radius: 4px; padding: 0 4px }
349
373
  .mwho .ch.priv { color: var(--priv); background: #9a5b1014 }
350
374
  /* 时刻只此一处 —— 气泡外, 跟着名字走。等宽数字让一列时间对得齐。 */
@@ -376,16 +400,16 @@ button.mchip.rcpt:hover { text-decoration: underline }
376
400
  .mchip.rcpt:is(.st-timeout, .st-silent, .st-dead, .st-canceled) { color: #cf222e; background: #cf222e10 }
377
401
  /* 说给谁 (inline): 塞进正文第一段开头的头像 + 名字, 像行内 mention; 小圆角底色, 点它走移交行同一套跳转。 */
378
402
  .to-in {
379
- display: inline-flex; align-items: center; gap: 3px; vertical-align: baseline; padding: 1px 6px; border-radius: 5px;
403
+ display: inline-flex; align-items: center; gap: 0; vertical-align: baseline; padding: 1px 6px; border-radius: 5px;
380
404
  line-height: 18px; background: color-mix(in srgb, #0969da 10%, transparent); color: var(--ink);
381
405
  cursor: pointer; transition: background .12s;
382
406
  }
383
407
  .to-in:hover { background: color-mix(in srgb, #0969da 20%, transparent) }
384
- .to-in .av { width: 16px; height: 16px; border-radius: 50%; border: 1px solid var(--line); background: var(--card); font-size: 10px; line-height: 14px; text-align: center; align-self: center }
408
+ .to-in .av { margin-right: 2px; width: 16px; height: 16px; border-radius: 50%; border: 1px solid var(--line); background: var(--card); font-size: 10px; line-height: 14px; text-align: center; align-self: center }
385
409
  .to-in .nm { font-weight: 500; font-size: 13px }
386
410
  .to-in .nm::before { content: none }
387
411
  /* 移交行里的对方: 小头像 + 名字。整行是入口 (开双方的往来), 悬停时整行提亮。 */
388
- .handoff .ho-who { display: inline-flex; align-items: center; gap: 3px; color: var(--ink) }
412
+ .handoff .ho-who { display: inline-flex; align-items: center; gap: 2px; color: var(--ink) }
389
413
  .handoff .ho-line[data-hto]:hover { background: var(--av-hot); border-radius: 6px }
390
414
  .handoff .ho-who .av { width: 16px; height: 16px; border-radius: 50%; border: 1px solid var(--line); background: var(--card); font-size: 10px; line-height: 14px; text-align: center }
391
415
  .handoff .ho-who .nm { font-weight: 500 }
@@ -900,8 +924,8 @@ button.mchip.rcpt:hover { text-decoration: underline }
900
924
  .sk-it.on .k { opacity: 1; color: var(--accent) }
901
925
  .sk-it .ts { margin-left: auto; flex: none; font-size: 11px; color: var(--faint); font-variant-numeric: tabular-nums }
902
926
  .sk-it .pv { display: block; font-size: 12px; color: var(--mute); overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
903
- .sk-it .l1 .nm + .nm, .sk-it .arr + .nm { font-weight: 500; color: var(--mute) }
904
- .sk-it .arr { flex: none; color: var(--faint); font-size: 11.5px }
927
+ .sk-it .said { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
928
+ .sk-it .said .nm + .nm { font-weight: 500; color: var(--mute) }
905
929
  .sk-it .tag { flex: none; font-size: 10.5px; color: var(--mute); background: var(--paper); border-radius: 4px; padding: 1px 5px }
906
930
  /* 命中的那一截: 正文色, 最多两行; 命中词是这一页的视角色淡底 —— 与选中态一个色系。 */
907
931
  .sk-it .sn {
@@ -943,6 +967,9 @@ button.mchip.rcpt:hover { text-decoration: underline }
943
967
  .jtag { flex: none; margin-left: 6px; font-size: 10.5px; line-height: 15px; padding: 0 5px; border-radius: 4px; color: var(--faint); background: #f1f3f6; font-weight: 500 }
944
968
  .jtag.open { color: #94741f; background: #bf870014 }
945
969
  .jstuck { flex: none; margin-left: 4px; font-size: 10.5px; line-height: 15px; padding: 0 5px; border-radius: 4px; color: #8250df; background: #fbefff; font-weight: 500 }
970
+ .wxm { flex: none; margin-left: 4px; padding: 0 4px; border-radius: 4px; font-size: 10.5px; white-space: nowrap; color: #2f7d32; background: #2f7d3214 }
971
+ .wxm.paused { color: #94741f; background: #bf870014 }
972
+ .wxm.down { color: #c62828; background: #c6282814 }
946
973
  .jmk { flex: none; margin-left: 4px; padding: 0 3px; border-radius: 4px; font-size: 10.5px; opacity: .8; cursor: pointer }
947
974
  .jmk:hover { opacity: 1; background: #bf870022 }
948
975
  .jmk i { font-style: normal; margin-left: 1px; color: #94741f; font-size: 10px }