dsh-agora-plugin 0.6.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/README.md +520 -0
- package/client/index.js +492 -0
- package/cordis.patch.yml +14 -0
- package/dsh.plugin.json +7 -0
- package/lib/agora-client.d.ts +73 -0
- package/lib/agora-client.d.ts.map +1 -0
- package/lib/agora-client.js +207 -0
- package/lib/agora-client.js.map +1 -0
- package/lib/client.js +492 -0
- package/lib/command-adapter.d.ts +44 -0
- package/lib/command-adapter.d.ts.map +1 -0
- package/lib/command-adapter.js +70 -0
- package/lib/command-adapter.js.map +1 -0
- package/lib/command.d.ts +53 -0
- package/lib/command.d.ts.map +1 -0
- package/lib/command.js +375 -0
- package/lib/command.js.map +1 -0
- package/lib/context-types.d.ts +51 -0
- package/lib/context-types.d.ts.map +1 -0
- package/lib/context-types.js +2 -0
- package/lib/context-types.js.map +1 -0
- package/lib/contracts.d.ts +402 -0
- package/lib/contracts.d.ts.map +1 -0
- package/lib/contracts.js +2 -0
- package/lib/contracts.js.map +1 -0
- package/lib/extension-sdk.d.ts +74 -0
- package/lib/extension-sdk.d.ts.map +1 -0
- package/lib/extension-sdk.js +160 -0
- package/lib/extension-sdk.js.map +1 -0
- package/lib/harness-runtime.d.ts +29 -0
- package/lib/harness-runtime.d.ts.map +1 -0
- package/lib/harness-runtime.js +422 -0
- package/lib/harness-runtime.js.map +1 -0
- package/lib/http-api.d.ts +10 -0
- package/lib/http-api.d.ts.map +1 -0
- package/lib/http-api.js +282 -0
- package/lib/http-api.js.map +1 -0
- package/lib/im-bridge-v1.d.ts +37 -0
- package/lib/im-bridge-v1.d.ts.map +1 -0
- package/lib/im-bridge-v1.js +43 -0
- package/lib/im-bridge-v1.js.map +1 -0
- package/lib/im-gateway.d.ts +24 -0
- package/lib/im-gateway.d.ts.map +1 -0
- package/lib/im-gateway.js +53 -0
- package/lib/im-gateway.js.map +1 -0
- package/lib/index.d.ts +46 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +210 -0
- package/lib/index.js.map +1 -0
- package/lib/node-worker.d.ts +52 -0
- package/lib/node-worker.d.ts.map +1 -0
- package/lib/node-worker.js +372 -0
- package/lib/node-worker.js.map +1 -0
- package/lib/service.d.ts +51 -0
- package/lib/service.d.ts.map +1 -0
- package/lib/service.js +196 -0
- package/lib/service.js.map +1 -0
- package/lib/tool.d.ts +72 -0
- package/lib/tool.d.ts.map +1 -0
- package/lib/tool.js +206 -0
- package/lib/tool.js.map +1 -0
- package/package.json +74 -0
- package/patches/dsh-im/@xmanrui__dsh-im@2.1.0.patch +920 -0
- package/patches/dsh-im/@xmanrui__dsh-im@2.3.0.patch +984 -0
- package/patches/dsh-im/README.md +82 -0
- package/src/agora-client.ts +333 -0
- package/src/command-adapter.ts +106 -0
- package/src/command.ts +424 -0
- package/src/context-types.ts +54 -0
- package/src/contracts.ts +413 -0
- package/src/extension-sdk.ts +225 -0
- package/src/harness-runtime.ts +518 -0
- package/src/http-api.ts +269 -0
- package/src/im-bridge-v1.ts +73 -0
- package/src/im-gateway.ts +82 -0
- package/src/index.ts +260 -0
- package/src/node-worker.ts +442 -0
- package/src/service.ts +262 -0
- package/src/tool.ts +269 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({ id: 'dsh-agora', factory: (require) => {
|
|
2
|
+
'use strict'
|
|
3
|
+
var module = { exports: {} }
|
|
4
|
+
var exports = module.exports
|
|
5
|
+
const React = require('react')
|
|
6
|
+
|
|
7
|
+
const API_PREFIX = '/dsh-agora/api/'
|
|
8
|
+
const POLL_MS = 5000
|
|
9
|
+
const TAB_ID = 'dsh-agora:dashboard'
|
|
10
|
+
const FIRST_OPEN_KEY = 'dsh-agora:dashboard:auto-opened:v1'
|
|
11
|
+
|
|
12
|
+
async function rpc(method, payload) {
|
|
13
|
+
const response = await fetch(API_PREFIX + method, {
|
|
14
|
+
method: 'POST',
|
|
15
|
+
headers: { 'Content-Type': 'application/json' },
|
|
16
|
+
body: JSON.stringify(payload || {}),
|
|
17
|
+
})
|
|
18
|
+
let body
|
|
19
|
+
try { body = await response.json() } catch (error) {
|
|
20
|
+
throw new Error('Agora API returned invalid JSON (HTTP ' + response.status + ')')
|
|
21
|
+
}
|
|
22
|
+
if (!response.ok || body?.ok !== true) {
|
|
23
|
+
throw new Error(body?.error?.message || ('Agora API failed (HTTP ' + response.status + ')'))
|
|
24
|
+
}
|
|
25
|
+
return body.value
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function AgoraIcon(props) {
|
|
29
|
+
const size = props?.size || 16
|
|
30
|
+
return React.createElement('svg', {
|
|
31
|
+
width: size, height: size, viewBox: '0 0 16 16', fill: 'none',
|
|
32
|
+
stroke: 'currentColor', strokeWidth: 1.35, strokeLinecap: 'round',
|
|
33
|
+
strokeLinejoin: 'round', 'aria-hidden': true,
|
|
34
|
+
},
|
|
35
|
+
React.createElement('circle', { cx: 8, cy: 8, r: 2.1 }),
|
|
36
|
+
React.createElement('circle', { cx: 3, cy: 4, r: 1.5 }),
|
|
37
|
+
React.createElement('circle', { cx: 13, cy: 4, r: 1.5 }),
|
|
38
|
+
React.createElement('circle', { cx: 3, cy: 12, r: 1.5 }),
|
|
39
|
+
React.createElement('circle', { cx: 13, cy: 12, r: 1.5 }),
|
|
40
|
+
React.createElement('path', { d: 'M4.3 4.8 6.2 6.4M11.7 4.8 9.8 6.4M4.3 11.2 6.2 9.6M11.7 11.2 9.8 9.6' }))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function openAgora(sidebar) {
|
|
44
|
+
if (!sidebar || typeof sidebar.openTab !== 'function') return
|
|
45
|
+
// A content seed makes better-sidebar expand whichever panel owns the tab.
|
|
46
|
+
sidebar.openTab({ type: TAB_ID, path: 'dsh-agora://dashboard' })
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function shouldAutoOpen() {
|
|
50
|
+
if (typeof window === 'undefined' || !window.sessionStorage) return false
|
|
51
|
+
try {
|
|
52
|
+
if (window.sessionStorage.getItem(FIRST_OPEN_KEY) === '1') return false
|
|
53
|
+
window.sessionStorage.setItem(FIRST_OPEN_KEY, '1')
|
|
54
|
+
return true
|
|
55
|
+
} catch (error) {
|
|
56
|
+
return false
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function installLauncher(sidebar) {
|
|
61
|
+
if (typeof document === 'undefined' || !document.body) return function () {}
|
|
62
|
+
const old = document.querySelector('[data-dsh-agora-launcher]')
|
|
63
|
+
if (old) old.remove()
|
|
64
|
+
const button = document.createElement('button')
|
|
65
|
+
button.type = 'button'
|
|
66
|
+
button.className = 'da-launcher'
|
|
67
|
+
button.setAttribute('data-dsh-agora-launcher', '')
|
|
68
|
+
button.setAttribute('aria-label', '打开 Agora 协同')
|
|
69
|
+
button.setAttribute('title', '打开 Agora 协同')
|
|
70
|
+
const icon = document.createElement('span')
|
|
71
|
+
icon.className = 'da-launcher-icon'
|
|
72
|
+
icon.setAttribute('aria-hidden', 'true')
|
|
73
|
+
icon.textContent = '⬡'
|
|
74
|
+
const label = document.createElement('span')
|
|
75
|
+
label.className = 'da-launcher-label'
|
|
76
|
+
label.textContent = 'Agora'
|
|
77
|
+
button.append(icon, label)
|
|
78
|
+
const onClick = function () { openAgora(sidebar) }
|
|
79
|
+
button.addEventListener('click', onClick)
|
|
80
|
+
document.body.appendChild(button)
|
|
81
|
+
return function () {
|
|
82
|
+
button.removeEventListener('click', onClick)
|
|
83
|
+
button.remove()
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function text(value, fallback) {
|
|
88
|
+
return typeof value === 'string' && value.trim() ? value : (fallback || '—')
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function list(value) {
|
|
92
|
+
return Array.isArray(value) ? value : []
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function shortTime(value) {
|
|
96
|
+
if (!value) return '—'
|
|
97
|
+
const date = new Date(value)
|
|
98
|
+
return Number.isNaN(date.getTime()) ? String(value) : date.toLocaleString()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function statusClass(value) {
|
|
102
|
+
const normalized = String(value || '').toLowerCase()
|
|
103
|
+
if (['ok', 'online', 'connected', 'completed', 'enabled'].includes(normalized)) return ' da-ok'
|
|
104
|
+
if (['failed', 'offline', 'error', 'cancelled', 'incompatible'].includes(normalized)) return ' da-bad'
|
|
105
|
+
return ' da-warn'
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function Badge(props) {
|
|
109
|
+
return React.createElement('span', { className: 'da-badge' + statusClass(props.value) }, text(props.value))
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function Empty(props) {
|
|
113
|
+
return React.createElement('div', { className: 'da-empty' }, props.children)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function SectionTitle(props) {
|
|
117
|
+
return React.createElement('div', { className: 'da-section-title' },
|
|
118
|
+
React.createElement('strong', null, props.title),
|
|
119
|
+
props.note ? React.createElement('span', null, props.note) : null)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function Overview(props) {
|
|
123
|
+
const snapshot = props.snapshot || {}
|
|
124
|
+
const nodes = props.nodes
|
|
125
|
+
const agents = props.agents
|
|
126
|
+
const bots = nodes.flatMap(function (node) { return list(node.bots) })
|
|
127
|
+
const active = nodes.reduce(function (sum, node) { return sum + Number(node.capacity?.active || 0) }, 0)
|
|
128
|
+
const capacity = nodes.reduce(function (sum, node) { return sum + Number(node.capacity?.max_concurrent || 0) }, 0)
|
|
129
|
+
return React.createElement('div', { className: 'da-scroll' },
|
|
130
|
+
React.createElement('div', { className: 'da-metrics' },
|
|
131
|
+
React.createElement('div', { className: 'da-metric' }, React.createElement('b', null, nodes.length), React.createElement('span', null, '节点')),
|
|
132
|
+
React.createElement('div', { className: 'da-metric' }, React.createElement('b', null, agents.length), React.createElement('span', null, 'Agent')),
|
|
133
|
+
React.createElement('div', { className: 'da-metric' }, React.createElement('b', null, bots.filter(function (bot) { return bot.connected }).length), React.createElement('span', null, '在线 Bot')),
|
|
134
|
+
React.createElement('div', { className: 'da-metric' }, React.createElement('b', null, active + '/' + capacity), React.createElement('span', null, '执行槽位'))),
|
|
135
|
+
React.createElement('div', { className: 'da-card' },
|
|
136
|
+
SectionTitle({ title: '连接状态', note: text(snapshot.serverUrl) }),
|
|
137
|
+
React.createElement('div', { className: 'da-kv' },
|
|
138
|
+
React.createElement('span', null, '本机节点'), Badge({ value: snapshot.node?.state || 'unknown' }),
|
|
139
|
+
React.createElement('span', null, 'dsh-im 桥'), Badge({ value: snapshot.imBridge?.state || 'unavailable' }),
|
|
140
|
+
React.createElement('span', null, '最近心跳'), React.createElement('code', null, shortTime(snapshot.node?.lastHeartbeatAt)),
|
|
141
|
+
React.createElement('span', null, '扩展协议'), React.createElement('code', null, list(snapshot.extensions).map(function (item) { return item.id }).join(', ') || '—'))),
|
|
142
|
+
React.createElement('div', { className: 'da-card' },
|
|
143
|
+
SectionTitle({ title: '通信方式', note: 'Discord 只负责呈现,Agent 通过持久派发队列协作' }),
|
|
144
|
+
React.createElement('div', { className: 'da-flow' },
|
|
145
|
+
React.createElement('span', null, 'IM / DSH Session'), React.createElement('i', null, '→'),
|
|
146
|
+
React.createElement('span', null, 'Agora Queue'), React.createElement('i', null, '→'),
|
|
147
|
+
React.createElement('span', null, '目标 Agent'))))
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function Nodes(props) {
|
|
151
|
+
if (!props.nodes.length) return Empty({ children: '尚未发现运行时节点。' })
|
|
152
|
+
return React.createElement('div', { className: 'da-scroll da-stack' }, props.nodes.map(function (node) {
|
|
153
|
+
return React.createElement('article', { className: 'da-card', key: node.node_id },
|
|
154
|
+
React.createElement('div', { className: 'da-card-head' },
|
|
155
|
+
React.createElement('div', null, React.createElement('strong', null, text(node.node_id)), React.createElement('small', null, text(node.host_framework))),
|
|
156
|
+
Badge({ value: node.presence })),
|
|
157
|
+
React.createElement('div', { className: 'da-inline-meta' },
|
|
158
|
+
React.createElement('span', null, '版本 ', text(node.plugin_version)),
|
|
159
|
+
React.createElement('span', null, '容量 ', Number(node.capacity?.active || 0) + '/' + Number(node.capacity?.max_concurrent || 0)),
|
|
160
|
+
React.createElement('span', null, '心跳 ', shortTime(node.last_seen_at))),
|
|
161
|
+
React.createElement('div', { className: 'da-subtitle' }, 'Agents'),
|
|
162
|
+
list(node.agents).map(function (agent) {
|
|
163
|
+
return React.createElement('div', { className: 'da-row', key: agent.agent_ref },
|
|
164
|
+
React.createElement('span', null, React.createElement('b', null, text(agent.display_name, agent.agent_ref)), React.createElement('code', null, agent.agent_ref)),
|
|
165
|
+
React.createElement('small', null, list(agent.capabilities).join(' · ') || '无能力标签'))
|
|
166
|
+
}),
|
|
167
|
+
React.createElement('div', { className: 'da-subtitle' }, 'Bots'),
|
|
168
|
+
list(node.bots).length ? list(node.bots).map(function (bot) {
|
|
169
|
+
return React.createElement('div', { className: 'da-row', key: bot.provider + ':' + bot.bot_ref },
|
|
170
|
+
React.createElement('span', null, React.createElement('b', null, text(bot.display_name, bot.bot_ref)), React.createElement('code', null, bot.provider)),
|
|
171
|
+
Badge({ value: bot.connected ? 'connected' : 'offline' }))
|
|
172
|
+
}) : React.createElement('small', { className: 'da-muted' }, '此节点没有 IM Bot'))
|
|
173
|
+
}))
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function Tasks(props) {
|
|
177
|
+
const [title, setTitle] = React.useState('')
|
|
178
|
+
const [description, setDescription] = React.useState('')
|
|
179
|
+
const [saving, setSaving] = React.useState(false)
|
|
180
|
+
async function createTask(event) {
|
|
181
|
+
event.preventDefault()
|
|
182
|
+
if (!title.trim() || saving) return
|
|
183
|
+
setSaving(true)
|
|
184
|
+
try {
|
|
185
|
+
await rpc('create', { title: title.trim(), description: description.trim() || undefined, creator: 'dsh-ui' })
|
|
186
|
+
setTitle(''); setDescription(''); await props.reload()
|
|
187
|
+
} catch (error) { props.onError(error) } finally { setSaving(false) }
|
|
188
|
+
}
|
|
189
|
+
return React.createElement('div', { className: 'da-scroll da-stack' },
|
|
190
|
+
React.createElement('form', { className: 'da-card da-form', onSubmit: createTask },
|
|
191
|
+
SectionTitle({ title: '创建协作任务', note: '任务持久保存在中央 Agora' }),
|
|
192
|
+
React.createElement('input', { value: title, placeholder: '任务标题', onChange: function (event) { setTitle(event.target.value) } }),
|
|
193
|
+
React.createElement('textarea', { value: description, rows: 3, placeholder: '目标、约束或验收标准(可选)', onChange: function (event) { setDescription(event.target.value) } }),
|
|
194
|
+
React.createElement('button', { className: 'da-primary', type: 'submit', disabled: saving || !title.trim() }, saving ? '创建中…' : '创建任务')),
|
|
195
|
+
props.tasks.length ? props.tasks.map(function (task) {
|
|
196
|
+
const id = task.id || task.task_id
|
|
197
|
+
return React.createElement('button', { type: 'button', className: 'da-card da-task', key: id, onClick: function () { props.onSelect(task) } },
|
|
198
|
+
React.createElement('div', { className: 'da-card-head' }, React.createElement('strong', null, text(task.title, id)), Badge({ value: task.state || task.status || 'unknown' })),
|
|
199
|
+
task.description ? React.createElement('p', null, task.description) : null,
|
|
200
|
+
React.createElement('small', null, text(id) + ' · ' + shortTime(task.updated_at || task.created_at)))
|
|
201
|
+
}) : Empty({ children: '暂无任务。创建第一个跨 Agent 协作任务吧。' }))
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function Agents(props) {
|
|
205
|
+
const [target, setTarget] = React.useState(props.agents[0]?.runtime_target_ref || '')
|
|
206
|
+
const [sessionId, setSessionId] = React.useState('')
|
|
207
|
+
const [prompt, setPrompt] = React.useState('')
|
|
208
|
+
const [presentation, setPresentation] = React.useState('silent')
|
|
209
|
+
const [sending, setSending] = React.useState(false)
|
|
210
|
+
React.useEffect(function () {
|
|
211
|
+
if (!target && props.agents[0]?.runtime_target_ref) setTarget(props.agents[0].runtime_target_ref)
|
|
212
|
+
}, [props.agents, target])
|
|
213
|
+
async function dispatch(event) {
|
|
214
|
+
event.preventDefault()
|
|
215
|
+
if (!target || !prompt.trim() || sending) return
|
|
216
|
+
setSending(true)
|
|
217
|
+
try {
|
|
218
|
+
const value = await rpc('dispatch', {
|
|
219
|
+
runtimeTargetRef: target,
|
|
220
|
+
prompt: prompt.trim(),
|
|
221
|
+
idempotencyKey: 'dsh-agora-ui-' + Date.now() + '-' + Math.random().toString(36).slice(2),
|
|
222
|
+
sessionId: sessionId.trim() || undefined,
|
|
223
|
+
presentationMode: presentation,
|
|
224
|
+
waitTimeoutMs: 0,
|
|
225
|
+
})
|
|
226
|
+
props.onDispatch(value)
|
|
227
|
+
if (value?.session_id) setSessionId(value.session_id)
|
|
228
|
+
setPrompt('')
|
|
229
|
+
} catch (error) { props.onError(error) } finally { setSending(false) }
|
|
230
|
+
}
|
|
231
|
+
return React.createElement('div', { className: 'da-scroll da-stack' },
|
|
232
|
+
React.createElement('form', { className: 'da-card da-form', onSubmit: dispatch },
|
|
233
|
+
SectionTitle({ title: '派发 Agent', note: '默认静默执行;主动回帖需明确选择' }),
|
|
234
|
+
React.createElement('label', null, '目标 Agent', React.createElement('select', { value: target, onChange: function (event) { setTarget(event.target.value) } },
|
|
235
|
+
props.agents.map(function (agent) { return React.createElement('option', { value: agent.runtime_target_ref, key: agent.runtime_target_ref }, text(agent.display_name, agent.runtime_target_ref)) }))),
|
|
236
|
+
React.createElement('label', null, '续接 Session(可选)', React.createElement('input', { value: sessionId, placeholder: 'session-…', onChange: function (event) { setSessionId(event.target.value) } })),
|
|
237
|
+
React.createElement('label', null, '结果呈现', React.createElement('select', { value: presentation, onChange: function (event) { setPresentation(event.target.value) } },
|
|
238
|
+
React.createElement('option', { value: 'silent' }, '静默,仅保存在 Agora'),
|
|
239
|
+
React.createElement('option', { value: 'destination_bot' }, '由目标 Bot 回帖'))),
|
|
240
|
+
React.createElement('textarea', { value: prompt, rows: 5, placeholder: '输入要交给目标 Agent 的任务…', onChange: function (event) { setPrompt(event.target.value) } }),
|
|
241
|
+
React.createElement('button', { className: 'da-primary', type: 'submit', disabled: sending || !target || !prompt.trim() }, sending ? '派发中…' : '立即派发')),
|
|
242
|
+
React.createElement('div', { className: 'da-card' },
|
|
243
|
+
SectionTitle({ title: '可用目标', note: props.agents.length + ' 个' }),
|
|
244
|
+
props.agents.length ? props.agents.map(function (agent) {
|
|
245
|
+
return React.createElement('div', { className: 'da-row', key: agent.runtime_target_ref },
|
|
246
|
+
React.createElement('span', null, React.createElement('b', null, text(agent.display_name, agent.runtime_target_ref)), React.createElement('code', null, agent.runtime_target_ref)),
|
|
247
|
+
Badge({ value: agent.enabled === false ? 'disabled' : 'enabled' }))
|
|
248
|
+
}) : React.createElement('small', { className: 'da-muted' }, '没有在线 Agent')),
|
|
249
|
+
props.dispatches.length ? React.createElement('div', { className: 'da-card' },
|
|
250
|
+
SectionTitle({ title: '本页派发记录', note: '租约心跳与工作进度分开显示' }),
|
|
251
|
+
props.dispatches.map(function (item, index) {
|
|
252
|
+
const progress = item?.latest_progress
|
|
253
|
+
const envelope = item?.result_envelope
|
|
254
|
+
const percent = Number(progress?.percent)
|
|
255
|
+
return React.createElement('article', { className: 'da-dispatch', key: item?.id || index },
|
|
256
|
+
React.createElement('div', { className: 'da-card-head' },
|
|
257
|
+
React.createElement('div', null, React.createElement('strong', null, text(item?.runtime_target_ref, 'dispatch')), React.createElement('code', null, text(item?.id))),
|
|
258
|
+
Badge({ value: item?.status || 'submitted' })),
|
|
259
|
+
React.createElement('div', { className: 'da-dispatch-signals' },
|
|
260
|
+
React.createElement('span', null, '租约心跳 ', React.createElement('b', null, shortTime(item?.claim_renewed_at))),
|
|
261
|
+
React.createElement('span', null, '工作进度 ', React.createElement('b', null, progress ? text(progress.phase) : '尚未报告'))),
|
|
262
|
+
progress ? React.createElement('div', { className: 'da-progress' },
|
|
263
|
+
React.createElement('div', { className: 'da-progress-track' }, React.createElement('i', { style: { width: Number.isFinite(percent) ? Math.max(0, Math.min(100, percent)) + '%' : '0%' } })),
|
|
264
|
+
React.createElement('small', null, text(progress.message, '阶段 #' + progress.attempt + '.' + progress.sequence) + (Number.isFinite(percent) ? ' · ' + percent + '%' : ''))) : null,
|
|
265
|
+
envelope ? React.createElement('div', { className: 'da-evidence' },
|
|
266
|
+
React.createElement('span', null, list(envelope.claims).length + ' 条主张'),
|
|
267
|
+
React.createElement('span', null, list(envelope.evidence).length + ' 项证据'),
|
|
268
|
+
typeof envelope.confidence === 'number' ? React.createElement('span', null, '置信度 ' + Math.round(envelope.confidence * 100) + '%') : null) : null,
|
|
269
|
+
envelope?.answer ? React.createElement('p', { className: 'da-result' }, envelope.answer) : null)
|
|
270
|
+
})) : null)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function Coordination(props) {
|
|
274
|
+
const [mode, setMode] = React.useState('fanout')
|
|
275
|
+
const [prompt, setPrompt] = React.useState('')
|
|
276
|
+
const [selected, setSelected] = React.useState([])
|
|
277
|
+
const [saving, setSaving] = React.useState(false)
|
|
278
|
+
React.useEffect(function () {
|
|
279
|
+
if (!selected.length && props.agents.length) {
|
|
280
|
+
setSelected(props.agents.slice(0, 3).map(function (agent) { return agent.runtime_target_ref }))
|
|
281
|
+
}
|
|
282
|
+
}, [props.agents, selected.length])
|
|
283
|
+
function toggle(target) {
|
|
284
|
+
setSelected(function (items) { return items.includes(target) ? items.filter(function (item) { return item !== target }) : items.concat(target) })
|
|
285
|
+
}
|
|
286
|
+
async function createRun(event) {
|
|
287
|
+
event.preventDefault()
|
|
288
|
+
if (!prompt.trim() || !selected.length || saving) return
|
|
289
|
+
setSaving(true)
|
|
290
|
+
try {
|
|
291
|
+
const run = await rpc('coordination-create', {
|
|
292
|
+
mode: mode,
|
|
293
|
+
prompt: prompt.trim(),
|
|
294
|
+
runtimeTargetRefs: selected,
|
|
295
|
+
budget: { max_agents: selected.length, max_dispatches: Math.max(selected.length, mode === 'fanout' || mode === 'single' ? selected.length : selected.length + 1), max_wall_clock_seconds: 1800 },
|
|
296
|
+
idempotencyKey: 'dsh-agora-ui-run-' + Date.now() + '-' + Math.random().toString(36).slice(2),
|
|
297
|
+
})
|
|
298
|
+
setPrompt(''); props.onCreated(run); await props.reload()
|
|
299
|
+
} catch (error) { props.onError(error) } finally { setSaving(false) }
|
|
300
|
+
}
|
|
301
|
+
return React.createElement('div', { className: 'da-scroll da-stack' },
|
|
302
|
+
React.createElement('form', { className: 'da-card da-form', onSubmit: createRun },
|
|
303
|
+
SectionTitle({ title: '新建协同运行', note: '自动选 Agent、限制预算、汇总冲突与证据' }),
|
|
304
|
+
React.createElement('label', null, '策略', React.createElement('select', { value: mode, onChange: function (event) { setMode(event.target.value) } },
|
|
305
|
+
React.createElement('option', { value: 'single' }, 'Single · 单 Agent'),
|
|
306
|
+
React.createElement('option', { value: 'fanout' }, 'Fan-out · 并行调查'),
|
|
307
|
+
React.createElement('option', { value: 'review' }, 'Review · 执行 + 复核'),
|
|
308
|
+
React.createElement('option', { value: 'debate' }, 'Debate · 冲突仲裁'),
|
|
309
|
+
React.createElement('option', { value: 'council' }, 'Council · 委员会裁决'))),
|
|
310
|
+
React.createElement('div', { className: 'da-agent-picker' }, props.agents.map(function (agent) {
|
|
311
|
+
const target = agent.runtime_target_ref
|
|
312
|
+
return React.createElement('label', { key: target, className: selected.includes(target) ? 'selected' : '' },
|
|
313
|
+
React.createElement('input', { type: 'checkbox', checked: selected.includes(target), onChange: function () { toggle(target) } }),
|
|
314
|
+
React.createElement('span', null, text(agent.display_name, target)), React.createElement('code', null, target))
|
|
315
|
+
})),
|
|
316
|
+
React.createElement('textarea', { value: prompt, rows: 5, placeholder: '描述需要多个 Agent 协同完成的目标和验收标准…', onChange: function (event) { setPrompt(event.target.value) } }),
|
|
317
|
+
React.createElement('button', { className: 'da-primary', type: 'submit', disabled: saving || !prompt.trim() || !selected.length }, saving ? '创建中…' : '开始协同')),
|
|
318
|
+
React.createElement('div', { className: 'da-card' },
|
|
319
|
+
SectionTitle({ title: '协同运行', note: props.runs.length + ' 条' }),
|
|
320
|
+
props.runs.length ? props.runs.map(function (run) {
|
|
321
|
+
const completed = list(run.members).filter(function (member) { return member.status === 'completed' }).length
|
|
322
|
+
return React.createElement('article', { className: 'da-run', key: run.id },
|
|
323
|
+
React.createElement('div', { className: 'da-card-head' }, React.createElement('div', null,
|
|
324
|
+
React.createElement('strong', null, text(run.mode).toUpperCase()), React.createElement('code', null, text(run.id))), Badge({ value: run.status })),
|
|
325
|
+
React.createElement('div', { className: 'da-inline-meta' },
|
|
326
|
+
React.createElement('span', null, '成员 ', completed + '/' + list(run.members).length),
|
|
327
|
+
React.createElement('span', null, '证据 ', list(run.synthesis?.evidence_ids).length),
|
|
328
|
+
React.createElement('span', null, '冲突 ', list(run.synthesis?.conflicts).length),
|
|
329
|
+
React.createElement('span', null, 'Token ', run.usage?.total_tokens ?? '—')),
|
|
330
|
+
run.stop_reason ? React.createElement('small', { className: 'da-muted' }, run.stop_reason) : null,
|
|
331
|
+
list(run.synthesis?.conflicts).slice(0, 3).map(function (conflict) {
|
|
332
|
+
return React.createElement('div', { className: 'da-conflict', key: conflict.id }, text(conflict.kind) + ': ' + text(conflict.detail))
|
|
333
|
+
}),
|
|
334
|
+
run.synthesis?.answer ? React.createElement('p', { className: 'da-result' }, run.synthesis.answer) : null)
|
|
335
|
+
}) : React.createElement('small', { className: 'da-muted' }, '暂无协同运行')),
|
|
336
|
+
React.createElement('div', { className: 'da-card' },
|
|
337
|
+
SectionTitle({ title: 'Agent Scorecard', note: '历史成功率、时延、证据增益与环境漂移' }),
|
|
338
|
+
props.scorecards.length ? props.scorecards.map(function (card) {
|
|
339
|
+
return React.createElement('div', { className: 'da-row', key: card.runtime_target_ref + ':' + card.task_type },
|
|
340
|
+
React.createElement('span', null, React.createElement('b', null, card.runtime_target_ref), React.createElement('code', null, card.task_type + ' · ' + card.observations + ' 次观测')),
|
|
341
|
+
React.createElement('small', null, '评分 ' + Math.round(card.score) + ' · 成功 ' + (card.success_rate == null ? '—' : Math.round(card.success_rate * 100) + '%') + ' · 漂移 ' + (card.environment_drift_rate == null ? '—' : Math.round(card.environment_drift_rate * 100) + '%')))
|
|
342
|
+
}) : React.createElement('small', { className: 'da-muted' }, '完成一次协同运行后生成评分')))
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function AgoraPanel(props) {
|
|
346
|
+
const [tab, setTab] = React.useState('overview')
|
|
347
|
+
const [snapshot, setSnapshot] = React.useState(null)
|
|
348
|
+
const [nodes, setNodes] = React.useState([])
|
|
349
|
+
const [agents, setAgents] = React.useState([])
|
|
350
|
+
const [tasks, setTasks] = React.useState([])
|
|
351
|
+
const [runs, setRuns] = React.useState([])
|
|
352
|
+
const [scorecards, setScorecards] = React.useState([])
|
|
353
|
+
const [dispatches, setDispatches] = React.useState([])
|
|
354
|
+
const [loading, setLoading] = React.useState(true)
|
|
355
|
+
const [error, setError] = React.useState('')
|
|
356
|
+
const [updatedAt, setUpdatedAt] = React.useState(null)
|
|
357
|
+
const dispatchIds = dispatches.map(function (item) { return item?.id }).filter(Boolean).join('|')
|
|
358
|
+
|
|
359
|
+
const load = React.useCallback(async function (quiet) {
|
|
360
|
+
if (!quiet) setLoading(true)
|
|
361
|
+
try {
|
|
362
|
+
const values = await Promise.all([rpc('snapshot'), rpc('nodes'), rpc('agents'), rpc('tasks'), rpc('coordination-runs'), rpc('scorecards')])
|
|
363
|
+
setSnapshot(values[0]); setNodes(list(values[1])); setAgents(list(values[2])); setTasks(list(values[3])); setRuns(list(values[4])); setScorecards(list(values[5]))
|
|
364
|
+
setError(''); setUpdatedAt(new Date())
|
|
365
|
+
} catch (reason) { setError(reason instanceof Error ? reason.message : String(reason)) }
|
|
366
|
+
finally { if (!quiet) setLoading(false) }
|
|
367
|
+
}, [])
|
|
368
|
+
|
|
369
|
+
React.useEffect(function () {
|
|
370
|
+
if (props.visible === false) return
|
|
371
|
+
load(false)
|
|
372
|
+
const timer = setInterval(function () { load(true) }, POLL_MS)
|
|
373
|
+
return function () { clearInterval(timer) }
|
|
374
|
+
}, [props.visible, load])
|
|
375
|
+
|
|
376
|
+
React.useEffect(function () {
|
|
377
|
+
if (props.visible === false || !dispatchIds) return
|
|
378
|
+
let cancelled = false
|
|
379
|
+
let timer
|
|
380
|
+
const refresh = async function () {
|
|
381
|
+
try {
|
|
382
|
+
const values = await Promise.all(dispatchIds.split('|').map(function (dispatchId) {
|
|
383
|
+
return rpc('dispatch-status', { dispatchId: dispatchId })
|
|
384
|
+
}))
|
|
385
|
+
if (cancelled) return
|
|
386
|
+
setDispatches(values)
|
|
387
|
+
if (values.some(function (item) { return !['completed', 'failed', 'cancelled'].includes(item?.status) })) {
|
|
388
|
+
timer = setTimeout(refresh, 2000)
|
|
389
|
+
}
|
|
390
|
+
} catch (reason) {
|
|
391
|
+
if (!cancelled) onError(reason)
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
refresh()
|
|
395
|
+
return function () { cancelled = true; if (timer) clearTimeout(timer) }
|
|
396
|
+
}, [props.visible, dispatchIds])
|
|
397
|
+
|
|
398
|
+
function onError(reason) { setError(reason instanceof Error ? reason.message : String(reason)) }
|
|
399
|
+
const tabs = [['overview', '总览'], ['nodes', '节点'], ['tasks', '任务'], ['agents', '派发'], ['coordination', '协同']]
|
|
400
|
+
let body
|
|
401
|
+
if (loading && !snapshot) body = React.createElement(Empty, null, '正在连接 Agora…')
|
|
402
|
+
else if (tab === 'nodes') body = React.createElement(Nodes, { nodes: nodes })
|
|
403
|
+
else if (tab === 'tasks') body = React.createElement(Tasks, { tasks: tasks, reload: function () { return load(true) }, onError: onError, onSelect: function () {} })
|
|
404
|
+
else if (tab === 'agents') body = React.createElement(Agents, { agents: agents, dispatches: dispatches, onError: onError, onDispatch: function (value) { setDispatches(function (items) { return [value].concat(items).slice(0, 12) }) } })
|
|
405
|
+
else if (tab === 'coordination') body = React.createElement(Coordination, { agents: agents, runs: runs, scorecards: scorecards, reload: function () { return load(true) }, onError: onError, onCreated: function (run) { setRuns(function (items) { return [run].concat(items).slice(0, 100) }) } })
|
|
406
|
+
else body = React.createElement(Overview, { snapshot: snapshot, nodes: nodes, agents: agents })
|
|
407
|
+
|
|
408
|
+
return React.createElement('div', { className: 'da-root' },
|
|
409
|
+
React.createElement('header', { className: 'da-header' },
|
|
410
|
+
React.createElement('div', null, React.createElement('strong', null, 'Agora'), React.createElement('small', null, '多 Agent 协同中心')),
|
|
411
|
+
React.createElement('button', { type: 'button', title: '立即刷新', onClick: function () { load(false) } }, '↻')),
|
|
412
|
+
React.createElement('nav', { className: 'da-tabs' }, tabs.map(function (item) {
|
|
413
|
+
return React.createElement('button', { type: 'button', key: item[0], className: tab === item[0] ? 'active' : '', onClick: function () { setTab(item[0]) } }, item[1])
|
|
414
|
+
})),
|
|
415
|
+
error ? React.createElement('div', { className: 'da-error' }, React.createElement('span', null, error), React.createElement('button', { type: 'button', onClick: function () { setError('') } }, '×')) : null,
|
|
416
|
+
body,
|
|
417
|
+
React.createElement('footer', { className: 'da-footer' },
|
|
418
|
+
React.createElement('span', null, snapshot?.node?.nodeId || '未连接节点'),
|
|
419
|
+
React.createElement('span', null, updatedAt ? '更新于 ' + updatedAt.toLocaleTimeString() : '等待数据')))
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const CSS = [
|
|
423
|
+
'.da-root{box-sizing:border-box;height:100%;min-height:0;display:flex;flex-direction:column;background:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-base));color:var(--dsw-alias-label-primary);font-size:13px;pointer-events:auto}',
|
|
424
|
+
'.da-root *{box-sizing:border-box}',
|
|
425
|
+
'.da-launcher{position:fixed;z-index:90;top:6px;right:46px;height:28px;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);display:inline-flex;align-items:center;gap:5px;padding:0 8px;cursor:pointer;font:inherit;font-size:11px;box-shadow:var(--dsw-shadow-lv1)}.da-launcher:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-brand-primary)}.da-launcher-icon{font-size:17px;line-height:1}.da-launcher-label{display:inline}@media(max-width:767px){.da-launcher{width:28px;padding:0;justify-content:center}.da-launcher-label{display:none}}',
|
|
426
|
+
'.da-header{height:44px;flex:none;padding:0 9px 0 12px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--dsw-alias-border-l1)}',
|
|
427
|
+
'.da-header>div{display:flex;flex-direction:column;line-height:17px}.da-header strong{font-size:14px}.da-header small,.da-muted{color:var(--dsw-alias-label-dimmed);font-size:11px}',
|
|
428
|
+
'.da-header button,.da-error button{border:0;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;border-radius:50%;width:28px;height:28px;font-size:18px}.da-header button:hover,.da-error button:hover{background:var(--dsw-alias-interactive-bg-hover)}',
|
|
429
|
+
'.da-tabs{height:35px;flex:none;display:grid;grid-template-columns:repeat(5,1fr);padding:3px 6px;border-bottom:1px solid var(--dsw-alias-border-l1);gap:2px}.da-tabs button{border:0;border-radius:7px;background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-size:11px;cursor:pointer}.da-tabs button:hover{background:var(--dsw-alias-interactive-bg-hover)}.da-tabs button.active{background:var(--dsw-alias-interactive-bg-active);color:var(--dsw-alias-brand-primary);font-weight:600}',
|
|
430
|
+
'.da-scroll{flex:1;min-height:0;overflow:auto;padding:10px}.da-stack{display:flex;flex-direction:column;gap:8px}',
|
|
431
|
+
'.da-metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:7px;margin-bottom:8px}.da-metric{border:1px solid var(--dsw-alias-border-l1);border-radius:10px;padding:10px;background:var(--dsw-alias-bg-base);display:flex;flex-direction:column}.da-metric b{font-size:19px;line-height:24px}.da-metric span{font-size:11px;color:var(--dsw-alias-label-dimmed)}',
|
|
432
|
+
'.da-card{border:1px solid var(--dsw-alias-border-l1);border-radius:10px;background:var(--dsw-alias-bg-base);padding:10px;margin-bottom:8px;color:inherit;text-align:left;width:100%;font:inherit}.da-card-head,.da-section-title{display:flex;align-items:flex-start;justify-content:space-between;gap:8px}.da-card-head>div{display:flex;flex-direction:column}.da-card-head small,.da-section-title span{font-size:10px;color:var(--dsw-alias-label-dimmed);font-weight:400}.da-section-title{margin-bottom:9px}.da-section-title strong{font-size:12px}',
|
|
433
|
+
'.da-badge{display:inline-flex;align-items:center;max-width:130px;height:20px;padding:0 7px;border-radius:999px;background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);font-size:10px;white-space:nowrap}.da-badge.da-ok{color:var(--dsw-alias-state-success-primary)}.da-badge.da-bad{color:var(--dsw-alias-state-error-primary)}.da-badge.da-warn{color:var(--dsw-alias-state-warning-primary,var(--dsw-alias-label-secondary))}',
|
|
434
|
+
'.da-kv{display:grid;grid-template-columns:max-content minmax(0,1fr);align-items:center;gap:7px 10px;font-size:11px}.da-kv>span:nth-child(odd){color:var(--dsw-alias-label-dimmed)}.da-kv code,.da-row code{font:10px var(--dsh-font-mono,ui-monospace,monospace);color:var(--dsw-alias-label-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}',
|
|
435
|
+
'.da-flow{display:flex;align-items:center;gap:5px;flex-wrap:wrap}.da-flow span{padding:5px 7px;border-radius:6px;background:var(--dsw-alias-interactive-bg-hover);font-size:10px}.da-flow i{font-style:normal;color:var(--dsw-alias-label-dimmed)}',
|
|
436
|
+
'.da-inline-meta{display:flex;gap:8px;flex-wrap:wrap;color:var(--dsw-alias-label-dimmed);font-size:10px;margin:8px 0}.da-subtitle{margin:8px 0 3px;text-transform:uppercase;letter-spacing:.08em;font-size:9px;color:var(--dsw-alias-label-dimmed)}',
|
|
437
|
+
'.da-row{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:7px 0;border-top:1px solid var(--dsw-alias-border-l1)}.da-row>span:first-child{display:flex;min-width:0;flex-direction:column}.da-row b{font-size:11px;font-weight:550}.da-row small{font-size:10px;color:var(--dsw-alias-label-dimmed);text-align:right}',
|
|
438
|
+
'.da-dispatch{padding:9px 0;border-top:1px solid var(--dsw-alias-border-l1)}.da-dispatch:first-of-type{border-top:0}.da-dispatch code{font:9px var(--dsh-font-mono,ui-monospace,monospace);color:var(--dsw-alias-label-dimmed)}.da-dispatch-signals{display:grid;grid-template-columns:1fr 1fr;gap:5px;margin:7px 0;color:var(--dsw-alias-label-dimmed);font-size:9px}.da-dispatch-signals b{display:block;color:var(--dsw-alias-label-secondary);font-weight:500}.da-progress-track{height:4px;border-radius:999px;background:var(--dsw-alias-interactive-bg-hover);overflow:hidden}.da-progress-track i{display:block;height:100%;background:var(--dsw-alias-brand-primary);border-radius:inherit}.da-progress small{display:block;margin-top:4px;color:var(--dsw-alias-label-secondary);font-size:9px}.da-evidence{display:flex;gap:5px;flex-wrap:wrap;margin-top:6px}.da-evidence span{padding:2px 5px;border-radius:5px;background:var(--dsw-alias-interactive-bg-hover);font-size:9px;color:var(--dsw-alias-label-secondary)}.da-result{margin:7px 0 0;max-height:64px;overflow:auto;white-space:pre-wrap;color:var(--dsw-alias-label-secondary);font-size:10px;line-height:15px}',
|
|
439
|
+
'.da-form{display:flex;flex-direction:column;gap:7px}.da-form label{display:flex;flex-direction:column;gap:4px;color:var(--dsw-alias-label-secondary);font-size:11px}.da-form input,.da-form textarea,.da-form select{width:100%;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;background:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-base));color:var(--dsw-alias-label-primary);padding:7px 8px;font:inherit;font-size:12px;outline:none;resize:vertical}.da-form input:focus,.da-form textarea:focus,.da-form select:focus{border-color:var(--dsw-alias-brand-primary)}',
|
|
440
|
+
'.da-agent-picker{display:flex;flex-direction:column;gap:4px;max-height:126px;overflow:auto}.da-form .da-agent-picker label{display:grid;grid-template-columns:16px minmax(0,1fr);gap:1px 6px;padding:5px 7px;border:1px solid var(--dsw-alias-border-l1);border-radius:7px;cursor:pointer}.da-form .da-agent-picker label.selected{border-color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-interactive-bg-active)}.da-form .da-agent-picker input{width:auto;grid-row:1/3;align-self:center}.da-agent-picker code{font:9px var(--dsh-font-mono,ui-monospace,monospace);color:var(--dsw-alias-label-dimmed);overflow:hidden;text-overflow:ellipsis}.da-run{padding:8px 0;border-top:1px solid var(--dsw-alias-border-l1)}.da-run:first-of-type{border-top:0}.da-run code{font:9px var(--dsh-font-mono,ui-monospace,monospace);color:var(--dsw-alias-label-dimmed)}.da-conflict{margin-top:5px;padding:5px 7px;border-radius:6px;background:color-mix(in srgb,var(--dsw-alias-state-warning-primary) 10%,transparent);color:var(--dsw-alias-state-warning-primary,var(--dsw-alias-label-secondary));font-size:9px}',
|
|
441
|
+
'.da-primary{height:30px;border:0;border-radius:7px;background:var(--dsw-alias-brand-primary);color:white;font:inherit;font-size:12px;cursor:pointer}.da-primary:disabled{opacity:.5;cursor:not-allowed}.da-task{cursor:pointer}.da-task:hover{border-color:var(--dsw-alias-brand-primary)}.da-task p{margin:7px 0;color:var(--dsw-alias-label-secondary);font-size:11px;line-height:16px}.da-task>small{color:var(--dsw-alias-label-dimmed);font-size:9px}',
|
|
442
|
+
'.da-empty{flex:1;min-height:120px;display:flex;align-items:center;justify-content:center;padding:24px;color:var(--dsw-alias-label-dimmed);text-align:center}.da-error{flex:none;display:flex;align-items:center;justify-content:space-between;gap:8px;padding:6px 8px 6px 10px;background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 12%,transparent);color:var(--dsw-alias-state-error-primary);font-size:11px}.da-error span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}',
|
|
443
|
+
'.da-footer{height:25px;flex:none;padding:0 10px;border-top:1px solid var(--dsw-alias-border-l1);display:flex;align-items:center;justify-content:space-between;color:var(--dsw-alias-label-dimmed);font-size:9px}',
|
|
444
|
+
].join('\n')
|
|
445
|
+
|
|
446
|
+
function installStyles() {
|
|
447
|
+
if (typeof document === 'undefined') return function () {}
|
|
448
|
+
const old = document.querySelector('style[data-plugin="dsh-agora"]')
|
|
449
|
+
if (old) old.remove()
|
|
450
|
+
const style = document.createElement('style')
|
|
451
|
+
style.setAttribute('data-plugin', 'dsh-agora')
|
|
452
|
+
style.textContent = CSS
|
|
453
|
+
;(document.head || document.documentElement).appendChild(style)
|
|
454
|
+
return function () { style.remove() }
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function apply(ctx) {
|
|
458
|
+
ctx.effect(function () { return installStyles() }, 'dsh-agora: client styles')
|
|
459
|
+
ctx.plugin({
|
|
460
|
+
inject: ['betterSidebar'],
|
|
461
|
+
apply(sidebarCtx) {
|
|
462
|
+
const sidebar = sidebarCtx.betterSidebar || sidebarCtx.get?.('betterSidebar')
|
|
463
|
+
if (!sidebar || typeof sidebar.registerTab !== 'function') return
|
|
464
|
+
sidebarCtx.effect(function () {
|
|
465
|
+
const unregister = sidebar.registerTab({
|
|
466
|
+
id: TAB_ID,
|
|
467
|
+
title: function () { return 'Agora 协同' },
|
|
468
|
+
icon: function (size) { return React.createElement(AgoraIcon, { size: size }) },
|
|
469
|
+
order: 45,
|
|
470
|
+
single: true,
|
|
471
|
+
component: function (props) { return React.createElement(AgoraPanel, { visible: props.visible, scope: props.scope }) },
|
|
472
|
+
})
|
|
473
|
+
const removeLauncher = installLauncher(sidebar)
|
|
474
|
+
let cancelled = false
|
|
475
|
+
if (shouldAutoOpen()) {
|
|
476
|
+
Promise.resolve().then(function () {
|
|
477
|
+
if (!cancelled) openAgora(sidebar)
|
|
478
|
+
})
|
|
479
|
+
}
|
|
480
|
+
return function () {
|
|
481
|
+
cancelled = true
|
|
482
|
+
removeLauncher()
|
|
483
|
+
unregister()
|
|
484
|
+
}
|
|
485
|
+
}, 'dsh-agora: better-sidebar tab')
|
|
486
|
+
},
|
|
487
|
+
})
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
exports.apply = apply
|
|
491
|
+
return module.exports
|
|
492
|
+
} })
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { AgoraCommandResult, AgoraRequestContext } from './contracts.js';
|
|
2
|
+
import type { DshImBridgeV1 } from './im-bridge-v1.js';
|
|
3
|
+
export declare const DSH_AGORA_COMMAND_ADAPTER_PROTOCOL: "dsh-agora.command-adapter/v1";
|
|
4
|
+
export interface DshAgoraCommandEventV1 {
|
|
5
|
+
readonly protocol: typeof DSH_AGORA_COMMAND_ADAPTER_PROTOCOL;
|
|
6
|
+
readonly idempotency_key: string;
|
|
7
|
+
readonly input: string;
|
|
8
|
+
readonly actor_ref: string;
|
|
9
|
+
readonly provider?: string | null;
|
|
10
|
+
readonly conversation_ref?: string | null;
|
|
11
|
+
readonly thread_ref?: string | null;
|
|
12
|
+
readonly reply?: {
|
|
13
|
+
readonly enabled: boolean;
|
|
14
|
+
readonly bot_ref?: string | null;
|
|
15
|
+
} | null;
|
|
16
|
+
readonly metadata?: Readonly<Record<string, unknown>> | null;
|
|
17
|
+
}
|
|
18
|
+
export interface DshAgoraCommandEventResultV1 {
|
|
19
|
+
readonly protocol: typeof DSH_AGORA_COMMAND_ADAPTER_PROTOCOL;
|
|
20
|
+
readonly idempotency_key: string;
|
|
21
|
+
readonly result: AgoraCommandResult;
|
|
22
|
+
readonly delivery: {
|
|
23
|
+
readonly sent: boolean;
|
|
24
|
+
readonly provider_message_refs: readonly string[];
|
|
25
|
+
readonly reason?: string;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export interface DshAgoraCommandAdapterV1 {
|
|
29
|
+
readonly protocol: typeof DSH_AGORA_COMMAND_ADAPTER_PROTOCOL;
|
|
30
|
+
ingest(event: DshAgoraCommandEventV1, signal?: AbortSignal): Promise<DshAgoraCommandEventResultV1>;
|
|
31
|
+
}
|
|
32
|
+
export declare class DshAgoraCommandAdapter implements DshAgoraCommandAdapterV1 {
|
|
33
|
+
private readonly options;
|
|
34
|
+
readonly protocol: "dsh-agora.command-adapter/v1";
|
|
35
|
+
private readonly receipts;
|
|
36
|
+
constructor(options: {
|
|
37
|
+
execute(input: string, context: AgoraRequestContext, signal?: AbortSignal): Promise<AgoraCommandResult>;
|
|
38
|
+
bridge(): DshImBridgeV1 | null;
|
|
39
|
+
receiptLimit?: number;
|
|
40
|
+
});
|
|
41
|
+
ingest(event: DshAgoraCommandEventV1, signal?: AbortSignal): Promise<DshAgoraCommandEventResultV1>;
|
|
42
|
+
private execute;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=command-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-adapter.d.ts","sourceRoot":"","sources":["../src/command-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AAC7E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAEtD,eAAO,MAAM,kCAAkC,EAAG,8BAAuC,CAAA;AAEzF,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,QAAQ,EAAE,OAAO,kCAAkC,CAAA;IAC5D,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE;QACf,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;QACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KACjC,GAAG,IAAI,CAAA;IACR,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAAA;CAC7D;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,QAAQ,EAAE,OAAO,kCAAkC,CAAA;IAC5D,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAA;IACnC,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;QACtB,QAAQ,CAAC,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAA;QACjD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KACzB,CAAA;CACF;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,QAAQ,EAAE,OAAO,kCAAkC,CAAA;IAC5D,MAAM,CAAC,KAAK,EAAE,sBAAsB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAAA;CACnG;AAED,qBAAa,sBAAuB,YAAW,wBAAwB;IAIzD,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,QAAQ,CAAC,QAAQ,iCAAqC;IACtD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA2D;gBAEvD,OAAO,EAAE;QACpC,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;QACvG,MAAM,IAAI,aAAa,GAAG,IAAI,CAAA;QAC9B,YAAY,CAAC,EAAE,MAAM,CAAA;KACtB;IAED,MAAM,CAAC,KAAK,EAAE,sBAAsB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,4BAA4B,CAAC;YAapF,OAAO;CA8BtB"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export const DSH_AGORA_COMMAND_ADAPTER_PROTOCOL = 'dsh-agora.command-adapter/v1';
|
|
2
|
+
export class DshAgoraCommandAdapter {
|
|
3
|
+
options;
|
|
4
|
+
protocol = DSH_AGORA_COMMAND_ADAPTER_PROTOCOL;
|
|
5
|
+
receipts = new Map();
|
|
6
|
+
constructor(options) {
|
|
7
|
+
this.options = options;
|
|
8
|
+
}
|
|
9
|
+
ingest(event, signal) {
|
|
10
|
+
validateEvent(event);
|
|
11
|
+
const existing = this.receipts.get(event.idempotency_key);
|
|
12
|
+
if (existing)
|
|
13
|
+
return existing;
|
|
14
|
+
const pending = this.execute(event, signal);
|
|
15
|
+
this.receipts.set(event.idempotency_key, pending);
|
|
16
|
+
while (this.receipts.size > (this.options.receiptLimit ?? 2_048))
|
|
17
|
+
this.receipts.delete(this.receipts.keys().next().value);
|
|
18
|
+
pending.catch(() => {
|
|
19
|
+
if (this.receipts.get(event.idempotency_key) === pending)
|
|
20
|
+
this.receipts.delete(event.idempotency_key);
|
|
21
|
+
});
|
|
22
|
+
return pending;
|
|
23
|
+
}
|
|
24
|
+
async execute(event, signal) {
|
|
25
|
+
const result = await this.options.execute(event.input, {
|
|
26
|
+
actorId: event.actor_ref,
|
|
27
|
+
...(event.provider ? { provider: event.provider } : {}),
|
|
28
|
+
...(event.conversation_ref ? { conversationRef: event.conversation_ref } : {}),
|
|
29
|
+
...(event.thread_ref ? { threadRef: event.thread_ref } : {}),
|
|
30
|
+
}, signal);
|
|
31
|
+
const bridge = this.options.bridge();
|
|
32
|
+
if (!event.reply?.enabled)
|
|
33
|
+
return envelope(event, result, { sent: false, provider_message_refs: [], reason: 'reply disabled' });
|
|
34
|
+
if (!bridge || !event.provider || !event.conversation_ref) {
|
|
35
|
+
return envelope(event, result, { sent: false, provider_message_refs: [], reason: 'IM bridge or destination unavailable' });
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const receipt = await bridge.send({
|
|
39
|
+
provider: event.provider,
|
|
40
|
+
conversation_ref: event.conversation_ref,
|
|
41
|
+
...(event.reply.bot_ref === undefined ? {} : { bot_ref: event.reply.bot_ref }),
|
|
42
|
+
...(event.thread_ref === undefined ? {} : { thread_ref: event.thread_ref }),
|
|
43
|
+
text: result.text ?? (result.kind === 'success' ? 'Agora command completed.' : 'Agora command failed.'),
|
|
44
|
+
idempotency_key: `command:${event.idempotency_key}`,
|
|
45
|
+
});
|
|
46
|
+
return envelope(event, result, { sent: true, provider_message_refs: receipt.provider_message_refs });
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return envelope(event, result, {
|
|
50
|
+
sent: false,
|
|
51
|
+
provider_message_refs: [],
|
|
52
|
+
reason: `IM delivery failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function envelope(event, result, delivery) {
|
|
58
|
+
return { protocol: DSH_AGORA_COMMAND_ADAPTER_PROTOCOL, idempotency_key: event.idempotency_key, result, delivery };
|
|
59
|
+
}
|
|
60
|
+
function validateEvent(event) {
|
|
61
|
+
if (event.protocol !== DSH_AGORA_COMMAND_ADAPTER_PROTOCOL)
|
|
62
|
+
throw new TypeError(`command event protocol must be ${DSH_AGORA_COMMAND_ADAPTER_PROTOCOL}`);
|
|
63
|
+
for (const [name, value] of [['idempotency_key', event.idempotency_key], ['input', event.input], ['actor_ref', event.actor_ref]]) {
|
|
64
|
+
if (typeof value !== 'string' || !value.trim())
|
|
65
|
+
throw new TypeError(`${name} is required`);
|
|
66
|
+
}
|
|
67
|
+
if (event.reply?.enabled && (!event.provider || !event.conversation_ref))
|
|
68
|
+
throw new TypeError('reply requires provider and conversation_ref');
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=command-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-adapter.js","sourceRoot":"","sources":["../src/command-adapter.ts"],"names":[],"mappings":"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"}
|