wezard 2.0.0 → 2.0.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.
Files changed (59) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CLAUDE.md +7 -8
  4. package/README.md +20 -32
  5. package/cli/wezard.sh +0 -62
  6. package/config.example.jsonc +3 -9
  7. package/dist/cli/init.js +12 -21
  8. package/dist/cli/init.js.map +1 -1
  9. package/dist/daemon/approval.js +2 -4
  10. package/dist/daemon/approval.js.map +1 -1
  11. package/dist/daemon/cc-session.js +136 -0
  12. package/dist/daemon/cc-session.js.map +1 -0
  13. package/dist/daemon/detail.js +2 -3
  14. package/dist/daemon/detail.js.map +1 -1
  15. package/dist/daemon/inbound.js +90 -110
  16. package/dist/daemon/inbound.js.map +1 -1
  17. package/dist/daemon/index.js +130 -111
  18. package/dist/daemon/index.js.map +1 -1
  19. package/dist/daemon/memory-steward.js +82 -0
  20. package/dist/daemon/memory-steward.js.map +1 -0
  21. package/dist/daemon/mirror-bridge.js +201 -62
  22. package/dist/daemon/mirror-bridge.js.map +1 -1
  23. package/dist/daemon/session-name.js +4 -27
  24. package/dist/daemon/session-name.js.map +1 -1
  25. package/dist/daemon/spawn-tmux.js +35 -13
  26. package/dist/daemon/spawn-tmux.js.map +1 -1
  27. package/dist/daemon/wizard-memory.js +21 -26
  28. package/dist/daemon/wizard-memory.js.map +1 -1
  29. package/dist/daemon/wizard.js +54 -11
  30. package/dist/daemon/wizard.js.map +1 -1
  31. package/dist/mcp/server.js +63 -153
  32. package/dist/mcp/server.js.map +1 -1
  33. package/dist/shared/chat-http.js +44 -16
  34. package/dist/shared/chat-http.js.map +1 -1
  35. package/dist/shared/chat-render.js +10 -8
  36. package/dist/shared/chat-render.js.map +1 -1
  37. package/dist/shared/cli-backends.js +2 -7
  38. package/dist/shared/cli-backends.js.map +1 -1
  39. package/dist/shared/config.js +10 -12
  40. package/dist/shared/config.js.map +1 -1
  41. package/dist/shared/detail-render.js +35 -16
  42. package/dist/shared/detail-render.js.map +1 -1
  43. package/dist/shared/detail-store.js +14 -3
  44. package/dist/shared/detail-store.js.map +1 -1
  45. package/dist/shared/role-render.js +4 -1
  46. package/dist/shared/role-render.js.map +1 -1
  47. package/dist/shared/role-view.js +49 -11
  48. package/dist/shared/role-view.js.map +1 -1
  49. package/dist/shared/world.js +11 -1
  50. package/dist/shared/world.js.map +1 -1
  51. package/hooks/pre-tool-use.sh +4 -4
  52. package/package.json +1 -2
  53. package/web/chat.css +103 -58
  54. package/web/chat.js +153 -82
  55. package/commands/wrc.md +0 -7
  56. package/dist/daemon/cc-bridge.js +0 -183
  57. package/dist/daemon/cc-bridge.js.map +0 -1
  58. package/dist/daemon/sessions.js +0 -4
  59. package/dist/daemon/sessions.js.map +0 -1
package/web/chat.js CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  // at / recvAt: 服务端快照时刻与本地收到时刻。所有"现在几点"的判断都换算到
24
24
  // 服务端时钟, 否则客户端时钟偏几分钟就会把运行中的会话判成已结束。
25
- var R = { at: 0, recvAt: 0, role: null, sessions: [], convs: [], relations: false, schedules: 0, stats: null };
25
+ var R = { at: 0, recvAt: 0, role: null, sessions: [], convs: [], relations: false, schedules: 0, stats: null, winStats: null };
26
26
  // frags: 当前窗口的原始片段 (id → 片段)。片段不带方向, 换视角时拿它就地重包左右。
27
27
  var S = { es: null, pinned: true, gen: 0, frags: {} };
28
28
  // 关系/日程两栏共用的世界快照。sel = 当前聚焦的节点, kinds = 边类型开关。
@@ -41,7 +41,6 @@
41
41
  var VIEW = 'msgs';
42
42
  var $ = function (s) { return document.querySelector(s); };
43
43
  var app = $('#app'), thread = $('#thread'), inner = $('#thread-in'), convsEl = $('#convs');
44
- var connEl = $('#conn');
45
44
  var wmapEl = $('#wmap'), wscrollEl = $('#wscroll'), wtoolsEl = $('#wtools'), planEl = $('#plan-in');
46
45
 
47
46
  var srvNow = function () { return R.at ? R.at + (Date.now() - R.recvAt) : Date.now(); };
@@ -115,7 +114,7 @@
115
114
  var names = {};
116
115
  var learn = function (id, name, label) { if (id && name) names[id] = { name: name, label: label || '' }; };
117
116
  var kindOf = function (id) {
118
- return /^task:/.test(id) ? 'task' : /^human:/.test(id) ? 'human' : 'wizard';
117
+ return /^task:/.test(id) ? 'task' : id === 'system:' ? 'system' : /^human:/.test(id) ? 'human' : 'wizard';
119
118
  };
120
119
  var nodeOf = function (target) {
121
120
  return W.nodes.filter(function (n) { return n.target === target; })[0];
@@ -126,14 +125,14 @@
126
125
  };
127
126
  var roleLabel = function (id) {
128
127
  var k = names[id] || {}, n = nodeOf(id);
129
- return k.label || (n && n.label) || (kindOf(id) === 'human' ? '👤' : kindOf(id) === 'task' ? '⏰' : '🧙');
128
+ return k.label || (n && n.label) || ({ human: '👤', task: '⏰', system: '🧙' })[kindOf(id)] || '🧙';
130
129
  };
131
130
  // `.name` 的点由 CSS 画 —— 人与定时任务没有地址, 不画点。
132
131
  var nm = function (id, name) {
133
132
  return '<span class="nm ' + kindOf(id) + '">' + esc(name || roleName(id)) + '</span>';
134
133
  };
135
134
  var nameOf = function (id) { return (kindOf(id) === 'wizard' ? '.' : '') + roleName(id); };
136
- var canSwitch = function (id) { return !!id && kindOf(id) !== 'task' && id !== 'human:'; };
135
+ var canSwitch = function (id) { return !!id && kindOf(id) !== 'task' && kindOf(id) !== 'system' && id !== 'human:'; };
137
136
 
138
137
  // ── markdown 渲染 (只对未渲染过的节点做, 复用节点不重绘) ──
139
138
  var md = null;
@@ -208,10 +207,35 @@
208
207
  var d = document.createElement('div'); d.innerHTML = html; return d;
209
208
  };
210
209
 
210
+ // ── 工具调用正文: 第二级数据, 展开那一刻才取 ──
211
+ // 片段里的 .tool-body 是个空壳 (data-lazy-turn / data-lazy-use), 首屏只带摘要行。
212
+ // toggle 不冒泡, 所以在线程根上用捕获听 —— 用户点开, 和 reconcile 换了新节点后
213
+ // restoreOpen 回填展开态, 走的是同一个事件: 后者正好把「展开着等结果」的那次调用
214
+ // 重取成带结果的正文。取到之后正文就留在节点上, 收起再展开不会重取。
215
+ var loadTool = function (d) {
216
+ var body = childBy(d, 'tool-body');
217
+ if (!body || !body.hasAttribute('data-lazy-turn') || body._busy || body.firstChild) return;
218
+ body._busy = true; body.removeAttribute('data-err');
219
+ api('api/tool', { turn: body.getAttribute('data-lazy-turn'), use: body.getAttribute('data-lazy-use') })
220
+ .then(function (r) {
221
+ if (!r.ok) throw new Error(r.error);
222
+ var stick = S.pinned;
223
+ body.innerHTML = r.html;
224
+ if (stick) toBottom(true);
225
+ })
226
+ // 失败留着空壳 (CSS 给出提示), 再展开一次就是重试。
227
+ .catch(function () { body.setAttribute('data-err', '1'); })
228
+ .then(function () { body._busy = false; });
229
+ };
230
+
211
231
  // ── 滚动: 默认到底; 用户手动上翻后解除吸附, 回到底部再吸附 ──
212
232
  var atBottom = function () { return thread.scrollHeight - thread.scrollTop - thread.clientHeight < 80; };
213
233
  var toBottom = function (force) { if (force || S.pinned) thread.scrollTop = thread.scrollHeight; };
214
234
  thread.addEventListener('scroll', function () { S.pinned = atBottom(); });
235
+ inner.addEventListener('toggle', function (e) {
236
+ var d = e.target;
237
+ if (d.open && d.classList && d.classList.contains('tool-call')) loadTool(d);
238
+ }, true);
215
239
 
216
240
  // ── 左栏: 当前 role 的名片 + 会话列表 ──
217
241
  var convOf = function (key) { return R.convs.filter(function (c) { return c.key === key; })[0]; };
@@ -224,45 +248,82 @@
224
248
  });
225
249
  return out;
226
250
  };
227
- var avatarOf = function (c) {
228
- if (c.kind !== 'group') return '<span class="av">' + esc(c.label) + '</span>';
251
+ // ── 状态: wizard 是否在执行中 ──
252
+ // 服务端只给事实 (busy / alive / runningUntil), 亮不亮在这里按本地时钟判 ——
253
+ // 到点没有新写入就自己熄 (同 expireRows), 不必等下一次推送。
254
+ var ST = { run: '执行中', idle: '空闲', off: '已关闭' };
255
+ var stateOf = function (s) { return wRunning(s) ? 'run' : s.alive ? 'idle' : 'off'; };
256
+ // 侧栏只在执行中时点一盏灯: 列表里安静是常态, 亮着的才值得看一眼。
257
+ var lampOf = function (s) {
258
+ return s && wRunning(s) ? '<i class="live" title="' + ST.run + '"></i>' : '';
259
+ };
260
+ var paintStatus = function () {
261
+ var el = $('#rb-st'), r = R.role;
262
+ if (!el || !r) return;
263
+ var k = stateOf(r);
264
+ el.className = 'st ' + k; el.textContent = ST[k];
265
+ };
266
+
267
+ var avatarOf = function (c, extra) {
268
+ if (c.kind !== 'group') return '<span class="av">' + esc(c.label) + (extra || '') + '</span>';
229
269
  var ms = membersOf(c).slice(0, 4);
230
270
  return '<span class="av mosaic n' + ms.length + '" aria-hidden="true">' +
231
271
  ms.map(function (m) { return '<i>' + esc(m.label || roleLabel(m.role)) + '</i>'; }).join('') + '</span>';
232
272
  };
233
273
 
274
+ // 选中的那一项若是个 role (私聊对端 / 群里的某人), 行内给一个换视角的开关。
275
+ // 不能是 <button>: 它嵌在整行那颗按钮里。
276
+ var SWAP = '<svg viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"><path d="M3 5.5h9M9.5 3l2.5 2.5L9.5 8M13 10.5H4M6.5 8 4 10.5 6.5 13" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>';
277
+ var swapOf = function (role) {
278
+ return canSwitch(role) && role !== ROLE
279
+ ? '<span class="sw" role="button" tabindex="0" data-sw="' + esc(role) + '" title="' + esc('切到 ' + nameOf(role) + ' 的视角') + '">' + SWAP + '</span>'
280
+ : '';
281
+ };
282
+ var line = function (title, sw, ts, pv) {
283
+ return '<span class="b"><span class="l1"><span class="t">' + title + '</span>' + sw +
284
+ '<span class="ts">' + esc(fmtAgo(ts)) + '</span></span>' +
285
+ '<span class="pv">' + esc(pv) + '</span></span>';
286
+ };
287
+
234
288
  var convItem = function (c) {
235
289
  var on = c.key === CONV;
236
290
  var title = c.kind === 'wizard' ? nm(c.peer, c.name) : '<span class="nm chat">' + esc(c.name) + '</span>';
237
- var subs = on && c.subs.length
238
- ? '<div class="subs">' + c.subs.map(function (s) {
239
- return '<button class="si' + (s.role === WITH ? ' on' : '') + (s.count ? '' : ' quiet') + '" data-conv="' + esc(c.key) + '" data-with="' + esc(s.role) + '" ' +
240
- 'title="' + esc(s.count ? '我与 ' + nameOf(s.role) + ' 在这里的 ' + s.count + ' 条往来' : '在这里, 但还没和我说过话') + '">' +
241
- '<span>' + esc(s.label) + '</span><span class="n">' + nm(s.role, s.name) + '</span>' +
242
- (s.count ? '<span class="c">' + s.count + '</span>' : '') + '</button>';
291
+ // 只列与我有往来的: 在群里但没和我说过话的人, 点进去也是空的。
292
+ var talked = on ? c.subs.filter(function (s) { return s.count; }) : [];
293
+ var subs = talked.length
294
+ ? '<div class="subs">' + talked.map(function (s) {
295
+ var sel = s.role === WITH;
296
+ return '<button class="si' + (sel ? ' on' : '') + '" data-conv="' + esc(c.key) + '" data-with="' + esc(s.role) + '" ' +
297
+ 'title="' + esc('我与 ' + nameOf(s.role) + ' 在这里的 ' + s.count + ' 条往来') + '">' +
298
+ '<span class="av">' + esc(s.label) + lampOf(s.status) + '</span>' +
299
+ line(nm(s.role, s.name), sel ? swapOf(s.role) : '', s.lastTs, s.preview) + '</button>';
243
300
  }).join('') + '</div>'
244
301
  : '';
245
- return '<button class="ci' + (on && !WITH ? ' on' : '') + '" data-conv="' + esc(c.key) + '">' +
246
- avatarOf(c) +
247
- '<span class="b"><span class="l1"><span class="t">' + title + '</span>' +
248
- '<span class="ts">' + esc(fmtAgo(c.lastTs)) + '</span></span>' +
249
- '<span class="pv">' + esc(c.preview) + '</span></span>' +
302
+ var sel = on && !WITH;
303
+ return '<button class="ci' + (sel ? ' on' : '') + '" data-conv="' + esc(c.key) + '">' +
304
+ avatarOf(c, lampOf(c.status)) + line(title, sel && c.kind === 'wizard' ? swapOf(c.peer) : '', c.lastTs, c.preview) +
250
305
  '</button>' + subs;
251
306
  };
252
307
 
253
308
  var renderConvs = function () {
254
309
  var groups = R.convs.filter(function (c) { return c.kind === 'group'; });
255
310
  var dms = R.convs.filter(function (c) { return c.kind !== 'group'; });
256
- var sec = function (title, list, none) {
257
- return '<h2>' + title + '<span>' + list.length + '</span></h2>' +
258
- (list.length ? list.map(convItem).join('') : '<div class="none">' + none + '</div>');
311
+ // 空着的那一栏不画 —— 对谁都一样: 人没有私聊只是这条规则的一个特例。
312
+ var sec = function (title, list) {
313
+ return list.length ? '<h2>' + title + '<span>' + list.length + '</span></h2>' + list.map(convItem).join('') : '';
259
314
  };
260
- // 人没有私聊 —— wizard↔wizard 才有, 空着的那一栏也不画。
261
- convsEl.innerHTML = sec('群聊', groups, '还没在任何群里说过话') +
262
- (R.role && R.role.kind === 'human' ? '' : sec('私聊', dms, '没有和别的 wizard 私聊过'));
315
+ // 没变就不碰 DOM: 心跳每 3s 来一次, 重建会把列表的滚动与焦点蹭掉。
316
+ var html = sec('群聊', groups) + sec('私聊', dms);
317
+ if (convsEl._html === html) return;
318
+ convsEl._html = html; convsEl.innerHTML = html;
263
319
  convsEl.querySelectorAll('[data-conv]').forEach(function (b) {
264
320
  b.onclick = function () { selectConv(b.getAttribute('data-conv'), b.getAttribute('data-with') || ''); };
265
321
  });
322
+ convsEl.querySelectorAll('[data-sw]').forEach(function (x) {
323
+ var go = function (e) { e.stopPropagation(); switchRole(x.getAttribute('data-sw')); };
324
+ x.onclick = go;
325
+ x.onkeydown = function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); go(e); } };
326
+ });
266
327
  };
267
328
 
268
329
  var shortCwd = function (p) {
@@ -321,16 +382,19 @@
321
382
  var renderRole = function () {
322
383
  var r = R.role;
323
384
  if (!r) return;
385
+ // 名字下面那一行就是出身 —— 有父亲的直接写成「谁的什么」(可点过去), 不再另起一条重复。
324
386
  // 分身 = 从父亲某个 session 节点 fork 出来的 (带着那一刻的上下文);
325
387
  // 子 wizard = 父亲 spawn 的白板, 只有出身、没有继承。
326
- var kind = r.kind === 'human' ? '人' : !r.parent ? 'wizard' : r.forkedFrom ? '分身' : '子 wizard';
327
- // 忙闲由右上角的状态点表达, 名片不再重复。
388
+ var kind = !r.parent
389
+ ? esc(r.kind === 'human' ? '人' : 'wizard')
390
+ : r.forkedFrom
391
+ ? '<span class="go" data-r="' + esc(r.parent.id) + '" title="从它的 session ' + esc(r.forkedFrom) + ' fork, 开局带着那一刻的上下文">⧉ .' + esc(r.parent.name) + ' 的分身 · @' + esc(r.forkedFrom.slice(0, 8)) + '</span>'
392
+ : '<span class="go" data-r="' + esc(r.parent.id) + '" title="它 spawn 的白板, 没有继承上下文">↳ .' + esc(r.parent.name) + ' 的子 wizard</span>';
328
393
  var facts = [];
329
394
  if (r.cwd) facts.push('<span title="' + esc(r.cwd) + '">📁 ' + esc(shortCwd(r.cwd)) + '</span>');
330
- if (r.bornAt) facts.push('<span title="创建时间">🐣 ' + esc(fmtDay(r.bornAt)) + '</span>');
331
- if (r.parent) facts.push(r.forkedFrom
332
- ? '<span class="go" data-r="' + esc(r.parent.id) + '" title="从它的 session ' + esc(r.forkedFrom) + ' fork, 开局带着那一刻的上下文">⧉ .' + esc(r.parent.name) + ' 的分身 · @' + esc(r.forkedFrom.slice(0, 8)) + '</span>'
333
- : '<span class="go" data-r="' + esc(r.parent.id) + '" title="它 spawn 的白板, 没有继承上下文">↳ .' + esc(r.parent.name) + ' 的子 wizard</span>');
395
+ // 生在哪个群、什么时候 —— 一件事, 一行。
396
+ var born = [r.chat, r.bornAt ? fmtAgo(r.bornAt) : ''].filter(Boolean);
397
+ if (born.length) facts.push('<span title="' + esc('创建于' + (r.chat ? ' ' + r.chat : '') + (r.bornAt ? ' · ' + fmtDay(r.bornAt) : '')) + '">🐣 ' + esc(born.join(' · ')) + '</span>');
334
398
  var kin = function (xs, what) {
335
399
  return xs.length ? '<span title="' + esc(xs.map(function (x) { return '.' + x.name; }).join(' ')) + '">' + xs.length + ' 个' + what + '</span>' : '';
336
400
  };
@@ -338,80 +402,86 @@
338
402
  $('#rb-who').innerHTML =
339
403
  '<div class="id"><span class="av">' + esc(r.label) + '</span>' +
340
404
  '<span class="l">' + nm(r.id, r.name) +
341
- '<span class="k">' + esc(kind + (r.chat ? ' · 住在 ' + r.chat : '')) + '</span></span></div>' +
405
+ '<span class="k">' + kind + (r.kind === 'wizard' ? '<span class="st" id="rb-st"></span>' : '') + '</span></span></div>' +
342
406
  (r.description ? '<p class="job">' + esc(r.description) + '</p>' : '') +
343
407
  '<div class="facts">' + facts.join('') + '</div>';
408
+ paintStatus();
344
409
  $('#rb-who').querySelectorAll('.go').forEach(function (g) {
345
410
  g.onclick = function () { switchRole(g.getAttribute('data-r')); };
346
411
  });
347
412
  $('#rb-sess').innerHTML = R.sessions.length > 1 ? sessPicker() : '';
413
+ $('#rb-foot').hidden = R.sessions.length < 2;
348
414
  bindSessPicker();
349
- syncFoot();
350
415
  var acts = [];
351
- if (R.relations) acts.push('<button class="vb' + (VIEW === 'world' ? ' on' : '') + '" data-view="world">关系图</button>');
352
- if (r.kind === 'wizard') acts.push('<button class="vb' + (VIEW === 'plan' ? ' on' : '') + '" data-view="plan">日程' + (R.schedules ? '<b>' + R.schedules + '</b>' : '') + '</button>');
416
+ // 入口只在有东西可看时出现, 且只是一枚徽标 —— 名片的主角是身份, 不是按钮。
417
+ if (R.relations) acts.push('<button class="bd' + (VIEW === 'world' ? ' on' : '') + '" data-view="world">关系图</button>');
418
+ if (R.schedules) acts.push('<button class="bd' + (VIEW === 'plan' ? ' on' : '') + '" data-view="plan">日程<b>' + R.schedules + '</b></button>');
353
419
  $('#rb-acts').innerHTML = acts.join('');
354
- $('#rb-acts').querySelectorAll('.vb').forEach(function (b) {
420
+ $('#rb-acts').querySelectorAll('.bd').forEach(function (b) {
355
421
  b.onclick = function () { setView(VIEW === b.getAttribute('data-view') ? 'msgs' : b.getAttribute('data-view')); };
356
422
  });
357
423
  // 标题就是这一页的主张: 你此刻站在谁的位置上。换视角 → 标题跟着换。
358
424
  document.title = nameOf(r.id) + ' 的视角';
359
425
  };
360
426
 
361
- // 左栏底: session 切换 + 用量总账, 都是「轮次」的事, 与上面的身份分开; 两样都空就整条收起。
362
- var syncFoot = function () {
363
- $('#rb-foot').hidden = !$('#rb-sess').innerHTML && !$('#rb-prof').innerHTML;
364
- };
365
-
366
- // ── profile: 当前 role 自己跑过的轮次的总账 (选了 session 就只算那一段) ──
367
- var COLORS = { input: '#0a7d6b', cacheRead: '#8250df', cacheWrite: '#953800', output: '#1a7f37' };
427
+ // ── 用量条: 一组轮次的总账 ──
428
+ // usageHTML 只管「一本账长什么样」(TagSummary → 一条), 账是谁的、挂在哪由 renderUsage 定。
429
+ var SEGS = [
430
+ ['input', '输入', '#0a7d6b'], ['cacheRead', '缓存读', '#8250df'],
431
+ ['cacheWrite', '缓存写', '#953800'], ['output', '输出', '#1a7f37'],
432
+ ];
368
433
  var TIP = {
369
434
  turns: '对话轮数', tools: '工具调用次数', api: 'API 请求次数',
370
- ctx: '上下文峰值 — 单次请求送入的 input + 缓存 的最高值',
371
- out: '累计输出 token', cache: '累计缓存 token (读 + 写)', time: '累计耗时',
435
+ ctx: '上下文峰值 — 单次请求送入的 input + 缓存 的最高值', time: '累计耗时',
372
436
  };
373
437
  var liveDur = function (t) {
374
438
  var d = (t.usage && t.usage.durationMs) || 0;
375
439
  if (!t.runningUntil) return d;
376
440
  return d + Math.max(0, Math.min(srvNow(), t.runningUntil) - R.at);
377
441
  };
378
- var renderProfile = function () {
379
- var el = $('#rb-prof');
380
- var t = R.stats;
381
- if (!t) { el.innerHTML = ''; syncFoot(); return; }
442
+ var usageHTML = function (t) {
382
443
  var u = t.usage || {};
383
- var segs = [['input', '输入'], ['cacheRead', '缓存读'], ['cacheWrite', '缓存写'], ['output', '输出']]
384
- .filter(function (s) { return u[s[0]] > 0; });
444
+ var segs = SEGS.filter(function (s) { return u[s[0]] > 0; });
385
445
  var total = segs.reduce(function (a, s) { return a + u[s[0]]; }, 0);
446
+ // 值为 0 = 该指标没有数据 (老记录 / 网关不报 usage), 不占位置。
447
+ var kv = function (k, n, text) {
448
+ return n ? '<span class="kv" title="' + esc(TIP[k] || k) + '"><b>' + esc(text) + '</b>' + k + '</span>' : '';
449
+ };
450
+ // 输出 / 缓存不再单列成指标 —— 它们就是 I/O 图例里的那几格。
386
451
  var io = total > 0
387
- ? '<div class="io" title="累计 token I/O · 共 ' + fmtTok(total) + '">' +
452
+ ? '<span class="u-io" title="累计 token I/O · 共 ' + fmtTok(total) + '">' +
388
453
  '<span class="bar">' + segs.map(function (s) {
389
454
  return '<span class="seg" style="width:' + (u[s[0]] / total * 100).toFixed(2) + '%;background:' +
390
- COLORS[s[0]] + '" title="' + s[1] + ': ' + fmtTok(u[s[0]]) + '"></span>';
455
+ s[2] + '" title="' + s[1] + ': ' + fmtTok(u[s[0]]) + '"></span>';
391
456
  }).join('') + '</span>' +
392
457
  '<span class="leg">' + segs.map(function (s) {
393
- return '<span class="lg"><i style="background:' + COLORS[s[0]] + '"></i>' + s[1] +
458
+ return '<span class="lg"><i style="background:' + s[2] + '"></i>' + s[1] +
394
459
  '<b>' + fmtTok(u[s[0]]) + '</b></span>';
395
460
  }).join('') + '</span>' +
396
- '</div>'
461
+ '</span>'
397
462
  : '';
398
- // 值为 0 = 该指标没有数据 (老记录 / 网关不报 usage), 不占格子。
399
- var st = function (k, n, text) {
400
- return n ? '<span class="kv" title="' + esc(TIP[k] || k) + '">' +
401
- '<span class="v">' + esc(text) + '</span><span class="k">' + k + '</span></span>' : '';
402
- };
403
- var cache = (u.cacheRead || 0) + (u.cacheWrite || 0);
404
- // 忙闲已在名片的状态里, 这里只留模型。
405
- el.innerHTML =
406
- '<div class="hd"><span class="lb">用量</span>' +
407
- (t.model ? '<span class="model" title="' + esc(t.model) + '">' + esc(t.model.replace(/^claude-/, '')) + '</span>' : '') +
408
- '</div>' +
409
- '<div class="grid">' +
410
- st('turns', t.turns, t.turns) + st('tools', u.tools, u.tools) + st('api', u.calls, u.calls) +
411
- st('ctx', u.ctxPeak, fmtTok(u.ctxPeak)) + st('out', u.output, fmtTok(u.output)) +
412
- st('cache', cache, fmtTok(cache)) + st('time', liveDur(t), fmtDur(liveDur(t))) +
413
- '</div>' + io;
414
- syncFoot();
463
+ return '<span class="u-lb">用量</span>' +
464
+ '<span class="u-kvs">' +
465
+ kv('turns', t.turns, t.turns) + kv('tools', u.tools, u.tools) + kv('api', u.calls, u.calls) +
466
+ kv('ctx', u.ctxPeak, fmtTok(u.ctxPeak)) + kv('time', liveDur(t), fmtDur(liveDur(t))) +
467
+ '</span>' + io +
468
+ (t.model ? '<span class="u-model" title="' + esc(t.model) + '">' + esc(t.model.replace(/^claude-/, '')) + '</span>' : '');
469
+ };
470
+ // 没变就不碰 DOM: 心跳每 3s 来一次, 重建会把悬停中的 tooltip 蹭掉。
471
+ var putUsage = function (el, t) {
472
+ var html = t ? usageHTML(t) : '';
473
+ // !el: web/ 是热更的, 外壳 HTML 要等 daemon 重启 —— 新脚本可能先遇上没有挂载点的旧外壳。
474
+ if (!el || el._html === html) return;
475
+ el._html = html; el.innerHTML = html; el.hidden = !html;
476
+ // 条的高度挤的是消息区 —— 原本贴底的继续贴底。
477
+ toBottom();
478
+ };
479
+ // wizard: 整页页脚, 它自己跑过的全部轮次 (选了 session 就只算那一段)。
480
+ // 人: 自己不跑轮次 —— 在群里点开某个 wizard 之后, 窗口底下是这段往来的账。
481
+ var renderUsage = function () {
482
+ var wiz = !!R.role && R.role.kind === 'wizard';
483
+ putUsage($('#pg-usage'), wiz ? R.stats : null);
484
+ putUsage($('#ch-usage'), !wiz && WITH && VIEW === 'msgs' ? R.winStats : null);
415
485
  };
416
486
 
417
487
  // ── 右栏头: 这个群聊 / 私聊是什么 (关系 / 日程视图时是视图名) ──
@@ -471,8 +541,7 @@
471
541
  var av = mine ? '' : '<button class="av' + (canSwitch(m.from) ? ' go' : '') + '" data-r="' + esc(m.from) + '" title="' + esc(nameOf(m.from)) + '"' + (canSwitch(m.from) ? '' : ' disabled') + '>' + esc(m.fromLabel || roleLabel(m.from)) + '</button>';
472
542
  return '<div class="mrow ' + (mine ? 'mine' : 'them') + '" data-id="' + esc(m.id) + '" data-turn="' + esc(m.turnId || m.id) + '" data-ts="' + m.ts + '"' +
473
543
  (m.ping ? ' data-ping="1"' : '') + ' data-sig="' + esc(m.sig) + '" data-stale-at="' + (m.staleAt || 0) + '">' +
474
- av +
475
- '<div class="mcol"><div class="mwho">' + who + '</div><div class="mb">' + m.html + '</div></div>' +
544
+ '<div class="mcol"><div class="mwho">' + av + who + '</div><div class="mb">' + m.html + '</div></div>' +
476
545
  flip +
477
546
  '</div>';
478
547
  };
@@ -605,7 +674,8 @@
605
674
  var applyRole = function (d) {
606
675
  R.at = d.at || Date.now(); R.recvAt = Date.now();
607
676
  R.role = d.role; R.sessions = d.sessions || []; R.convs = d.convs || [];
608
- R.relations = !!d.relations; R.schedules = d.schedules || 0; R.stats = d.stats || null;
677
+ R.relations = !!d.relations; R.schedules = d.schedules || 0;
678
+ R.stats = d.stats || null; R.winStats = d.winStats || null;
609
679
  ROLE = d.role.id;
610
680
  learn(d.role.id, d.role.name, d.role.label);
611
681
  R.convs.forEach(function (c) {
@@ -613,7 +683,7 @@
613
683
  c.subs.forEach(function (s) { learn(s.role, s.name, s.label); });
614
684
  });
615
685
  if (!CONV || !convOf(CONV)) { CONV = d.conv || ''; WITH = ''; }
616
- renderRole(); renderProfile(); renderConvs(); renderHead();
686
+ renderRole(); renderUsage(); renderConvs(); renderHead();
617
687
  };
618
688
 
619
689
  // ── SSE: role 摘要 + 当前窗口的消息增量。断线指数退避重连。 ──
@@ -625,9 +695,8 @@
625
695
  S.es = es;
626
696
  es.addEventListener('role', function (e) { try { applyRole(JSON.parse(e.data)); } catch (err) { } });
627
697
  es.addEventListener('msg', function (e) { try { upsertMsg(JSON.parse(e.data)); } catch (err) { } });
628
- es.onopen = function () { backoff = 1000; connEl.className = 'conn'; connEl.title = '实时连接'; };
698
+ es.onopen = function () { backoff = 1000; };
629
699
  es.onerror = function () {
630
- connEl.className = 'conn off'; connEl.title = '连接断开, 重连中';
631
700
  es.close(); if (S.es === es) S.es = null;
632
701
  setTimeout(function () { if (!S.es) connect(); }, backoff);
633
702
  backoff = Math.min(backoff * 2, 15000);
@@ -655,7 +724,9 @@
655
724
  CONV = key; WITH = withRole || '';
656
725
  if (VIEW !== 'msgs') setView('msgs');
657
726
  app.classList.add('reading');
658
- renderConvs(); renderHead();
727
+ // 上一个窗口的账不属于这个窗口 —— 先收起, 新的随 SSE 的首个 role 事件到。
728
+ R.winStats = null;
729
+ renderConvs(); renderHead(); renderUsage();
659
730
  S.gen++;
660
731
  syncUrl();
661
732
  inner.innerHTML = '<div class="empty">加载中…</div>';
@@ -1444,7 +1515,7 @@
1444
1515
  app.classList.toggle('outer', v !== 'msgs');
1445
1516
  // 手机上关系/日程占满主区 —— 进入即阅读态。
1446
1517
  if (v !== 'msgs') app.classList.add('reading');
1447
- renderRole(); renderHead();
1518
+ renderRole(); renderHead(); renderUsage();
1448
1519
  if (v === 'msgs') { fxStop(); toBottom(true); }
1449
1520
  else {
1450
1521
  // 关系图一打开就聚焦在当前 role 身上: 它的邻居亮着, 其余压暗。
@@ -1465,13 +1536,13 @@
1465
1536
  // 本地心跳: 相对时间、运行中判定、耗时都随时间变化, 但服务端没有新事件可推。
1466
1537
  setInterval(function () {
1467
1538
  if (!R.role) return;
1468
- renderProfile();
1539
+ renderUsage();
1469
1540
  expireRows();
1541
+ // 侧栏的「几分钟前」与状态灯: 内容没变时 renderConvs 不碰 DOM。
1542
+ paintStatus(); renderConvs();
1470
1543
  if (VIEW === 'world') tickWorld();
1471
1544
  else if (VIEW === 'plan') renderPlan();
1472
1545
  }, TICK_MS);
1473
- // 侧栏的「几分钟前」一分钟刷一次就够 —— 每 3s 重建会把列表的滚动与焦点蹭掉。
1474
- setInterval(function () { if (R.role && VIEW === 'msgs') renderConvs(); }, 60000);
1475
1546
 
1476
1547
  // ── boot ──
1477
1548
  // 带着 role / conv 来的链接直接进阅读态 (手机上不先落在会话列表)。
package/commands/wrc.md DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- description: WeCom remote control — mirror current agent session to WeCom
3
- allowed-tools: Bash(${CLAUDE_PLUGIN_ROOT}/cli/wezard.sh:*)
4
- argument-hint: [user:<userid>|chat:<chatid>]
5
- ---
6
-
7
- !`${CLAUDE_PLUGIN_ROOT}/cli/wezard.sh mirror $ARGUMENTS`
@@ -1,183 +0,0 @@
1
- // Headless Claude Code bridge. Spawns `claude -p` per WeCom message, streams
2
- // stdout (stream-json) back via WeCom replyStream. One in-flight subprocess
3
- // per principal; subsequent messages queue.
4
- import { spawn } from "node:child_process";
5
- import { createInterface } from "node:readline";
6
- import { expandHome } from "../shared/paths.js";
7
- import { augmentedPath } from "../shared/exec-path.js";
8
- import { resolveCliBackend } from "../shared/cli-backends.js";
9
- const FLUSH_INTERVAL_MS = 250;
10
- const deltaText = (line) => {
11
- if (line.type === "stream_event" &&
12
- line.event?.type === "content_block_delta" &&
13
- line.event.delta?.type === "text_delta") {
14
- return line.event.delta.text ?? "";
15
- }
16
- return undefined;
17
- };
18
- const assistantText = (line) => {
19
- if (line.type === "assistant" && line.message?.content) {
20
- return line.message.content
21
- .filter((b) => b.type === "text" && typeof b.text === "string")
22
- .map((b) => b.text)
23
- .join("");
24
- }
25
- return undefined;
26
- };
27
- const runOne = async (args) => {
28
- const { text, resumeSid, cfg, log, client, frame, streamId, principal, onSession } = args;
29
- const cliArgs = [
30
- "-p",
31
- text,
32
- "--output-format",
33
- "stream-json",
34
- ...resolveCliBackend(cfg.wrc).headlessStreamingArgs,
35
- ...cfg.wrc.extraArgs,
36
- ];
37
- if (resumeSid)
38
- cliArgs.push("--resume", resumeSid);
39
- log.info({ resumeSid, claudeBin: cfg.wrc.claudeBin }, "spawn claude");
40
- const proc = spawn(cfg.wrc.claudeBin, cliArgs, {
41
- cwd: expandHome(cfg.wrc.cwd),
42
- env: { ...process.env, PATH: augmentedPath(process.env.PATH) },
43
- stdio: ["ignore", "pipe", "pipe"],
44
- });
45
- let acc = "";
46
- let lastSent = "";
47
- let sawDelta = false; // gate assistant-event fallback (see deltaText/assistantText comment)
48
- let flushTimer;
49
- let stderrTail = "";
50
- const sendFlush = async (finish) => {
51
- if (acc === lastSent && !finish)
52
- return;
53
- lastSent = acc;
54
- try {
55
- await client.replyStream(frame, streamId, acc || " ", finish);
56
- }
57
- catch (e) {
58
- log.warn({ err: e.message, finish }, "replyStream failed");
59
- }
60
- };
61
- const scheduleFlush = () => {
62
- if (flushTimer)
63
- return;
64
- flushTimer = setTimeout(() => {
65
- flushTimer = undefined;
66
- void sendFlush(false);
67
- }, FLUSH_INTERVAL_MS);
68
- };
69
- const stdout = proc.stdout;
70
- const stderr = proc.stderr;
71
- if (!stdout || !stderr) {
72
- throw new Error("claude subprocess missing stdio pipes");
73
- }
74
- // ENOENT / EACCES 等 spawn 失败走 'error' 事件,不监听会 crash 整个 daemon。
75
- let spawnError;
76
- proc.on("error", (err) => {
77
- spawnError = err;
78
- log.error({ err: err.message, code: err.code }, "spawn error");
79
- });
80
- const rl = createInterface({ input: stdout });
81
- rl.on("line", (raw) => {
82
- if (!raw.trim())
83
- return;
84
- let line;
85
- try {
86
- line = JSON.parse(raw);
87
- }
88
- catch {
89
- log.debug({ raw: raw.slice(0, 200) }, "non-JSON stdout line");
90
- return;
91
- }
92
- if (line.type === "system" && line.subtype === "init" && line.session_id) {
93
- onSession(line.session_id);
94
- return;
95
- }
96
- const delta = deltaText(line);
97
- if (delta) {
98
- sawDelta = true;
99
- acc += delta;
100
- scheduleFlush();
101
- }
102
- else {
103
- const whole = assistantText(line);
104
- // Fallback only if we never saw any delta (e.g. cached / non-streaming response).
105
- if (whole && !sawDelta) {
106
- acc += whole;
107
- scheduleFlush();
108
- }
109
- }
110
- if (line.type === "result") {
111
- // result.result is the final assistant text in cases where no assistant event was emitted (e.g. no_response).
112
- if (!acc && typeof line.result === "string")
113
- acc = line.result;
114
- }
115
- });
116
- proc.stderr?.on("data", (chunk) => {
117
- const s = chunk.toString("utf8");
118
- stderrTail = (stderrTail + s).slice(-2000);
119
- });
120
- await new Promise((resolve) => {
121
- proc.on("close", async (code) => {
122
- if (flushTimer) {
123
- clearTimeout(flushTimer);
124
- flushTimer = undefined;
125
- }
126
- if (spawnError || code !== 0) {
127
- const reason = spawnError
128
- ? `spawn ${cfg.wrc.claudeBin}: ${spawnError.message}`
129
- : `claude exited ${code}`;
130
- log.error({ code, spawnErr: spawnError?.message, stderrTail: stderrTail.slice(-500) }, "claude failure");
131
- const fallback = acc
132
- ? `${acc}\n\n[${reason}]`
133
- : `[wezard] ${reason}\n${stderrTail.slice(-500)}`;
134
- acc = fallback;
135
- }
136
- await sendFlush(true);
137
- // WeCom 列表对 stream 消息硬编码 "…" 预览,唯一覆盖方式是在 stream 终止后补发一条普通消息。
138
- // 取 acc 按空行切分的最后一段——对应"最后一段话",多段冗长输出不会全压进列表。
139
- const tail = acc.split(/\n{2,}/).filter((s) => s.trim()).pop();
140
- if (tail) {
141
- const chatId = principal.includes(":") ? principal.slice(principal.indexOf(":") + 1) : principal;
142
- try {
143
- await client.sendMessage(chatId, { msgtype: "markdown", markdown: { content: tail } });
144
- }
145
- catch (e) {
146
- log.warn({ err: e.message }, "preview push failed");
147
- }
148
- }
149
- log.info({ code, chars: acc.length }, "claude done");
150
- resolve();
151
- });
152
- });
153
- };
154
- const queues = new Map();
155
- const enqueue = (key, job) => {
156
- const prev = queues.get(key) ?? Promise.resolve();
157
- const next = prev.then(job, job).catch(() => undefined);
158
- queues.set(key, next.finally(() => {
159
- if (queues.get(key) === next)
160
- queues.delete(key);
161
- }));
162
- return next;
163
- };
164
- export const makeBridge = (deps) => {
165
- return {
166
- dispatch: ({ principal, text, images, frame, streamId }) => enqueue(principal, () => {
167
- const refs = (images ?? []).map((p) => `@${p}`).join("\n");
168
- const finalText = refs ? (text ? `${refs}\n${text}` : refs) : text;
169
- return runOne({
170
- text: finalText,
171
- resumeSid: deps.sessions.get(principal),
172
- cfg: deps.cfg,
173
- log: deps.log.child({ principal }),
174
- client: deps.client,
175
- frame,
176
- streamId,
177
- principal,
178
- onSession: (sid) => deps.sessions.set(principal, sid),
179
- });
180
- }),
181
- };
182
- };
183
- //# sourceMappingURL=cc-bridge.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cc-bridge.js","sourceRoot":"","sources":["../../daemon/cc-bridge.ts"],"names":[],"mappings":"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"}
@@ -1,4 +0,0 @@
1
- // Persisted principal → claude session_id map. Single JSON file, write-through.
2
- import { loadJsonMap } from "../shared/json-map-store.js";
3
- export const loadSessionStore = (filePath) => loadJsonMap(filePath);
4
- //# sourceMappingURL=sessions.js.map