agentworkshop 0.7.38 → 0.7.39

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 (44) hide show
  1. package/.AgentWorkShop/prompts/aml-mission-default.md +20 -20
  2. package/.AgentWorkShop/prompts/aml-playbook.md +49 -49
  3. package/.AgentWorkShop/prompts/host-tools.json +1238 -1238
  4. package/.AgentWorkShop/prompts/peer-reply-cross-channel.md +10 -10
  5. package/.output/.AgentWorkShop/prompts/aml-mission-default.md +20 -20
  6. package/.output/.AgentWorkShop/prompts/aml-playbook.md +49 -49
  7. package/.output/.AgentWorkShop/prompts/host-tools.json +1238 -1238
  8. package/.output/.AgentWorkShop/prompts/peer-reply-cross-channel.md +10 -10
  9. package/.output/nitro.json +1 -1
  10. package/.output/public/_nuxt/{BG8sCcwx.js → CUDwzgLV.js} +4 -4
  11. package/.output/public/_nuxt/CmsOtOAA2.js +1 -0
  12. package/.output/public/_nuxt/builds/latest.json +1 -1
  13. package/.output/public/_nuxt/builds/meta/4457d63f-0866-4cea-b320-04a321f24795.json +1 -0
  14. package/.output/public/_nuxt/default.BXI_fZUi.css +1 -0
  15. package/.output/server/chunks/_/nitro.mjs +706 -706
  16. package/.output/server/chunks/build/{default-hseyOqBT.mjs → default-CZmZFpqH.mjs} +27 -42
  17. package/.output/server/chunks/build/{default-hseyOqBT.mjs.map → default-CZmZFpqH.mjs.map} +1 -1
  18. package/.output/server/chunks/routes/renderer.mjs +1 -1
  19. package/.output/server/chunks/virtual/entry.mjs +3 -1
  20. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  21. package/.output/server/package.json +1 -1
  22. package/README-zh.md +6 -6
  23. package/README.md +6 -6
  24. package/app/components/AppHeader.vue +0 -17
  25. package/app/components/AppSidebar.vue +1 -0
  26. package/docs/full-test-plan.md +4 -0
  27. package/docs/plugins.en.md +1 -0
  28. package/docs/plugins.md +1 -0
  29. package/i18n/locales/en.ts +1 -0
  30. package/i18n/locales/zh-CN.ts +1 -0
  31. package/package.json +1 -1
  32. package/scripts/_audit/detached-start.mjs +103 -0
  33. package/scripts/_audit/guard-upstream-liveness.mjs +104 -0
  34. package/scripts/_dbg-perms-e2e.mjs +10 -3
  35. package/scripts/_dbg-protocol-matrix.mjs +453 -441
  36. package/scripts/_dbg-render-regression.mjs +151 -130
  37. package/scripts/_dbg-town-agents-probe.mjs +60 -0
  38. package/scripts/_dbg-visual-acceptance.mjs +63 -0
  39. package/scripts/e2e-agent-team.mjs +5 -3
  40. package/scripts/three-system-e2e.mjs +6 -6
  41. package/server/plugins-builtin/rag-bridge/index.mjs +9 -2
  42. package/.output/public/_nuxt/DVsjtsvZ2.js +0 -1
  43. package/.output/public/_nuxt/builds/meta/44b66249-54d0-4c8a-9057-9755a32329e2.json +0 -1
  44. package/.output/public/_nuxt/default.DE_vT238.css +0 -1
@@ -1,130 +1,151 @@
1
- /** 渲染优化功能回归:/daq 行渲染与交互、/daq/[id] 详情、/town 场景与模型预览、全局 smoke。
2
- * 用法:node scripts/_dbg-render-regression.mjs [base] [email] [pass] */
3
- import puppeteer from 'puppeteer-core'
4
- import { writeFileSync } from 'node:fs'
5
-
6
- const ROOT = process.argv[2] ?? 'http://127.0.0.1:3001'
7
- const EMAIL = process.argv[3] ?? 'perf-runner@awshop.io'
8
- const PASS = process.argv[4] ?? 'Perf@Run2026'
9
- const sleep = ms => new Promise(r => setTimeout(r, ms))
10
- const results = []
11
- const ok = (name, pass, detail = '') => {
12
- results.push({ name, pass, detail })
13
- console.log(`${pass ? '✔' : '✖'} ${name}${detail ? ' —— ' + detail : ''}`)
14
- }
15
-
16
- const login = await fetch(`${ROOT}/api/users/login`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ email: EMAIL, password: PASS }) }).then(r => r.json())
17
- if (!login?.data?.token) { console.error('LOGIN FAIL'); process.exit(1) }
18
- const browser = await puppeteer.launch({ executablePath: 'C:/Program Files/Google/Chrome/Application/chrome.exe', headless: 'new', args: ['--no-sandbox', '--disable-gpu', '--window-size=1600,1000'] })
19
- const page = await browser.newPage()
20
- await page.setViewport({ width: 1600, height: 1000, deviceScaleFactor: 1 })
21
- await page.setCookie({ name: 'token', value: login.data.token, domain: new URL(ROOT).hostname, path: '/' })
22
- const pageErrors = []
23
- page.on('pageerror', e => pageErrors.push(String(e).slice(0, 200)))
24
-
25
- // ---------- 1. /daq 行渲染完整性 ----------
26
- await page.goto(`${ROOT}/daq`, { waitUntil: 'domcontentloaded', timeout: 60000 })
27
- await sleep(12000)
28
- const daqStruct = await page.evaluate(() => {
29
- const rows = document.querySelectorAll('.nodes-table tbody tr')
30
- const polylines = document.querySelectorAll('.nodes-table svg polyline')
31
- const limLines = document.querySelectorAll('.nodes-table svg line.lim')
32
- const pills = document.querySelectorAll('.nodes-table .st-pill')
33
- const valCells = [...document.querySelectorAll('.nodes-table .val')].slice(0, 5).map(c => c.textContent.trim())
34
- const toggles = document.querySelectorAll('.nodes-table .node-toggle')
35
- const links = document.querySelectorAll('.nodes-table .console-link')
36
- const lineSelects = document.querySelectorAll('.nodes-table .line-sel:not(.dev-add)')
37
- return { rows: rows.length, polylines: polylines.length, limLines: limLines.length, pills: pills.length, valCells, toggles: toggles.length, links: links.length, lineSelects: lineSelects.length }
38
- })
39
- ok('/daq 行数 227', daqStruct.rows === 227, `rows=${daqStruct.rows}`)
40
- ok('/daq 每行趋势折线', daqStruct.polylines === 227, `polylines=${daqStruct.polylines}`)
41
- ok('/daq 状态 pill 齐全', daqStruct.pills >= 227, `pills=${daqStruct.pills}`)
42
- ok('/daq 启停按钮齐全', daqStruct.toggles === 227, `toggles=${daqStruct.toggles}`)
43
- ok('/daq 控制台链接齐全', daqStruct.links === 227, `links=${daqStruct.links}`)
44
- ok('/daq 产线下拉齐全', daqStruct.lineSelects === 227, `selects=${daqStruct.lineSelects}`)
45
- ok('/daq 值列有数据', daqStruct.valCells.some(v => /\d/.test(v)), daqStruct.valCells[0])
46
-
47
- // ---------- 2. WS 实时收敛:值列随读数帧变化(优化后合批仍须驱动行更新) ----------
48
- const valChanges = await page.evaluate(() => new Promise((resolve) => {
49
- const cells = [...document.querySelectorAll('.nodes-table .val')]
50
- const texts = cells.map(c => c.textContent.trim())
51
- setTimeout(() => {
52
- const now = cells.map(c => c.textContent.trim())
53
- let changed = 0
54
- for (let i = 0; i < texts.length; i++) if (now[i] !== texts[i]) changed++
55
- resolve(changed)
56
- }, 8000)
57
- }))
58
- ok('/daq WS 读数驱动行更新(8s 内 ≥5 行变化)', valChanges >= 5, `changedRows=${valChanges}`)
59
-
60
- // ---------- 3. 筛选交互(行组件随筛选增减) ----------
61
- await page.type('.nodes-search input, input[placeholder]', '压力', { delay: 30 }).catch(() => {})
62
- await sleep(1500)
63
- const searchFiltered = await page.evaluate(() => document.querySelectorAll('.nodes-table tbody tr').length)
64
- ok('/daq 搜索筛选生效', searchFiltered > 0 && searchFiltered < 227, `rows=${searchFiltered}`)
65
- await page.evaluate(() => { location.hash = '' })
66
- // 清空搜索(全选删除)
67
- await page.keyboard.down('Control'); await page.keyboard.press('a'); await page.keyboard.up('Control'); await page.keyboard.press('Backspace')
68
- await sleep(1200)
69
- const afterClear = await page.evaluate(() => document.querySelectorAll('.nodes-table tbody tr').length)
70
- ok('/daq 清空搜索恢复全量', afterClear === 227, `rows=${afterClear}`)
71
-
72
- // ---------- 4. /daq/[id] 详情页(由行内控制台链接进入) ----------
73
- const detailHref = await page.evaluate(() => document.querySelector('.nodes-table .console-link')?.getAttribute('href'))
74
- await page.goto(`${ROOT}${detailHref}`, { waitUntil: 'domcontentloaded', timeout: 60000 })
75
- await sleep(6000)
76
- const detail = await page.evaluate(() => ({
77
- bigVal: document.querySelector('.big-val')?.textContent?.trim() ?? '',
78
- liveCanvas: !!document.querySelector('.live-canvas'),
79
- histCanvas: !!document.querySelector('.hist-canvas'),
80
- facts: document.querySelectorAll('.facts div').length,
81
- }))
82
- ok('/daq/[id] 大值卡渲染', /\d/.test(detail.bigVal), detail.bigVal.slice(0, 30))
83
- ok('/daq/[id] live 趋势画布存在', detail.liveCanvas)
84
- ok('/daq/[id] 历史画布存在', detail.histCanvas)
85
- ok('/daq/[id] 参数事实表渲染', detail.facts >= 5, `facts=${detail.facts}`)
86
-
87
- // ---------- 5. /town 场景 + 仪表化 + 模型预览 ----------
88
- await page.goto(`${ROOT}/town`, { waitUntil: 'domcontentloaded', timeout: 60000 })
89
- await sleep(18000)
90
- const town1 = await page.evaluate(() => ({
91
- canvas: !!document.querySelector('#town-host canvas'),
92
- stats: window.__townStats ? { ...window.__townStats } : null,
93
- }))
94
- ok('/town 3D 画布挂载', town1.canvas)
95
- ok('/town 仪表化 __townStats 存在', !!town1.stats)
96
- ok('/town 场景实体(Agent/设备)', !!town1.stats && town1.stats.agents >= 12 && town1.stats.devices >= 30, `agents=${town1.stats?.agents} devices=${town1.stats?.devices}`)
97
- ok('/town 帧率 > 0(墙钟)', !!town1.stats && town1.stats.fps > 0, `fps=${town1.stats?.fps} rafHz=${town1.stats?.rafHz}`)
98
- ok('/town 60fps 预算生效', !!town1.stats && town1.stats.frameBudgetMs === 16.67, `frameBudgetMs=${town1.stats?.frameBudgetMs}`)
99
- const town2 = await page.evaluate(() => new Promise((resolve) => {
100
- const s0 = JSON.stringify(window.__townStats)
101
- setTimeout(() => resolve({ s0, s1: JSON.stringify(window.__townStats) }), 3000)
102
- }))
103
- ok('/town 仪表化持续更新', town2.s0 !== town2.s1)
104
-
105
- // 打开模型库抽屉(若存在入口按钮)→ 预览卡渲染不崩
106
- const libOpened = await page.evaluate(() => {
107
- const btn = [...document.querySelectorAll('button, .hud-btn, [class*="lib"]')].find(b => /模型/.test(b.textContent ?? ''))
108
- if (btn) { btn.click(); return true }
109
- return false
110
- })
111
- if (libOpened) {
112
- await sleep(4000)
113
- const previews = await page.evaluate(() => document.querySelectorAll('.model-preview-3d canvas').length)
114
- ok('/town 模型库打开且预览卡有画布', previews > 0, `previews=${previews}`)
115
- }
116
- await page.screenshot({ path: '.e2e-shots/regression-town.png' })
117
-
118
- // ---------- 6. 全局页面 smoke(200 + 无 pageerror) ----------
119
- for (const path of ['/workshop', '/dcw', '/logs', '/monitor', '/settings', '/users', '/permissions']) {
120
- const resp = await page.goto(`${ROOT}${path}`, { waitUntil: 'domcontentloaded', timeout: 60000 }).then(r => r.status()).catch(() => 0)
121
- await sleep(1500)
122
- ok(`smoke ${path}`, resp === 200, `status=${resp}`)
123
- }
124
-
125
- ok('全程无 pageerror', pageErrors.length === 0, pageErrors.slice(0, 3).join(' | '))
126
- const failed = results.filter(r => !r.pass)
127
- writeFileSync('.e2e-shots/render-regression.json', JSON.stringify({ at: new Date().toISOString(), base: ROOT, passed: results.length - failed.length, failed: failed.length, results }, null, 2))
128
- console.log(`\n==== ${results.length - failed.length}/${results.length} PASS ====${failed.length ? ' FAILED: ' + failed.map(f => f.name).join(', ') : ''}`)
129
- await browser.close()
130
- process.exit(failed.length ? 1 : 0)
1
+ /** 渲染优化功能回归:/daq 行渲染与交互、/daq/[id] 详情、/town 场景与模型预览、全局 smoke。
2
+ * 用法:node scripts/_dbg-render-regression.mjs [base] [email] [pass] */
3
+ import puppeteer from 'puppeteer-core'
4
+ import { writeFileSync } from 'node:fs'
5
+
6
+ const ROOT = process.argv[2] ?? 'http://127.0.0.1:3001'
7
+ const EMAIL = process.argv[3] ?? 'perf-runner@awshop.io'
8
+ const PASS = process.argv[4] ?? 'Perf@Run2026'
9
+ const sleep = ms => new Promise(r => setTimeout(r, ms))
10
+ const results = []
11
+ const ok = (name, pass, detail = '') => {
12
+ results.push({ name, pass, detail })
13
+ console.log(`${pass ? '✔' : '✖'} ${name}${detail ? ' —— ' + detail : ''}`)
14
+ }
15
+
16
+ const login = await fetch(`${ROOT}/api/users/login`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ email: EMAIL, password: PASS }) }).then(r => r.json())
17
+ if (!login?.data?.token) { console.error('LOGIN FAIL'); process.exit(1) }
18
+ const browser = await puppeteer.launch({ executablePath: 'C:/Program Files/Google/Chrome/Application/chrome.exe', headless: 'new', args: ['--no-sandbox', '--disable-gpu', '--window-size=1600,1000'] })
19
+ const page = await browser.newPage()
20
+ await page.setViewport({ width: 1600, height: 1000, deviceScaleFactor: 1 })
21
+ await page.setCookie({ name: 'token', value: login.data.token, domain: new URL(ROOT).hostname, path: '/' })
22
+ const pageErrors = []
23
+ page.on('pageerror', e => pageErrors.push(String(e).slice(0, 200)))
24
+
25
+ // 动态基线:节点数从 API 取(共享实例会增长,硬编码计数必然漂移)
26
+ const AUTH = { authorization: `Bearer ${login.data.token}` }
27
+ const daqCount = (await fetch(`${ROOT}/api/workshop/daq`, { headers: AUTH }).then(r => r.json())).data?.nodes?.length ?? 0
28
+ ok('/daq 基线计数(daq API)', daqCount > 0, `nodes=${daqCount}`)
29
+
30
+ // ---------- 1. /daq 行渲染完整性 ----------
31
+ await page.goto(`${ROOT}/daq`, { waitUntil: 'domcontentloaded', timeout: 60000 })
32
+ await sleep(12000)
33
+ const daqStruct = await page.evaluate(() => {
34
+ const rows = document.querySelectorAll('.nodes-table tbody tr')
35
+ const polylines = document.querySelectorAll('.nodes-table svg polyline')
36
+ const limLines = document.querySelectorAll('.nodes-table svg line.lim')
37
+ const pills = document.querySelectorAll('.nodes-table .st-pill')
38
+ const valCells = [...document.querySelectorAll('.nodes-table .val')].slice(0, 5).map(c => c.textContent.trim())
39
+ const toggles = document.querySelectorAll('.nodes-table .node-toggle')
40
+ const links = document.querySelectorAll('.nodes-table .console-link')
41
+ const lineSelects = document.querySelectorAll('.nodes-table .line-sel:not(.dev-add)')
42
+ return { rows: rows.length, polylines: polylines.length, limLines: limLines.length, pills: pills.length, valCells, toggles: toggles.length, links: links.length, lineSelects: lineSelects.length }
43
+ })
44
+ ok('/daq 行数=API 节点数', daqStruct.rows === daqCount, `rows=${daqStruct.rows} expect=${daqCount}`)
45
+ ok('/daq 趋势折线有渲染(有数节点)', daqStruct.polylines >= Math.min(10, daqCount) && daqStruct.polylines <= daqCount, `polylines=${daqStruct.polylines}/${daqCount}`)
46
+ ok('/daq 状态 pill 齐全', daqStruct.pills >= daqCount, `pills=${daqStruct.pills}`)
47
+ ok('/daq 启停按钮齐全', daqStruct.toggles === daqCount, `toggles=${daqStruct.toggles}`)
48
+ ok('/daq 控制台链接齐全', daqStruct.links === daqCount, `links=${daqStruct.links}`)
49
+ ok('/daq 产线下拉齐全', daqStruct.lineSelects === daqCount, `selects=${daqStruct.lineSelects}`)
50
+ ok('/daq 值列有数据', daqStruct.valCells.some(v => /\d/.test(v)), daqStruct.valCells[0])
51
+
52
+ // ---------- 2. WS 实时收敛:值列随读数帧变化(优化后合批仍须驱动行更新) ----------
53
+ const valChanges = await page.evaluate(() => new Promise((resolve) => {
54
+ const cells = [...document.querySelectorAll('.nodes-table .val')]
55
+ const texts = cells.map(c => c.textContent.trim())
56
+ setTimeout(() => {
57
+ const now = cells.map(c => c.textContent.trim())
58
+ let changed = 0
59
+ for (let i = 0; i < texts.length; i++) if (now[i] !== texts[i]) changed++
60
+ resolve(changed)
61
+ }, 8000)
62
+ }))
63
+ ok('/daq WS 读数驱动行更新(8s 内 ≥5 行变化)', valChanges >= 5, `changedRows=${valChanges}`)
64
+
65
+ // ---------- 3. 筛选交互(行组件随筛选增减) ----------
66
+ await page.type('.nodes-search input, input[placeholder]', '压力', { delay: 30 }).catch(() => {})
67
+ await sleep(1500)
68
+ const searchFiltered = await page.evaluate(() => document.querySelectorAll('.nodes-table tbody tr').length)
69
+ ok('/daq 搜索筛选生效', searchFiltered > 0 && searchFiltered < daqCount, `rows=${searchFiltered}`)
70
+ await page.evaluate(() => { location.hash = '' })
71
+ // 清空搜索(全选删除)
72
+ await page.keyboard.down('Control'); await page.keyboard.press('a'); await page.keyboard.up('Control'); await page.keyboard.press('Backspace')
73
+ await sleep(1200)
74
+ const afterClear = await page.evaluate(() => document.querySelectorAll('.nodes-table tbody tr').length)
75
+ ok('/daq 清空搜索恢复全量', afterClear === daqCount, `rows=${afterClear} expect=${daqCount}`)
76
+
77
+ // ---------- 4. /daq/[id] 详情页(由行内控制台链接进入) ----------
78
+ const detailHref = await page.evaluate(() => document.querySelector('.nodes-table .console-link')?.getAttribute('href'))
79
+ await page.goto(`${ROOT}${detailHref}`, { waitUntil: 'domcontentloaded', timeout: 60000 })
80
+ await sleep(6000)
81
+ const detail = await page.evaluate(() => ({
82
+ bigVal: document.querySelector('.big-val')?.textContent?.trim() ?? '',
83
+ liveCanvas: !!document.querySelector('.live-canvas'),
84
+ histCanvas: !!document.querySelector('.hist-canvas'),
85
+ facts: document.querySelectorAll('.facts div').length,
86
+ }))
87
+ ok('/daq/[id] 大值卡渲染', /\d/.test(detail.bigVal), detail.bigVal.slice(0, 30))
88
+ ok('/daq/[id] live 趋势画布存在', detail.liveCanvas)
89
+ ok('/daq/[id] 历史画布存在', detail.histCanvas)
90
+ ok('/daq/[id] 参数事实表渲染', detail.facts >= 5, `facts=${detail.facts}`)
91
+
92
+ // ---------- 5. /town 场景 + 仪表化 + 模型预览 ----------
93
+ // 夹具自愈:hydrate 只呈现「已放置」频道 —— 共享实例的布局/挂载清单随各轮测试漂移,
94
+ // 零交集时 agents=0 是数据态而非代码回归;故断言前确保第一个挂载频道已被放置。
95
+ {
96
+ const wsList = (await fetch(`${ROOT}/api/workshop/workspaces`, { headers: AUTH }).then(r => r.json())).data ?? []
97
+ const firstCid = wsList.flatMap(w => w.channelIds ?? [])[0]
98
+ const layoutsRes = await fetch(`${ROOT}/api/workshop/scene/layouts`, { headers: AUTH }).then(r => r.json())
99
+ const laidIds = new Set((layoutsRes.data?.layouts ?? []).map(l => l.channelId))
100
+ if (firstCid && !laidIds.has(firstCid)) {
101
+ const put = await fetch(`${ROOT}/api/workshop/scene/layouts/${firstCid}`, {
102
+ method: 'PUT', headers: { ...AUTH, 'content-type': 'application/json' },
103
+ body: JSON.stringify({ x: 1150, z: 1500, radiusX: 320, radiusZ: 210, shape: 'ellipse' }),
104
+ }).then(r => r.json())
105
+ console.log(` [fixture] 放置频道 ${firstCid.slice(0, 8)} → ${put.code === 0 ? 'ok' : put.message}`)
106
+ await sleep(1000)
107
+ }
108
+ }
109
+ await page.goto(`${ROOT}/town`, { waitUntil: 'domcontentloaded', timeout: 60000 })
110
+ await sleep(18000)
111
+ const town1 = await page.evaluate(() => ({
112
+ canvas: !!document.querySelector('#town-host canvas'),
113
+ stats: window.__townStats ? { ...window.__townStats } : null,
114
+ }))
115
+ ok('/town 3D 画布挂载', town1.canvas)
116
+ ok('/town 仪表化 __townStats 存在', !!town1.stats)
117
+ ok('/town 场景实体(Agent/设备)', !!town1.stats && town1.stats.agents >= 1 && town1.stats.devices >= 5, `agents=${town1.stats?.agents} devices=${town1.stats?.devices}`)
118
+ ok('/town 帧率 > 0(墙钟)', !!town1.stats && town1.stats.fps > 0, `fps=${town1.stats?.fps} rafHz=${town1.stats?.rafHz}`)
119
+ ok('/town 60fps 预算生效', !!town1.stats && town1.stats.frameBudgetMs === 16.67, `frameBudgetMs=${town1.stats?.frameBudgetMs}`)
120
+ const town2 = await page.evaluate(() => new Promise((resolve) => {
121
+ const s0 = JSON.stringify(window.__townStats)
122
+ setTimeout(() => resolve({ s0, s1: JSON.stringify(window.__townStats) }), 3000)
123
+ }))
124
+ ok('/town 仪表化持续更新', town2.s0 !== town2.s1)
125
+
126
+ // 打开模型库抽屉(若存在入口按钮)→ 预览卡渲染不崩
127
+ const libOpened = await page.evaluate(() => {
128
+ const btn = [...document.querySelectorAll('button, .hud-btn, [class*="lib"]')].find(b => /模型/.test(b.textContent ?? ''))
129
+ if (btn) { btn.click(); return true }
130
+ return false
131
+ })
132
+ if (libOpened) {
133
+ await sleep(4000)
134
+ const previews = await page.evaluate(() => document.querySelectorAll('.model-preview-3d canvas').length)
135
+ ok('/town 模型库打开且预览卡有画布', previews > 0, `previews=${previews}`)
136
+ }
137
+ await page.screenshot({ path: '.e2e-shots/regression-town.png' })
138
+
139
+ // ---------- 6. 全局页面 smoke(200 + 无 pageerror) ----------
140
+ for (const path of ['/workshop', '/dcw', '/logs', '/monitor', '/settings', '/users', '/permissions']) {
141
+ const resp = await page.goto(`${ROOT}${path}`, { waitUntil: 'domcontentloaded', timeout: 60000 }).then(r => r.status()).catch(() => 0)
142
+ await sleep(1500)
143
+ ok(`smoke ${path}`, resp === 200, `status=${resp}`)
144
+ }
145
+
146
+ ok('全程无 pageerror', pageErrors.length === 0, pageErrors.slice(0, 3).join(' | '))
147
+ const failed = results.filter(r => !r.pass)
148
+ writeFileSync('.e2e-shots/render-regression.json', JSON.stringify({ at: new Date().toISOString(), base: ROOT, passed: results.length - failed.length, failed: failed.length, results }, null, 2))
149
+ console.log(`\n==== ${results.length - failed.length}/${results.length} PASS ====${failed.length ? ' FAILED: ' + failed.map(f => f.name).join(', ') : ''}`)
150
+ await browser.close()
151
+ process.exit(failed.length ? 1 : 0)
@@ -0,0 +1,60 @@
1
+ /** 诊断探针:/town 场景 agents=0 之谜 —— 注入 WS 钩子记录 channel.snapshot,转储 __townStats。 */
2
+ import puppeteer from 'puppeteer-core'
3
+
4
+ const ROOT = process.argv[2] ?? 'http://127.0.0.1:3001'
5
+ const EMAIL = process.argv[3] ?? 'admin@awshop.local'
6
+ const PASS = process.argv[4] ?? 'admin123'
7
+ const sleep = ms => new Promise(r => setTimeout(r, ms))
8
+
9
+ const login = await fetch(`${ROOT}/api/users/login`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ email: EMAIL, password: PASS }) }).then(r => r.json())
10
+ if (!login?.data?.token) { console.error('LOGIN FAIL'); process.exit(1) }
11
+ const browser = await puppeteer.launch({ executablePath: 'C:/Program Files/Google/Chrome/Application/chrome.exe', headless: 'new', args: ['--no-sandbox', '--disable-gpu', '--window-size=1600,1000'] })
12
+ const page = await browser.newPage()
13
+ await page.setViewport({ width: 1600, height: 1000, deviceScaleFactor: 1 })
14
+ await page.setCookie({ name: 'token', value: login.data.token, domain: new URL(ROOT).hostname, path: '/' })
15
+ await page.evaluateOnNewDocument(() => {
16
+ window.__wslog = []
17
+ const OrigWS = window.WebSocket
18
+ window.WebSocket = function (url, protocols) {
19
+ window.__wslog.push({ ev: 'open-req', url: String(url) })
20
+ const ws = protocols !== undefined ? new OrigWS(url, protocols) : new OrigWS(url)
21
+ ws.addEventListener('open', () => window.__wslog.push({ ev: 'open', url: String(url) }))
22
+ ws.addEventListener('close', (e) => window.__wslog.push({ ev: 'close', code: e.code, reason: e.reason }))
23
+ ws.addEventListener('error', () => window.__wslog.push({ ev: 'error', url: String(url) }))
24
+ ws.addEventListener('message', (ev) => {
25
+ const raw = typeof ev.data === 'string' ? ev.data : ''
26
+ let entry = { ev: 'msg', bytes: ev.data?.size ?? raw.length, kind: ev.data?.constructor?.name }
27
+ try {
28
+ const d = JSON.parse(raw)
29
+ entry.t = d?.type ?? d?.event ?? '?'
30
+ if (String(entry.t).includes('snapshot')) {
31
+ const ag = d?.data?.agents ?? d?.payload?.agents
32
+ entry.agents = Array.isArray(ag) ? ag.length : typeof ag
33
+ entry.channelId = d?.data?.channelId ?? d?.channelId
34
+ }
35
+ }
36
+ catch { entry.parse = 'fail' }
37
+ window.__wslog.push(entry)
38
+ })
39
+ const origSend = ws.send.bind(ws)
40
+ ws.send = (d) => { window.__wslog.push({ ev: 'send', head: String(d).slice(0, 120) }); return origSend(d) }
41
+ return ws
42
+ }
43
+ window.WebSocket.prototype = OrigWS.prototype
44
+ window.WebSocket.CONNECTING = OrigWS.CONNECTING
45
+ window.WebSocket.OPEN = OrigWS.OPEN
46
+ window.WebSocket.CLOSING = OrigWS.CLOSING
47
+ window.WebSocket.CLOSED = OrigWS.CLOSED
48
+ })
49
+ page.on('pageerror', e => console.log('PAGEERROR:', String(e).slice(0, 160)))
50
+
51
+ await page.goto(`${ROOT}/town`, { waitUntil: 'domcontentloaded', timeout: 60000 })
52
+ await sleep(15000)
53
+ const dump = await page.evaluate(() => ({
54
+ stats: window.__townStats ?? null,
55
+ wslog: (window.__wslog ?? []).slice(0, 30),
56
+ empty: !!document.querySelector('[data-hud="town-empty"]'),
57
+ loading: !!document.querySelector('[data-hud="town-loading"]'),
58
+ }))
59
+ console.log(JSON.stringify(dump, null, 1))
60
+ await browser.close()
@@ -0,0 +1,63 @@
1
+ /** P7 目视验收截图:关键页面逐页截图到 .e2e-shots/p7-*.png,并汇总每页 console error。 */
2
+ import puppeteer from 'puppeteer-core'
3
+ import { mkdirSync } from 'node:fs'
4
+
5
+ const ROOT = process.argv[2] ?? 'http://127.0.0.1:3001'
6
+ const EMAIL = process.argv[3] ?? 'admin@awshop.local'
7
+ const PASS = process.argv[4] ?? 'admin123'
8
+ const OUT = '.e2e-shots'
9
+ mkdirSync(OUT, { recursive: true })
10
+
11
+ const login = await fetch(`${ROOT}/api/users/login`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ email: EMAIL, password: PASS }) }).then(r => r.json())
12
+ if (!login?.data?.token) { console.error('LOGIN FAIL'); process.exit(1) }
13
+
14
+ const browser = await puppeteer.launch({ executablePath: 'C:/Program Files/Google/Chrome/Application/chrome.exe', headless: 'new', args: ['--no-sandbox', '--disable-gpu', '--window-size=1600,1000'] })
15
+ const page = await browser.newPage()
16
+ await page.setViewport({ width: 1600, height: 1000, deviceScaleFactor: 1 })
17
+ await page.setCookie({ name: 'token', value: login.data.token, domain: new URL(ROOT).hostname, path: '/' })
18
+
19
+ const pages = [
20
+ ['workshop', '/workshop', 9000],
21
+ ['daq', '/daq', 12000],
22
+ ['dcw', '/dcw', 9000],
23
+ ['town', '/town', 20000],
24
+ ['settings', '/settings', 9000],
25
+ ['monitor', '/monitor', 9000],
26
+ ['aml', '/aml', 9000],
27
+ ['logs', '/logs', 9000],
28
+ ['users', '/users', 9000],
29
+ ]
30
+ const summary = []
31
+ for (const [name, path, wait] of pages) {
32
+ const errors = []
33
+ const onConsole = (m) => { if (m.type() === 'error') errors.push(m.text().slice(0, 120)) }
34
+ page.on('console', onConsole)
35
+ try {
36
+ await page.goto(`${ROOT}${path}`, { waitUntil: 'domcontentloaded', timeout: 60000 })
37
+ await new Promise(r => setTimeout(r, wait))
38
+ const file = `${OUT}/p7-${name}.png`
39
+ await page.screenshot({ path: file })
40
+ summary.push({ name, ok: true, errors })
41
+ console.log(`✓ ${name} → ${file}${errors.length ? ` (console errors: ${errors.length})` : ''}`)
42
+ } catch (e) {
43
+ summary.push({ name, ok: false, errors: [String(e).slice(0, 120)] })
44
+ console.log(`✖ ${name}: ${String(e).slice(0, 120)}`)
45
+ }
46
+ page.off('console', onConsole)
47
+ }
48
+ // daq 详情页(从列表第一个 console-link 进入)
49
+ try {
50
+ await page.goto(`${ROOT}/daq`, { waitUntil: 'domcontentloaded', timeout: 60000 })
51
+ await new Promise(r => setTimeout(r, 10000))
52
+ const href = await page.evaluate(() => document.querySelector('.nodes-table .console-link')?.getAttribute('href'))
53
+ if (href) {
54
+ await page.goto(`${ROOT}${href}`, { waitUntil: 'domcontentloaded', timeout: 60000 })
55
+ await new Promise(r => setTimeout(r, 8000))
56
+ await page.screenshot({ path: `${OUT}/p7-daq-detail.png` })
57
+ console.log(`✓ daq-detail → ${OUT}/p7-daq-detail.png`)
58
+ }
59
+ } catch (e) { console.log(`✖ daq-detail: ${String(e).slice(0, 100)}`) }
60
+ await browser.close()
61
+ const bad = summary.filter(s => !s.ok || s.errors.length)
62
+ console.log(`\n页面数=${summary.length} 有问题页=${bad.length}`)
63
+ for (const b of bad) console.log(' -', b.name, b.errors.slice(0, 2).join(' | '))
@@ -7,7 +7,9 @@
7
7
  * 5. 成员移除 / team 更新 / 删除
8
8
  * 运行: node scripts/e2e-agent-team.mjs [--base http://127.0.0.1:3000]
9
9
  */
10
- const BASE = (process.argv.find(a => a.startsWith('--base='))?.slice(7) ?? 'http://127.0.0.1:3000') + '/api/workshop'
10
+ const BASE = (process.argv.find(a => a.startsWith('--base='))?.slice(7) ?? 'http://127.0.0.1:3000') + '/api/workshop'
11
+ // 注册走平台 origin 的 /api/users/register(不在 /api/workshop 下;注册现要求 email+password)
12
+ const ORIGIN = BASE.slice(0, BASE.indexOf('/api'))
11
13
 
12
14
  let pass = 0
13
15
  let fail = 0
@@ -21,9 +23,9 @@ function check(name, ok, detail = '') {
21
23
  }
22
24
 
23
25
  // 用户级隔离:注册测试用户;管理面 API 全程携带用户 token
24
- const __user = await fetch(BASE + '/users/register', {
26
+ const __user = await fetch(ORIGIN + '/api/users/register', {
25
27
  method: 'POST', headers: { 'content-type': 'application/json' },
26
- body: JSON.stringify({ name: 'e2e-' + Math.random().toString(36).slice(2, 10) }),
28
+ body: JSON.stringify({ name: 'e2e-' + Math.random().toString(36).slice(2, 10), email: `e2e-team-${Date.now()}@test.local`, password: 'Passw0rd!123' }),
27
29
  }).then(r => r.json()).catch(() => null)
28
30
  const __userToken = __user?.data?.token
29
31
  if (!__userToken) {
@@ -378,16 +378,16 @@ let channelB
378
378
 
379
379
  // KB 回环:kb_index 入库独特自然语句 → kb_search 命中(嵌入/分词对自然语句才可靠);kb_store 沉淀经验
380
380
  const marker = `三系统验证密语${Date.now().toString(36)}:青花瓷泵站三十七号叶片于黄昏完成校准`
381
- const idx = await invoke(toolInvoker, 'kb_index', {
381
+ const idx = await invoke(toolInvoker, 'kb_index', { // rag 负载下事件循环停顿可达 60s+,放宽超时
382
382
  title: `三系统集成验证 ${Date.now().toString(36)}`,
383
383
  content: `# 三系统集成验证\n\n${marker}。\n\n本文由 three-system-e2e 经 rag-bridge 插件写入,验证 文档写盘 → 向量索引 → kb_search 检索 全链路。\n`,
384
384
  tags: ['e2e'],
385
- })
385
+ }, 120000)
386
386
  ok(resultText(idx).includes('已入库'), 'kb_index 文档入库成功', resultText(idx).slice(0, 80))
387
387
  let hit = ''
388
- for (let i = 0; i < 10; i++) { // 首次检索可能触发 BGE-M3 模型加载,放宽到 10×3s
388
+ for (let i = 0; i < 20; i++) { // 首次检索可能触发 BGE-M3 加载/异步索引落盘,放宽到 20×3s
389
389
  await sleep(3000)
390
- const s = await invoke(toolInvoker, 'kb_search', { query: marker })
390
+ const s = await invoke(toolInvoker, 'kb_search', { query: marker }, 120000)
391
391
  hit = resultText(s)
392
392
  if (hit.includes(marker)) break
393
393
  }
@@ -400,7 +400,7 @@ let channelB
400
400
  solution: '先查产线 LineRun 是否活动、节点 publishIntervalMs 是否在节拍上,再缩窗重试。',
401
401
  key_lessons: ['空窗先查打标窗口', 'bucket 5s 可对齐多节点'],
402
402
  tags: ['e2e', 'daq'],
403
- })
403
+ }, 120000)
404
404
  ok(resultText(store).includes('已沉淀'), 'kb_store 经验沉淀成功', resultText(store).slice(0, 80))
405
405
 
406
406
  // 跨通道:分析组长 → 控制组长(require_reply;用频道 ID 定向,避免跨账号同名频道歧义)
@@ -628,7 +628,7 @@ let runId = ''
628
628
  return (envelopeData(members) ?? []).find(m => m?.role === 'lead') ?? leadA
629
629
  }
630
630
  let gone = false
631
- for (let i = 0; i < 20 && !gone; i++) {
631
+ for (let i = 0; i < 40 && !gone; i++) { // 共享实例 agent 运行时重建慢,60s 窗
632
632
  await sleep(1500)
633
633
  const fl = await freshLead()
634
634
  const h = await raw('GET', `${BASE}/api/plugins/diag-bridge/health`, { token: userToken })
@@ -349,10 +349,17 @@ export default {
349
349
  outbound: state.outbound,
350
350
  auth: kbToken() ? 'bearer' : 'anonymous',
351
351
  // token 配置与「实测是否被接受」分开呈现:401/403 = 明确被拒(运维一眼看出 token 配错),
352
- // 网络不通/超时为 null(未知),2xx 或非鉴权类失败为 true(未被拒)
352
+ // 网络不通/超时为 null(未知),2xx 或非鉴权类失败为 true(未被拒)。
353
+ // 实测打受保护 GET /api/v1/auth/me —— rag 后端 GET 只读端点公开(/health 匿名可过,
354
+ // 测不出 token 对错),/auth/me 无 token/错 token 均 401。
353
355
  token: {
354
356
  configured: Boolean(kbToken()),
355
- accepted: backend.ok ? true : (backend.status === 401 || backend.status === 403 ? false : null),
357
+ accepted: await (async () => {
358
+ if (!state.outbound) return null
359
+ if (!kbToken()) return backend.ok ? true : null
360
+ const me = await callJson('GET', `${base()}/api/v1/auth/me`, null, { timeoutMs: 5000, retries: 0 })
361
+ return me.ok ? true : (me.status === 401 || me.status === 403 ? false : null)
362
+ })(),
356
363
  },
357
364
  kb: { name: KB_NAME, id: kbId() || null },
358
365
  backend: { url: base(), ok: backend.ok, ...(backend.ok ? { status: backend.body?.status ?? null } : { status: backend.status ?? null, error: backend.error }) },
@@ -1 +0,0 @@
1
- import{a as e,c as t,j as n}from"./BU83-NnX.js";import{$ as r,D as i,G as a,H as o,Ht as s,P as c,R as l,S as u,Ut as d,V as f,Vt as p,W as m,b as h,d as g,f as _,h as v,ht as y,i as b,m as x,nt as S,p as C,pt as w,tt as T,v as E,xt as D,y as O}from"./CSkxz2NX.js";import{d as ee,f as k,n as te,t as A,u as j}from"./D6uzXueg.js";import{d as M,p as N}from"./BJHKLJJU.js";import{i as ne}from"./YnQxK3eG.js";import{n as re,r as ie,t as ae}from"./BzHEilTt.js";import{t as oe}from"./BHA40aHa.js";import{n as P}from"./KZQX7xoM.js";import{t as se}from"./D9Oidm5I.js";import{At as F,St as ce,d as le,jt as I,n as L,nt as R,t as z,u as B,wt as V}from"./4sIEbgcl.js";import{i as ue}from"./DYr1VMiQ.js";import{t as de}from"./aWWIh8YL.js";import{a as H,i as U,n as fe,r as pe,t as me}from"./CzlMDL5H.js";import{t as he}from"./lO5tpDZ5.js";import{n as W}from"./JCoYF4IJ.js";import{t as ge}from"./Crod5z2f.js";import{t as _e}from"./C4DWUZ6d2.js";import{t as ve}from"./JTLM-kqr2.js";import{t as ye}from"./DIaZ2PTo2.js";import{t as G}from"./BDNMzG2s2.js";import{t as be}from"./Cc_9VzD42.js";import{t as xe}from"./D0ZBQrN_2.js";import{m as Se,p as Ce,t as we}from"./BNT8gYU92.js";import{t as Te}from"./BN6iuJEg2.js";var K=e=>{let{componentCls:t,colorBgContainer:n,colorBgBody:r,colorText:i}=e;return{[`${t}-sider-light`]:{background:n,[`${t}-sider-trigger`]:{color:i,background:n},[`${t}-sider-zero-width-trigger`]:{color:i,background:n,border:`1px solid ${r}`,borderInlineStart:0}}}},Ee=e=>{let{antCls:t,componentCls:n,colorText:r,colorTextLightSolid:i,colorBgHeader:a,colorBgBody:o,colorBgTrigger:s,layoutHeaderHeight:c,layoutHeaderPaddingInline:l,layoutHeaderColor:u,layoutFooterPadding:d,layoutTriggerHeight:f,layoutZeroTriggerSize:p,motionDurationMid:m,motionDurationSlow:h,fontSize:g,borderRadius:_}=e;return{[n]:F(F({display:`flex`,flex:`auto`,flexDirection:`column`,color:r,minHeight:0,background:o,"&, *":{boxSizing:`border-box`},[`&${n}-has-sider`]:{flexDirection:`row`,[`> ${n}, > ${n}-content`]:{width:0}},[`${n}-header, &${n}-footer`]:{flex:`0 0 auto`},[`${n}-header`]:{height:c,paddingInline:l,color:u,lineHeight:`${c}px`,background:a,[`${t}-menu`]:{lineHeight:`inherit`}},[`${n}-footer`]:{padding:d,color:r,fontSize:g,background:o},[`${n}-content`]:{flex:`auto`,minHeight:0},[`${n}-sider`]:{position:`relative`,minWidth:0,background:a,transition:`all ${m}, background 0s`,"&-children":{height:`100%`,marginTop:-.1,paddingTop:.1,[`${t}-menu${t}-menu-inline-collapsed`]:{width:`auto`}},"&-has-trigger":{paddingBottom:f},"&-right":{order:1},"&-trigger":{position:`fixed`,bottom:0,zIndex:1,height:f,color:i,lineHeight:`${f}px`,textAlign:`center`,background:s,cursor:`pointer`,transition:`all ${m}`},"&-zero-width":{"> *":{overflow:`hidden`},"&-trigger":{position:`absolute`,top:c,insetInlineEnd:-p,zIndex:1,width:p,height:p,color:i,fontSize:e.fontSizeXL,display:`flex`,alignItems:`center`,justifyContent:`center`,background:a,borderStartStartRadius:0,borderStartEndRadius:_,borderEndEndRadius:_,borderEndStartRadius:0,cursor:`pointer`,transition:`background ${h} ease`,"&::after":{position:`absolute`,inset:0,background:`transparent`,transition:`all ${h}`,content:`""`},"&:hover::after":{background:`rgba(255, 255, 255, 0.2)`},"&-right":{insetInlineStart:-p,borderStartStartRadius:_,borderStartEndRadius:0,borderEndEndRadius:0,borderEndStartRadius:_}}}}},K(e)),{"&-rtl":{direction:`rtl`}})}},De=le(`Layout`,e=>{let{colorText:t,controlHeightSM:n,controlHeight:r,controlHeightLG:i,marginXXS:a}=e,o=i*1.25;return[Ee(B(e,{layoutHeaderHeight:r*2,layoutHeaderPaddingInline:o,layoutHeaderColor:t,layoutFooterPadding:`${n}px ${o}px`,layoutTriggerHeight:i+a*2,layoutZeroTriggerSize:i}))]},e=>{let{colorBgLayout:t}=e;return{colorBgHeader:`#001529`,colorBgBody:t,colorBgTrigger:`#002140`}}),q=()=>({prefixCls:String,hasSider:{type:Boolean,default:void 0},tagName:String});function J(e){let{suffixCls:t,tagName:n,name:r}=e;return e=>u({compatConfig:{MODE:3},name:r,props:q(),setup(r,i){let{slots:a}=i,{prefixCls:o}=L(t,r);return()=>{let t=F(F({},r),{prefixCls:o.value,tagName:n});return h(e,t,a)}}})}var Y=u({compatConfig:{MODE:3},props:q(),setup(e,t){let{slots:n}=t;return()=>h(e.tagName,{class:e.prefixCls},n)}}),Oe=u({compatConfig:{MODE:3},inheritAttrs:!1,props:q(),setup(e,t){let{slots:n,attrs:r}=t,{prefixCls:i,direction:a}=L(``,e),[s,c]=De(i),l=w([]);o(H,{addSider:e=>{l.value=[...l.value,e]},removeSider:e=>{l.value=l.value.filter(t=>t!==e)}});let u=g(()=>{let{prefixCls:t,hasSider:n}=e;return{[c.value]:!0,[`${t}`]:!0,[`${t}-has-sider`]:typeof n==`boolean`?n:l.value.length>0,[`${t}-rtl`]:a.value===`rtl`}});return()=>{let{tagName:t}=e;return s(h(t,F(F({},r),{class:[u.value,r.class]}),n))}}}),ke=J({suffixCls:`layout`,tagName:`section`,name:`ALayout`})(Oe),X=J({suffixCls:`layout-header`,tagName:`header`,name:`ALayoutHeader`})(Y),Z=J({suffixCls:`layout-footer`,tagName:`footer`,name:`ALayoutFooter`})(Y),Q=J({suffixCls:`layout-content`,tagName:`main`,name:`ALayoutContent`})(Y),Ae={icon:{tag:`svg`,attrs:{viewBox:`0 0 1024 1024`,focusable:`false`},children:[{tag:`path`,attrs:{d:`M912 192H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 284H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 284H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM104 228a56 56 0 10112 0 56 56 0 10-112 0zm0 284a56 56 0 10112 0 56 56 0 10-112 0zm0 284a56 56 0 10112 0 56 56 0 10-112 0z`}}]},name:`bars`,theme:`outlined`};function je(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]==null?{}:Object(arguments[t]),r=Object.keys(n);typeof Object.getOwnPropertySymbols==`function`&&(r=r.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),r.forEach(function(t){Me(e,t,n[t])})}return e}function Me(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var Ne=function(e,t){var r=je({},e,t.attrs);return h(n,je({},r,{icon:Ae}),null)};Ne.displayName=`BarsOutlined`,Ne.inheritAttrs=!1;var Pe={xs:`479.98px`,sm:`575.98px`,md:`767.98px`,lg:`991.98px`,xl:`1199.98px`,xxl:`1599.98px`,xxxl:`1999.98px`},Fe=()=>({prefixCls:String,collapsible:{type:Boolean,default:void 0},collapsed:{type:Boolean,default:void 0},defaultCollapsed:{type:Boolean,default:void 0},reverseArrow:{type:Boolean,default:void 0},zeroWidthTriggerStyle:{type:Object,default:void 0},trigger:z.any,width:z.oneOfType([z.number,z.string]),collapsedWidth:z.oneOfType([z.number,z.string]),breakpoint:z.oneOf(R(`xs`,`sm`,`md`,`lg`,`xl`,`xxl`,`xxxl`)),theme:z.oneOf(R(`light`,`dark`)).def(`dark`),onBreakpoint:Function,onCollapse:Function}),Ie=(()=>{let e=0;return function(){let t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:``;return e+=1,`${t}${e}`}})(),$=u({compatConfig:{MODE:3},name:`ALayoutSider`,inheritAttrs:!1,props:ce(Fe(),{collapsible:!1,defaultCollapsed:!1,reverseArrow:!1,width:200,collapsedWidth:80}),emits:[`breakpoint`,`update:collapsed`,`collapse`],setup(e,t){let{emit:n,attrs:a,slots:s}=t,{prefixCls:u}=L(`layout-sider`,e),d=i(H,void 0),f=y(!!(e.collapsed===void 0?e.defaultCollapsed:e.collapsed)),p=y(!1);r(()=>e.collapsed,()=>{f.value=!!e.collapsed}),o(U,f);let m=(t,r)=>{e.collapsed===void 0&&(f.value=t),n(`update:collapsed`,t),n(`collapse`,t,r)},g=y(e=>{p.value=e.matches,n(`breakpoint`,e.matches),f.value!==e.matches&&m(e.matches,`responsive`)}),_;function v(e){return g.value(e)}let b=Ie(`ant-sider-`);d&&d.addSider(b),l(()=>{r(()=>e.breakpoint,()=>{try{_?.removeEventListener(`change`,v)}catch{_?.removeListener(v)}if(typeof window<`u`){let{matchMedia:t}=window;if(t&&e.breakpoint&&e.breakpoint in Pe){_=t(`(max-width: ${Pe[e.breakpoint]})`);try{_.addEventListener(`change`,v)}catch{_.addListener(v)}v(_)}}},{immediate:!0})}),c(()=>{try{_?.removeEventListener(`change`,v)}catch{_?.removeListener(v)}d&&d.removeSider(b)});let x=()=>{m(!f.value,`clickTrigger`)};return()=>{let t=u.value,{collapsedWidth:n,width:r,reverseArrow:i,zeroWidthTriggerStyle:o,trigger:c=s.trigger?.call(s),collapsible:l,theme:d}=e,m=f.value?n:r,g=he(m)?`${m}px`:String(m),_=parseFloat(String(n||0))===0?h(`span`,{onClick:x,class:V(`${t}-zero-width-trigger`,`${t}-zero-width-trigger-${i?`right`:`left`}`),style:o},[c||h(Ne,null,null)]):null,v={expanded:h(i?W:ge,null,null),collapsed:h(i?ge:W,null,null)}[f.value?`collapsed`:`expanded`],y=c===null?null:_||h(`div`,{class:`${t}-trigger`,onClick:x,style:{width:g}},[c||v]),b=[a.style,{flex:`0 0 ${g}`,maxWidth:g,minWidth:g,width:g}],S=V(t,`${t}-${d}`,{[`${t}-collapsed`]:!!f.value,[`${t}-has-trigger`]:l&&c!==null&&!_,[`${t}-below`]:!!p.value,[`${t}-zero-width`]:parseFloat(g)===0},a.class);return h(`aside`,I(I({},a),{},{class:S,style:b}),[h(`div`,{class:`${t}-children`},[s.default?.call(s)]),l||p.value&&_?y:null])}}}),Le=X,Re=Z,ze=$,Be=Q,Ve=F(ke,{Header:X,Footer:Z,Content:Q,Sider:$,install:e=>(e.component(ke.name,ke),e.component(X.name,X),e.component(Z.name,Z),e.component($.name,$),e.component(Q.name,Q),e)}),He=[`width`,`height`],Ue=Object.assign(u({__name:`AwLogo`,props:{size:{default:32}},setup(e){return(t,n)=>(f(),v(`svg`,{width:e.size,height:e.size,viewBox:`0 0 64 64`,fill:`none`,"aria-hidden":`true`},[...n[0]||=[E(`<defs><linearGradient id="aw-logo-gradient" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#35e0a0"></stop><stop offset="1" stop-color="#41c8f4"></stop></linearGradient></defs><path d="M32 4 L56 18 V46 L32 60 L8 46 V18 Z" stroke="url(#aw-logo-gradient)" stroke-width="4" stroke-linejoin="round"></path><path d="M32 15 L46.7 23.5 V40.5 L32 49 L17.3 40.5 V23.5 Z" stroke="#41c8f4" stroke-opacity="0.45" stroke-width="1.8" stroke-dasharray="4 3"></path><path d="M32 23 L25 38 M32 23 L39 38 M25 38 L39 38" stroke="#35e0a0" stroke-width="1.8" stroke-opacity="0.75"></path><circle cx="32" cy="23" r="3.4" fill="#35e0a0"></circle><circle cx="25" cy="38" r="3.4" fill="#41c8f4"></circle><circle cx="39" cy="38" r="3.4" fill="#41c8f4"></circle>`,7)]],8,He))}}),{__name:`AwLogo`}),We={class:`logo`},Ge={class:`logo-mark`,"aria-hidden":`true`},Ke={class:`logo-text`},qe={class:`logo-title`},Je={class:`app-menu`},Ye={class:`menu-group-label`},Xe={class:`menu-group-text`},Ze={class:`menu-group-sep`,"aria-hidden":`true`},Qe=[`title`,`onClick`],$e={class:`sider-footer`},et={class:`footer-line`},tt=Object.assign(G(u({__name:`AppSidebar`,setup(e){let t=ye(),{t:n}=P(),r=N(),i=ae(),a=re(),o=g(()=>[{id:`console`,label:n(`menu.groups.console`),items:[{key:`/`,icon:`i-tabler-layout-dashboard`,label:n(`menu.dashboard`),motion:`im-pop`},{key:`/workshop`,icon:`i-tabler-box`,label:n(`menu.workshop`),motion:`im-pop`},{key:`/town`,icon:`i-tabler-map-2`,label:n(`menu.town`),motion:`im-pop`},{key:`/daq`,icon:`i-tabler-activity`,label:n(`menu.daq`),motion:`im-pop`},{key:`/dcw`,icon:`i-tabler-settings-automation`,label:n(`menu.dcw`),motion:`im-pop`}]},{id:`ops`,label:n(`menu.groups.ops`),items:[{key:`/monitor`,icon:`i-tabler-cpu`,label:n(`menu.monitor`),motion:`im-pulse`},{key:`/logs`,icon:`i-tabler-list-details`,label:n(`menu.logs`),motion:`im-pop`}]},{id:`system`,label:n(`menu.groups.system`),items:[{key:`/tokens`,icon:`i-tabler-key`,label:n(`menu.tokens`),motion:`im-nudge-up`},{key:`/users`,icon:`i-tabler-users-group`,label:n(`menu.users`),motion:`im-pop`},{key:`/permissions`,icon:`i-tabler-shield-lock`,label:n(`menu.permissions`),motion:`im-pop`,adminOnly:!0},{key:`/plugins`,icon:`i-tabler-puzzle`,label:n(`menu.plugins`),motion:`im-pop`},{key:`/settings`,icon:`i-tabler-settings`,label:n(`menu.settings`),motion:`im-rotate`}]}].map(e=>({...e,items:e.items.filter(e=>!e.adminOnly||t.isAdmin)})).filter(e=>e.items.length>0)),s=e=>e===`/`?r.path===`/`:r.path.startsWith(e),c=e=>{s(e)||M(e)};return(e,t)=>{let n=Ue,r=ze;return f(),C(r,{collapsed:D(a).sidebarCollapsed,"onUpdate:collapsed":t[0]||=e=>D(a).sidebarCollapsed=e,trigger:null,collapsible:``,breakpoint:`lg`,width:228,"collapsed-width":64,class:`app-sider`},{default:T(()=>[_(`div`,We,[_(`div`,Ge,[h(n,{size:30})]),h(A,{name:`slide-fade`},{default:T(()=>[S(_(`div`,Ke,[_(`span`,qe,d(D(i).name),1),t[1]||=_(`span`,{class:`logo-sub`},`digital twin`,-1)],512),[[j,!D(a).sidebarCollapsed]])]),_:1})]),_(`nav`,Je,[(f(!0),v(b,null,m(D(o),e=>(f(),v(`div`,{key:e.id,class:`menu-group`},[S(_(`div`,Ye,[_(`span`,Xe,d(e.label),1),t[2]||=_(`span`,{class:`menu-group-rule`,"aria-hidden":`true`},null,-1)],512),[[j,!D(a).sidebarCollapsed]]),S(_(`div`,Ze,null,512),[[j,D(a).sidebarCollapsed]]),(f(!0),v(b,null,m(e.items,e=>(f(),v(`button`,{key:e.key,type:`button`,class:p([`menu-item im`,{active:s(e.key)}]),title:D(a).sidebarCollapsed?e.label:void 0,onClick:t=>c(e.key)},[_(`span`,{class:p([`menu-icon`,[e.icon,e.motion]])},null,2),S(_(`span`,{class:`menu-label`},d(e.label),513),[[j,!D(a).sidebarCollapsed]])],10,Qe))),128))]))),128))]),S(_(`div`,$e,[t[4]||=_(`div`,{class:`footer-rule`},null,-1),_(`div`,et,[_(`span`,null,`v`+d(D(i).version),1),t[3]||=_(`span`,{class:`sep`},`/`,-1),_(`span`,null,d(D(i).mode),1)])],512),[[j,!D(a).sidebarCollapsed]])]),_:1},8,[`collapsed`])}}}),[[`__scopeId`,`data-v-095cc5a0`]]),{__name:`AppSidebar`}),nt=ne(`app.routeTrail`,()=>{let e=w([]);function t(t){if(!t||t.startsWith(`/api`))return;let n=e.value.filter(e=>e.path!==t);e.value=[...n,{path:t,at:Date.now()}].slice(-8)}function n(t){let n=e.value.findIndex(e=>e.path===t);return n>=0&&(e.value=e.value.filter(e=>e.path!==t)),n}function r(){e.value=[]}return{waypoints:e,visit:t,remove:n,clear:r}},{persist:{pick:[`waypoints`],storage:ie.localStorage()}});function rt(){let{t:e}=P(),t=be(),n={"/":{key:`menu.dashboard`,icon:`i-tabler-layout-dashboard`},"/workshop":{key:`menu.workshop`,icon:`i-tabler-box`},"/workshop/agents":{key:`meta.agents`,icon:`i-tabler-users`},"/workshop/teams":{key:`meta.teams`,icon:`i-tabler-users-group`},"/workshop/channel-templates":{key:`meta.ctpl`,icon:`i-tabler-layout-grid-add`},"/town":{key:`menu.town`,icon:`i-tabler-map-2`},"/tokens":{key:`menu.tokens`,icon:`i-tabler-key`},"/daq":{key:`menu.daq`,icon:`i-tabler-activity`},"/dcw":{key:`menu.dcw`,icon:`i-tabler-settings-automation`},"/logs":{key:`menu.logs`,icon:`i-tabler-list-details`},"/permissions":{key:`menu.permissions`,icon:`i-tabler-shield-lock`},"/plugins":{key:`menu.plugins`,icon:`i-tabler-puzzle`},"/users":{key:`menu.users`,icon:`i-tabler-users-group`},"/monitor":{key:`menu.monitor`,icon:`i-tabler-cpu`},"/settings":{key:`menu.settings`,icon:`i-tabler-settings`}};return{metaFor:r=>{let i=n[r];if(i)return{title:e(i.key),icon:i.icon};let a=r.match(/^\/workshop\/w\/([^/]+)$/);if(a){let n=a[1];return{title:t.workspaces.find(e=>e.id===n)?.name??`${e(`header.workspace`)} ${n.slice(0,6)}`,icon:`i-tabler-console`}}let o=r.split(`/`).filter(Boolean).pop()??`·`;return{title:o.charAt(0).toUpperCase()+o.slice(1),icon:`i-tabler-point`}}}}var it={class:`header-left`},at=[`aria-label`],ot={class:`node-title`},st=[`aria-label`],ct=[`title`,`onClick`,`onKeydown`],lt={class:`node-title`},ut=[`aria-label`,`title`,`onClick`],dt={class:`header-right`},ft=[`title`],pt={class:`hitl-count`},mt={class:`hitl-menu`},ht={class:`hitl-menu-title`},gt=[`onClick`],_t={class:`hitl-item-top`},vt={class:`hitl-item-agent`},yt={class:`hitl-item-kind`},bt={class:`hitl-item-title`},xt=[`title`],St={class:`user-chip`},Ct={class:`user-initial aw-avatar`},wt={class:`user-meta`},Tt={class:`user-name`},Et={class:`user-role`},Dt=Object.assign(G(u({__name:`AppHeader`,setup(n){let{t:i,locale:a,locales:o,setLocale:s}=P(),u=re(),y=N(),S=nt(),E=ye(),{metaFor:A}=rt(),j=Ce(),ne=we(),ie=()=>{E.isLoggedIn&&ne.ensureConnected()};r(()=>E.token,e=>{e?(ie(),j.loadSnapshot()):j.clear()},{immediate:!0});let ae=new Set([`codex-approval`,`opencode-permission`,`dsh-permission`]),F=e=>{switch(e){case`omp-dialog`:return i(`appHeader.hitlKindOmp`);case`dcw-approval`:return i(`appHeader.hitlKindDcw`);case`codex-approval`:return i(`appHeader.hitlKindCodex`);case`opencode-permission`:return i(`appHeader.hitlKindOpencode`);case`dsh-permission`:return i(`appHeader.hitlKindDsh`);default:return e}},ce=e=>{M({path:`/monitor`,query:{agentId:e.agentId,channelId:e.channelId}})},le=Te(),I=w(null),L=async(t,n)=>{I.value=`${t.kind}:${t.id}`;try{await le.respondHitl({kind:t.kind,id:t.id,confirmed:n,cancelled:!n}),await j.loadSnapshot()}catch(t){e.error(xe(t))}finally{I.value=null}},R=g(()=>o.value.map(e=>({label:e.name,value:e.code}))),z=e=>{e!=null&&(localStorage.setItem(`aw.locale`,String(e)),s(String(e)),window.location.reload())},B=w(!1),V=w(!1),H=null;r(()=>y.path,()=>{S.visit(y.path),V.value=!1,requestAnimationFrame(()=>{V.value=!0,H&&clearTimeout(H),H=setTimeout(()=>{V.value=!1},620)})},{immediate:!1}),l(()=>{B.value=!0,S.visit(y.path)}),c(()=>{H&&clearTimeout(H)});let U=e=>{e!==y.path&&M(e)},he=e=>{let t=S.remove(e);if(e!==y.path)return;let n=S.waypoints,r=n[Math.min(Math.max(t-1,0),Math.max(n.length-1,0))]?.path??`/`;M(r)},W=w(!1);function ge(){document.fullscreenElement?(document.exitFullscreen(),W.value=!1):(document.documentElement.requestFullscreen(),W.value=!0)}let ve=g(()=>(E.user?.name??`?`).trim().charAt(0).toUpperCase()),G=g(()=>E.user?.name??i(`header.guest`)),be=g(()=>E.isLoggedIn?E.user?.role??`user`:`anonymous`),K=Se(),Ee=g(()=>K.state!==`closed`||K.lastDataAt>0),De=g(()=>K.state===`open`?K.pendingReplay?`syncing`:`live`:K.state===`connecting`?`syncing`:`down`),q=g(()=>K.state===`open`?K.pendingReplay?i(`appHeader.kwsdot0002`):i(`appHeader.kwsdot0001`):K.state===`connecting`?i(`appHeader.kwsdot0003`):i(`appHeader.kwsdot0004`)),J=g(()=>{let e=[{key:`tokens`,icon:`i-tabler-key`,label:i(`menu.tokens`)},{key:`settings`,icon:`i-tabler-adjustments`,label:i(`menu.settings`)}];return E.isLoggedIn&&(e.push({key:`d-logout`,label:``,divider:!0}),e.push({key:`logout`,icon:`i-tabler-logout`,label:i(`header.logout`),danger:!0})),e}),Y=async({key:e})=>{e===`settings`?M(`/settings`):e===`tokens`?M(`/tokens`):e===`logout`&&(await E.logout(),M(`/workshop`))};return(e,n)=>{let r=oe,o=t,s=_e,c=se,l=ue,g=de,w=fe,E=pe,M=me,N=Le;return f(),C(N,{class:`app-header app-header-glass`},{default:T(()=>[_(`div`,it,[_(`button`,{class:`collapse-btn`,"aria-label":D(u).sidebarCollapsed?D(i)(`header.expand`):D(i)(`header.collapse`),onClick:n[0]||=e=>D(u).toggleSidebar()},[...n[3]||=[_(`span`,{class:`i-tabler-menu-2`},null,-1)]],8,at),n[6]||=_(`span`,{class:`rail-mark i-tabler-route`},null,-1),h(r,{class:p([`trail-node aml-entry`,{active:D(y).path.startsWith(`/aml`)}]),to:`/aml`,title:D(i)(`aml.k1amlx159`)},{default:T(()=>[n[4]||=_(`span`,{class:`node-icon i-tabler-flask`},null,-1),_(`span`,ot,d(D(i)(`aml.k1amlx158`)),1)]),_:1},8,[`class`,`title`]),_(`nav`,{class:`trail`,"aria-label":D(i)(`header.trail`)},[h(te,{name:`stamp`,tag:`div`,class:`trail-row`},{default:T(()=>[D(B)?(f(!0),v(b,{key:0},m(D(S).waypoints,(t,r)=>(f(),v(b,{key:t.path},[r>0?(f(),v(`span`,{key:`link-${t.path}`,class:`trail-link`,"aria-hidden":`true`})):x(``,!0),_(`div`,{class:p([`trail-node`,{active:t.path===D(y).path}]),role:`button`,tabindex:`0`,title:D(A)(t.path).title,onClick:e=>U(t.path),onKeydown:ee(k(e=>U(t.path),[`prevent`]),[`enter`])},[_(`span`,{class:p([`node-icon`,D(A)(t.path).icon])},null,2),_(`span`,lt,d(D(A)(t.path).title),1),_(`button`,{class:`node-close`,"aria-label":e.$t(`appHeader.kzwjv99001`,{p0:D(A)(t.path).title}),title:e.$t(`appHeader.kzwjv99001`,{p0:D(A)(t.path).title}),onClick:k(e=>he(t.path),[`stop`])},[...n[5]||=[_(`span`,{class:`i-tabler-x`},null,-1)]],8,ut)],42,ct)],64))),128)):x(``,!0)]),_:1})],8,st)]),_(`div`,dt,[h(c,null,{default:T(()=>[D(j).count>0?(f(),C(s,{key:0,placement:`bottomRight`},{overlay:T(()=>[_(`div`,mt,[_(`div`,ht,d(D(i)(`appHeader.hitlBadge`))+` · `+d(D(j).count),1),(f(!0),v(b,null,m(D(j).items,e=>(f(),v(`button`,{key:`${e.kind}:${e.id}`,class:`hitl-item`,onClick:t=>ce(e)},[_(`span`,_t,[_(`span`,vt,d(e.agentName),1),_(`span`,yt,d(F(e.kind)),1)]),_(`span`,bt,d(e.title),1),D(ae).has(e.kind)?(f(),v(`span`,{key:0,class:`hitl-item-actions`,onClick:n[1]||=k(()=>{},[`stop`])},[h(o,{size:`small`,type:`primary`,loading:D(I)===`${e.kind}:${e.id}`,onClick:t=>L(e,!0)},{default:T(()=>[O(d(D(i)(`appHeader.hitlApprove`)),1)]),_:1},8,[`loading`,`onClick`]),h(o,{size:`small`,danger:``,disabled:D(I)===`${e.kind}:${e.id}`,onClick:t=>L(e,!1)},{default:T(()=>[O(d(D(i)(`appHeader.hitlReject`)),1)]),_:1},8,[`disabled`,`onClick`])])):x(``,!0)],8,gt))),128))])]),default:T(()=>[_(`button`,{class:`icon-btn hitl-bell`,title:D(i)(`appHeader.hitlBadge`)},[n[7]||=_(`span`,{class:`i-tabler-bell-ringing`},null,-1),_(`span`,pt,d(D(j).count),1)],8,ft)]),_:1})):x(``,!0)]),_:1}),D(Ee)?(f(),v(`span`,{key:0,class:p([`ws-dot`,D(De)]),title:D(q)},null,10,xt)):x(``,!0),h(l,{title:D(i)(`header.fullscreen`)},{default:T(()=>[_(`button`,{class:`icon-btn`,onClick:ge},[_(`span`,{class:p([`i-tabler-arrows-maximize`,{hidden:D(W)}])},null,2),_(`span`,{class:p([`i-tabler-arrows-minimize`,{hidden:!D(W)}])},null,2)])]),_:1},8,[`title`]),h(g,{id:`hdr-locale`,value:D(a),size:`middle`,options:D(R),class:`lang-select`,onChange:z},null,8,[`value`,`options`]),h(l,{title:D(u).isDark?D(i)(`common.light`):D(i)(`common.dark`)},{default:T(()=>[_(`button`,{class:`icon-btn`,onClick:n[2]||=e=>D(u).toggleDark()},[_(`span`,{class:p([`i-tabler-sun-high`,{hidden:D(u).isDark}])},null,2),_(`span`,{class:p([`i-tabler-moon-stars`,{hidden:!D(u).isDark}])},null,2)])]),_:1},8,[`title`]),h(s,null,{overlay:T(()=>[h(M,{onClick:Y},{default:T(()=>[(f(!0),v(b,null,m(D(J),e=>(f(),v(b,{key:e.key},[e.divider?(f(),C(w,{key:0})):(f(),C(E,{key:e.key,danger:e.danger},{default:T(()=>[_(`span`,{class:p([e.icon,`mr-2`])},null,2),O(` `+d(e.label),1)]),_:2},1032,[`danger`]))],64))),128))]),_:1})]),default:T(()=>[_(`div`,St,[h(c,null,{fallback:T(()=>[...n[8]||=[_(`span`,{class:`user-initial aw-avatar`},`·`,-1),_(`span`,{class:`user-meta`},[_(`span`,{class:`user-name`},`·`),_(`span`,{class:`user-role`},`·`)],-1)]]),default:T(()=>[_(`span`,Ct,d(D(ve)),1),_(`span`,wt,[_(`span`,Tt,d(D(G)),1),_(`span`,Et,d(D(be)),1)])]),_:1})])]),_:1})]),_(`span`,{class:p([`plotter-line`,{run:D(V)}]),"aria-hidden":`true`},null,2)]),_:1})}}}),[[`__scopeId`,`data-v-1fa471c5`]]),{__name:`AppHeader`}),Ot={class:`footer-title aw-serif-accent`},kt=G(u({__name:`default`,setup(e){let{token:t}=ve.useToken(),n=ae();return(e,r)=>{let i=tt,o=Dt,c=Be,l=Re,u=Ve;return f(),C(u,{class:`app-layout`,"has-sider":``},{default:T(()=>[h(i),h(u,{class:`app-main`,style:s({background:`transparent`,"--app-bg-container":D(t)?.colorBgContainer,"--app-bg-layout":D(t)?.colorBgLayout,"--app-text":D(t)?.colorText,"--app-text-secondary":D(t)?.colorTextSecondary,"--app-border":D(t)?.colorBorderSecondary,"--app-fill":D(t)?.colorFillQuaternary,"--app-header-h":`56px`,"--app-footer-h":`46px`})},{default:T(()=>[h(o),h(c,{class:`app-content`},{default:T(()=>[a(e.$slots,`default`,{},void 0,!0)]),_:3}),h(l,{class:`app-footer`,style:s({color:D(t)?.colorTextSecondary})},{default:T(()=>[_(`span`,Ot,d(D(n).title),1),r[0]||=_(`span`,{class:`sep`},`/`,-1),_(`span`,null,`v`+d(D(n).version),1),r[1]||=_(`span`,{class:`sep`},`/`,-1),_(`span`,null,`mode=`+d(D(n).mode),1)]),_:1},8,[`style`])]),_:3},8,[`style`]),r[2]||=_(`span`,{class:`aw-noise`,"aria-hidden":`true`},null,-1)]),_:3})}}}),[[`__scopeId`,`data-v-b48dd792`]]);export{kt as default};
@@ -1 +0,0 @@
1
- {"id":"44b66249-54d0-4c8a-9057-9755a32329e2","timestamp":1789189585393,"prerendered":[]}
@@ -1 +0,0 @@
1
- .app-sider[data-v-095cc5a0]{background:var(--mat-chrome-bg);height:100vh;-webkit-backdrop-filter:var(--vibrancy-chrome);backdrop-filter:var(--vibrancy-chrome);border-right:1px solid var(--glass-line);box-shadow:var(--glass-specular);z-index:20;position:sticky;top:0;overflow:hidden auto}.app-sider[data-v-095cc5a0]::-webkit-scrollbar{width:0}.logo[data-v-095cc5a0]{border-bottom:1px solid var(--line);align-items:center;gap:12px;height:58px;padding:0 16px;display:flex;overflow:hidden}.logo-mark[data-v-095cc5a0]{filter:drop-shadow(0 0 6px #35e0a042);flex:none;justify-content:center;align-items:center;width:32px;height:36px;display:flex}.logo-text[data-v-095cc5a0]{white-space:nowrap;flex-direction:column;line-height:1.25;display:flex}.logo-title[data-v-095cc5a0]{font-family:var(--font-mono);letter-spacing:.06em;text-transform:uppercase;color:var(--ink);font-size:13px;font-weight:600}.logo-sub[data-v-095cc5a0]{font-family:var(--font-mono);letter-spacing:.2em;text-transform:uppercase;color:var(--sider-ink-faint);font-size:8.5px}.app-menu[data-v-095cc5a0]{flex-direction:column;gap:14px;padding:16px 10px 0;display:flex}.menu-group[data-v-095cc5a0]{flex-direction:column;gap:1px;display:flex}.menu-group-label[data-v-095cc5a0]{align-items:center;gap:8px;height:22px;margin-bottom:3px;padding:0 10px;display:flex}.menu-group-text[data-v-095cc5a0]{font-family:var(--font-mono);letter-spacing:.16em;text-transform:uppercase;color:var(--sider-ink-faint);flex:none;font-size:9.5px;font-weight:600}.menu-group-rule[data-v-095cc5a0]{background:linear-gradient(90deg, var(--divider-hair), transparent);flex:auto;height:1px}.menu-group-sep[data-v-095cc5a0]{background:var(--line-strong);width:18px;height:1px;margin:6px 0 6px 13px}.menu-item[data-v-095cc5a0]{width:100%;min-height:34px;font-family:var(--font-body);color:var(--sider-ink);text-align:left;cursor:pointer;border-radius:var(--radius-panel-sm);transition:background-color var(--transition-fast), color var(--transition-fast), padding-left var(--transition-fast), transform var(--transition-fast);background:0 0;border:0;align-items:center;padding:0 10px;font-size:13px;font-weight:500;display:flex;position:relative}@media (hover:hover) and (pointer:fine){.menu-item[data-v-095cc5a0]:hover{color:var(--ink);background:var(--paper-deep);padding-left:13px}}.menu-item[data-v-095cc5a0]:active{transform:scale(.985)}.menu-item.active[data-v-095cc5a0]{color:var(--accent-strong);background:var(--accent-soft);box-shadow:inset 2px 0 0 var(--accent);font-weight:600}.menu-item.active[data-v-095cc5a0]:hover{background:var(--tone-success-bg);padding-left:10px}.menu-icon[data-v-095cc5a0]{flex:0 0 20px;font-size:16px}.menu-item .menu-icon[data-v-095cc5a0]{color:var(--sider-ink-faint);transition:color var(--transition-fast)}.menu-item:hover .menu-icon[data-v-095cc5a0]{color:var(--ink)}.menu-item.active .menu-icon[data-v-095cc5a0]{color:var(--accent-strong)}.menu-label[data-v-095cc5a0]{white-space:nowrap;text-overflow:ellipsis;transition:opacity var(--transition-fast);padding-left:10px;overflow:hidden}.sider-footer[data-v-095cc5a0]{background:linear-gradient(180deg, transparent, var(--frost-bg) 42%);width:100%;padding:0 18px 18px;position:absolute;bottom:0;left:0}.footer-rule[data-v-095cc5a0]{background:repeating-linear-gradient(90deg, var(--tick-c-fine) 0 1px, transparent 1px 9px), var(--divider-hair);opacity:.9;height:1px;margin-bottom:12px}.footer-line[data-v-095cc5a0]{font-family:var(--font-mono);font-variant-numeric:tabular-nums;letter-spacing:.06em;color:var(--sider-ink-faint);align-items:center;gap:8px;font-size:10px;display:flex}.footer-line .sep[data-v-095cc5a0]{color:var(--ink-faint)}.slide-fade-enter-active[data-v-095cc5a0],.slide-fade-leave-active[data-v-095cc5a0]{transition:opacity .2s cubic-bezier(.22,1,.36,1),transform .2s cubic-bezier(.22,1,.36,1)}.slide-fade-enter-from[data-v-095cc5a0],.slide-fade-leave-to[data-v-095cc5a0]{opacity:0;transform:translate(-8px)}.app-header[data-v-1fa471c5]{height:var(--app-header-h,56px);border-bottom:1px solid var(--divider-hair);justify-content:space-between;align-items:center;padding:0 20px 0 10px;transition:background .3s,border-color .3s;display:flex;position:relative;overflow:hidden}.app-header-glass[data-v-1fa471c5]{z-index:30;background:var(--mat-chrome-bg);-webkit-backdrop-filter:var(--vibrancy-chrome);backdrop-filter:var(--vibrancy-chrome);border-bottom:1px solid var(--glass-line);box-shadow:var(--glass-specular);position:sticky;top:0}.header-left[data-v-1fa471c5],.header-right[data-v-1fa471c5]{align-items:center;gap:10px;min-width:0;display:flex}.rail-mark[data-v-1fa471c5]{color:var(--ink-faint);flex:none;font-size:15px}.aml-entry[data-v-1fa471c5]{flex:none;margin-right:2px}.trail[data-v-1fa471c5]{scrollbar-width:none;flex:0 auto;min-width:0;overflow:auto hidden;-webkit-mask-image:linear-gradient(90deg,#0000 0,#000 8px calc(100% - 18px),#0000);mask-image:linear-gradient(90deg,#0000 0,#000 8px calc(100% - 18px),#0000)}.trail[data-v-1fa471c5]::-webkit-scrollbar{display:none}.trail-row[data-v-1fa471c5]{white-space:nowrap;align-items:center;gap:0;padding:3px 4px;display:flex}.trail-link[data-v-1fa471c5]{background:var(--line-strong);flex:none;width:14px;height:1px}.trail-node[data-v-1fa471c5]{height:32px;font-family:var(--font-body);letter-spacing:.01em;color:var(--app-text,var(--ink-soft));cursor:pointer;background:var(--paper-deep);border:1px solid var(--line);border-radius:var(--radius-pill);transition:background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);flex:none;align-items:center;gap:6px;padding:0 12px;font-size:12px;font-weight:500;display:inline-flex;position:relative}.trail-node[data-v-1fa471c5]:hover{color:var(--ink);background:var(--paper-tint);border-color:var(--line-strong)}.trail-node[data-v-1fa471c5]:active{transform:scale(.98)}.trail-node[data-v-1fa471c5]:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.trail-node.active[data-v-1fa471c5]{color:var(--on-accent);background:var(--accent);border-color:var(--accent)}.node-icon[data-v-1fa471c5]{font-size:13px}.node-title[data-v-1fa471c5]{letter-spacing:.02em;text-overflow:ellipsis;white-space:nowrap;max-width:132px;transition:max-width .18s var(--ease-out-quart), margin .18s var(--ease-out-quart), opacity .18s var(--ease-out-quart);overflow:hidden}.node-close[data-v-1fa471c5]{width:16px;height:16px;color:var(--ink-faint);cursor:pointer;border-radius:var(--radius-chip);opacity:0;transition:opacity .18s var(--ease-out-quart), transform .18s var(--ease-out-quart), color var(--transition-fast), background var(--transition-fast);background:0 0;border:none;flex:none;justify-content:center;align-items:center;margin-left:4px;font-size:12px;display:inline-flex;transform:scale(.6)}.trail-node:focus-within .node-close[data-v-1fa471c5],.trail-node:hover .node-close[data-v-1fa471c5]{opacity:1;transform:none}.node-close[data-v-1fa471c5]:hover{color:var(--ink);background:var(--hover-tint)}.node-close[data-v-1fa471c5]:active{transform:scale(.9)}.trail-node.active .node-close[data-v-1fa471c5]{color:color-mix(in srgb, var(--on-accent) 85%, transparent)}.trail-node.active .node-close[data-v-1fa471c5]:hover{color:var(--on-accent);background:color-mix(in srgb, var(--on-accent) 18%, transparent)}.node-close[data-v-1fa471c5]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.stamp-enter-active[data-v-1fa471c5]{transition:opacity .22s var(--ease-out-quart), transform .22s var(--ease-out-quart)}.stamp-enter-from[data-v-1fa471c5]{opacity:0;transform:translateY(4px)}.stamp-leave-active[data-v-1fa471c5]{transition:opacity .14s;position:absolute}.stamp-leave-to[data-v-1fa471c5]{opacity:0}.stamp-move[data-v-1fa471c5]{transition:transform .22s}.plotter-line[data-v-1fa471c5]{background:linear-gradient(90deg, transparent, var(--ink-fainter) 30%, var(--accent));opacity:0;pointer-events:none;transform-origin:0;width:100%;height:2px;position:absolute;bottom:-1px;left:0;transform:scaleX(0)}.plotter-line.run[data-v-1fa471c5]{animation:.6s cubic-bezier(.3,.8,.4,1) forwards plot-sweep-1fa471c5}@keyframes plot-sweep-1fa471c5{0%{opacity:.9;transform:scaleX(0)}70%{opacity:.9}to{opacity:0;transform:scaleX(1)}}.collapse-btn[data-v-1fa471c5],.icon-btn[data-v-1fa471c5]{width:34px;height:34px;color:var(--app-text,var(--ink-soft));cursor:pointer;border-radius:var(--radius-panel-sm);transition:background var(--transition-fast), color var(--transition-fast), transform .16s cubic-bezier(.22, 1, .36, 1);background:0 0;border:0;flex:none;justify-content:center;align-items:center;font-size:16px;display:inline-flex}.collapse-btn[data-v-1fa471c5]:hover,.icon-btn[data-v-1fa471c5]:hover{color:var(--ink);background:var(--paper-deep)}.collapse-btn[data-v-1fa471c5]:active,.icon-btn[data-v-1fa471c5]:active{transform:scale(.94)}.lang-select[data-v-1fa471c5]{width:118px}.lang-select[data-v-1fa471c5] .ant-select-selector{color:var(--app-text,var(--ink));border-color:var(--app-border,var(--line))!important;background:0 0!important}.lang-select[data-v-1fa471c5] .ant-select-arrow{color:var(--app-text-secondary,var(--ink-faint))}.user-chip[data-v-1fa471c5]{cursor:pointer;border-radius:var(--radius-pill);border:1px solid #0000;align-items:center;gap:10px;height:42px;margin-left:4px;padding:0 12px 0 8px;transition:border-color .16s,background .16s;display:flex}.user-chip[data-v-1fa471c5]:hover{border-color:var(--line);background:var(--hover-tint)}.user-initial[data-v-1fa471c5]{width:28px;height:28px;font-size:12px}.user-meta[data-v-1fa471c5]{flex-direction:column;line-height:1.15;display:flex}.user-name[data-v-1fa471c5]{color:var(--app-text,var(--ink));letter-spacing:.01em;font-size:12.5px;font-weight:600}.user-role[data-v-1fa471c5]{font-family:var(--font-mono);letter-spacing:.18em;text-transform:uppercase;color:var(--app-text-secondary,var(--ink-faint));font-size:8.5px}.hidden[data-v-1fa471c5]{display:none}.ws-dot[data-v-1fa471c5]{background:var(--tone-success-dot);width:8px;height:8px;box-shadow:0 0 6px color-mix(in srgb, var(--tone-success-dot) 55%, transparent);border-radius:50%;flex:none;margin:0 2px}.ws-dot.syncing[data-v-1fa471c5]{background:var(--tone-warning-dot);box-shadow:0 0 6px color-mix(in srgb, var(--tone-warning-dot) 55%, transparent);animation:1.2s ease-in-out infinite ws-pulse-1fa471c5}.ws-dot.down[data-v-1fa471c5]{background:var(--tone-danger-dot);box-shadow:0 0 6px color-mix(in srgb, var(--tone-danger-dot) 55%, transparent)}@keyframes ws-pulse-1fa471c5{50%{opacity:.45}}.hitl-bell[data-v-1fa471c5]{color:var(--tone-warning-dot,#d4a017);position:relative}.hitl-count[data-v-1fa471c5]{min-width:15px;height:15px;font-family:var(--font-mono);color:var(--on-accent,#fff);text-align:center;background:var(--tone-danger-dot,#e05252);border-radius:8px;padding:0 4px;font-size:9.5px;font-weight:700;line-height:15px;position:absolute;top:2px;right:2px}.hitl-menu[data-v-1fa471c5]{background:var(--paper,#fff);border:1px solid var(--line);border-radius:var(--radius-panel-sm,10px);min-width:260px;max-width:340px;padding:6px;box-shadow:0 10px 32px #00000024}.hitl-menu-title[data-v-1fa471c5]{letter-spacing:.08em;color:var(--ink-faint);border-bottom:1px solid var(--line);padding:6px 10px;font-size:11px;font-weight:600}.hitl-item[data-v-1fa471c5]{text-align:left;cursor:pointer;border-radius:var(--radius-panel-sm,8px);width:100%;transition:background var(--transition-fast), border-color var(--transition-fast);background:0 0;border:1px solid #0000;flex-direction:column;gap:3px;margin-top:4px;padding:8px 10px;display:flex}.hitl-item[data-v-1fa471c5]:hover{background:var(--paper-deep);border-color:var(--line)}.hitl-item-top[data-v-1fa471c5]{justify-content:space-between;align-items:baseline;gap:8px;display:flex}.hitl-item-agent[data-v-1fa471c5]{color:var(--ink);font-size:12.5px;font-weight:600}.hitl-item-kind[data-v-1fa471c5]{font-family:var(--font-mono);letter-spacing:.1em;color:var(--tone-warning-dot,#d4a017);font-size:9.5px}.hitl-item-title[data-v-1fa471c5]{color:var(--ink-soft);text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.hitl-item-actions[data-v-1fa471c5]{gap:6px;margin-top:6px;display:flex}@media (prefers-reduced-motion:reduce){.plotter-line.run[data-v-1fa471c5]{opacity:0;animation:none}.stamp-enter-active[data-v-1fa471c5],.stamp-leave-active[data-v-1fa471c5],.stamp-move[data-v-1fa471c5]{transition:none}}.app-layout[data-v-b48dd792]{background:0 0;flex-direction:row;min-height:100vh;display:flex}.app-main[data-v-b48dd792]{flex-direction:column;flex:auto;min-width:0;min-height:100vh;display:flex;position:relative}.app-content[data-v-b48dd792]{flex:1;margin:12px 16px 0;padding:2px}.app-footer[data-v-b48dd792]{background:var(--mat-chrome-bg);-webkit-backdrop-filter:var(--vibrancy-chrome);backdrop-filter:var(--vibrancy-chrome);border-top:1px solid var(--glass-line);box-shadow:var(--glass-specular);height:var(--app-footer-h);font-family:var(--font-mono);letter-spacing:.08em;text-align:center;justify-content:center;align-items:center;gap:10px;padding:0 50px;font-size:10.5px;display:flex}.app-footer .sep[data-v-b48dd792]{color:var(--ink-faint)}.footer-title[data-v-b48dd792]{font-size:13px}