@lyra_wang/dsh-terminal-context 0.3.0 → 0.4.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.
package/client.js CHANGED
@@ -1,56 +1,76 @@
1
1
  /**
2
- * 界面侧 · 读终端画面(v0.3.0)
2
+ * 界面侧 · 读侧边栏(v0.4.0)
3
3
  *
4
- * 走的是"看屏幕"这条路,不是接口:终端是 xterm 渲染的 DOM,和对话在同一个页面里,
5
- * 所以直接读渲染出来的行即可 —— 和"豆包看着 Word 的界面操作 Word"同一个思路。
4
+ * 原来只读终端的 .xterm-rows。但侧边栏不止终端 —— 还可能挂着浏览器面板,
5
+ * 两者都是"你眼前的东西",都该进 Agent 的上下文。
6
6
  *
7
- * 为什么不用 terminalController:客户端 `ctx` 上只有 `fiber`,服务名找不到;
8
- * 而 DOM 这条路不依赖任何内部 API。
7
+ * 做法:先按已知结构读终端;再扫一遍可能的侧边栏容器,把它们的结构一起推回去。
8
+ * 为什么要推结构:不同面板的 DOM 差别很大,与其猜选择器,不如让页面自己报告
9
+ * "有哪些容器、各有多少字",下一版照着写就一次到位。
9
10
  *
10
- * 三条硬约束(上次事故的教训):
11
- * 1. inject: [] —— 没有依赖,就不可能 pending,不可能拖垮启动
12
- * 2. 全部 try/catch —— 一个异常不该让界面白屏
13
- * 3. 只读 —— 不改终端、不发输入
11
+ * 三条硬约束(v0.1 事故的教训):
12
+ * inject: [] 不声明依赖 → 不可能 pending → 不会拖垮启动
13
+ * 全部 try/catch 读失败只是"没数据",不白屏
14
+ * 只读 不点击、不输入
14
15
  */
15
16
  window.__ModuleLoader__.load({
16
17
  id: '@lyra_wang/dsh-terminal-context',
17
18
  factory() {
18
- const MAX_LINES = 60
19
- const readScreen = () => {
20
- // xterm 把可见区渲染成 .xterm-rows 下的若干行
19
+ const MAX = 80
20
+ const clean = (t) => (t || '').replace(/\u00a0/g, ' ').replace(/[ \t]+$/gm, '')
21
+
22
+ const readTerminal = () => {
21
23
  const rows = document.querySelectorAll('.xterm-rows > div')
22
- if (!rows.length) {
23
- const screen = document.querySelector('.xterm-screen')
24
- if (!screen) return null
25
- const text = (screen.innerText || '').split('\n')
26
- return text.slice(-MAX_LINES)
27
- }
28
- const lines = []
29
- for (const row of rows) {
30
- const text = (row.textContent || '').replace(/\u00a0/g, ' ').replace(/\s+$/, '')
31
- lines.push(text)
24
+ if (!rows.length) return null
25
+ const out = []
26
+ for (const r of rows) out.push(clean(r.textContent))
27
+ while (out.length && !out[0].trim()) out.shift()
28
+ while (out.length && !out[out.length - 1].trim()) out.pop()
29
+ return out.slice(-MAX)
30
+ }
31
+
32
+ // 扫可能的侧边栏容器。命中后连结构和样本一起带回去,供下一版锁定选择器。
33
+ const scanPanels = () => {
34
+ const hits = []
35
+ const seen = new Set()
36
+ const nodes = document.querySelectorAll('[class*="sidebar"],[class*="rightbar"],[class*="panel"],[class*="terminal"],[class*="browser"],[id*="sidebar"],[id*="panel"]')
37
+ for (const el of nodes) {
38
+ if (seen.has(el) || el === document.body) continue
39
+ seen.add(el)
40
+ const text = clean(el.innerText || '')
41
+ if (text.length < 40) continue
42
+ hits.push({
43
+ tag: el.tagName.toLowerCase(),
44
+ cls: String(el.className || '').slice(0, 80),
45
+ chars: text.length,
46
+ sample: text.slice(0, 120).replace(/\n+/g, ' ⏎ '),
47
+ })
48
+ if (hits.length >= 8) break
32
49
  }
33
- while (lines.length && !lines[0].trim()) lines.shift()
34
- while (lines.length && !lines[lines.length - 1].trim()) lines.pop()
35
- return lines.slice(-MAX_LINES)
50
+ return hits
36
51
  }
52
+
37
53
  return {
38
54
  inject: [],
39
55
  apply(ctx) {
40
- let last = ''
56
+ let lastKey = ''
41
57
  const push = () => {
42
58
  try {
43
- const lines = readScreen()
44
- if (lines === null) return
45
- const joined = lines.join('\n')
46
- if (joined === last) return
47
- last = joined
59
+ const lines = readTerminal()
60
+ const panels = scanPanels()
61
+ const key = JSON.stringify([lines && lines.slice(-5), panels.map((p) => p.cls + p.chars)])
62
+ if (key === lastKey) return
63
+ lastKey = key
48
64
  fetch('/plugins/terminal-context/push', {
49
65
  method: 'POST',
50
66
  headers: { 'content-type': 'application/json' },
51
- body: JSON.stringify({ lines, truncated: lines.length >= MAX_LINES, diag: null }),
67
+ body: JSON.stringify({
68
+ lines: lines || [],
69
+ truncated: !!(lines && lines.length >= MAX),
70
+ diag: panels.length ? { panels } : null,
71
+ }),
52
72
  }).catch(() => {})
53
- } catch { /* 静默:读屏失败不能影响界面 */ }
73
+ } catch { /* 静默 */ }
54
74
  }
55
75
  try {
56
76
  ctx.effect(() => {
package/locale/en.json CHANGED
@@ -1 +1,6 @@
1
- { "meta": { "title": "DSH Terminal Context", "description": "Feeds the DSH sidebar terminal's recent output to the agent so its next step matches what is actually on screen." } }
1
+ {
2
+ "meta": {
3
+ "title": "DSH Sidebar Context",
4
+ "description": "Feeds the DSH sidebar's current content (terminal / browser panel) to the agent so its next step matches what is on screen."
5
+ }
6
+ }
package/locale/zh.json CHANGED
@@ -1 +1,6 @@
1
- { "meta": { "title": "DSH 终端上下文", "description": "把 DSH 侧边栏终端最近若干行交给 Agent,让它给的下一步依据你当前的真实处境。" } }
1
+ {
2
+ "meta": {
3
+ "title": "DSH 侧边栏上下文",
4
+ "description": "把 DSH 侧边栏当前显示的内容(终端 / 浏览器面板)交给 Agent,让它依据你眼前真实的东西给下一步。"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@lyra_wang/dsh-terminal-context",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
- "description": "把侧边栏终端最近若干行交给 Agent,作为它判断当前处境的上下文。",
5
+ "description": "把 DSH 侧边栏当前显示的内容(终端 / 浏览器面板)交给 Agent,作为它判断当前处境的上下文。",
6
6
  "exports": {
7
7
  ".": "./index.js",
8
8
  "./locale/*.json": "./locale/*.json",