dsh-device-fleet 0.1.0-dev.29 → 0.1.0-dev.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client.js +29 -2
- package/docs/notes/dev-release.md +5 -1
- package/package.json +1 -1
- package/src/host/ui-web.js +6 -0
- package/src/plugin/index.js +10 -0
package/client.js
CHANGED
|
@@ -1317,9 +1317,21 @@ window.__ModuleLoader__.load({
|
|
|
1317
1317
|
h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, fontSize: 11, opacity: 0.8, padding: '2px 2px 0', flexWrap: 'wrap' } },
|
|
1318
1318
|
pill('Room', 'ok'),
|
|
1319
1319
|
h('span', null, room.members.length + ' 名成员'),
|
|
1320
|
-
|
|
1320
|
+
h('span', { style: { opacity: 0.6 } }, '可发送给'),
|
|
1321
|
+
...room.members.map((member) =>
|
|
1322
|
+
h('button', {
|
|
1323
|
+
key: member.memberId,
|
|
1324
|
+
type: 'button',
|
|
1325
|
+
onClick: () => {
|
|
1326
|
+
if (!insertMentionText(props, member.name)) {
|
|
1327
|
+
setStatus('请在输入框手动输入 @' + member.name)
|
|
1328
|
+
}
|
|
1329
|
+
},
|
|
1330
|
+
style: { padding: '1px 7px', borderRadius: 999, border: '1px solid var(--dsw-alias-border-l1, #333)', background: 'transparent', color: 'inherit', fontSize: 11, cursor: 'pointer' },
|
|
1331
|
+
}, '@' + member.name),
|
|
1332
|
+
),
|
|
1321
1333
|
h('span', { style: { flex: 1 } }),
|
|
1322
|
-
h('span', { style: { opacity: 0.
|
|
1334
|
+
h('span', { style: { opacity: 0.5, fontSize: 10 } }, '点名字插入 @;不点名发给所有人'),
|
|
1323
1335
|
),
|
|
1324
1336
|
);
|
|
1325
1337
|
}
|
|
@@ -1361,6 +1373,21 @@ window.__ModuleLoader__.load({
|
|
|
1361
1373
|
return () => clearInterval(timer);
|
|
1362
1374
|
}, [roomId]);
|
|
1363
1375
|
|
|
1376
|
+
// Only route Chat input into the Room while this view is open.
|
|
1377
|
+
useEffect(() => {
|
|
1378
|
+
if (!roomId) return undefined;
|
|
1379
|
+
const body = JSON.stringify({ roomId, focused: true });
|
|
1380
|
+
void fetch('/device-fleet/room/focus', { method: 'POST', headers: { 'content-type': 'application/json' }, body }).catch(() => {})
|
|
1381
|
+
return () => {
|
|
1382
|
+
void fetch('/device-fleet/room/focus', {
|
|
1383
|
+
method: 'POST',
|
|
1384
|
+
headers: { 'content-type': 'application/json' },
|
|
1385
|
+
body: JSON.stringify({ roomId, focused: false }),
|
|
1386
|
+
keepalive: true,
|
|
1387
|
+
}).catch(() => {})
|
|
1388
|
+
}
|
|
1389
|
+
}, [roomId]);
|
|
1390
|
+
|
|
1364
1391
|
const act = async (label, action) => {
|
|
1365
1392
|
setStatus(label + '…');
|
|
1366
1393
|
try {
|
|
@@ -153,4 +153,8 @@ dsh plugin --profile desktop add dsh-device-fleet@dev
|
|
|
153
153
|
- 移除房间页自带发送栏,房间消息直接使用 DSH 底部输入框;
|
|
154
154
|
- Host 监听 Room Host 会话的 user/message:解析 `@成员名`,被点名成员才加入收件人;未点名默认发给所有参与者;
|
|
155
155
|
- 消息文本会去掉 @mention 标记后写入房间日志;host 本身已收到 Chat 输入,不重复唤醒;
|
|
156
|
-
- 房间页保留成员/气泡/看板,底部提示用 @ 点名。
|
|
156
|
+
- 房间页保留成员/气泡/看板,底部提示用 @ 点名。
|
|
157
|
+
## 0.1.0-dev.29(@补全面板 + host 房间发言)
|
|
158
|
+
|
|
159
|
+
- 房间 Dock 读取 DSH 输入框草稿(标准 `useInput` 钩子),输入 `@` 时在输入框上方弹出补全面板,顶部标注「可发送给:所有人 / 指定 agent」,列出 Room 成员并可点击插入 `@name`;
|
|
160
|
+
- host agent 在 Chat 的 assistant 消息会同步写入 Room 日志(只读记录,不唤醒参与者),房间页左侧出现 host 气泡与 host 头像。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-device-fleet",
|
|
3
|
-
"version": "0.1.0-dev.
|
|
3
|
+
"version": "0.1.0-dev.30",
|
|
4
4
|
"description": "DeepSeek Harness device fleet: enroll devices, direct-call remote tools, shared Rooms with lazy-woken agents, and delegated subagents across devices.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/plugin/index.js",
|
package/src/host/ui-web.js
CHANGED
|
@@ -111,6 +111,12 @@ export function registerUiRoutes({ webServer, inbox, rooms, getOverview, actions
|
|
|
111
111
|
return { ok: true, room: await actions.upgradeRoom(body ?? {}) }
|
|
112
112
|
}),
|
|
113
113
|
)
|
|
114
|
+
disposers.push(
|
|
115
|
+
actionRoute(webServer, '/device-fleet/room/focus', 'POST', async (body) => {
|
|
116
|
+
if (!actions?.setRoomFocus) throw Object.assign(new Error('room actions are unavailable'), { code: 'unsupported' })
|
|
117
|
+
return { ok: true, result: actions.setRoomFocus(body ?? {}) }
|
|
118
|
+
}),
|
|
119
|
+
)
|
|
114
120
|
disposers.push(
|
|
115
121
|
actionRoute(webServer, '/device-fleet/pair/invite', 'POST', async () => {
|
|
116
122
|
if (!actions?.createInvite) throw Object.assign(new Error('pairing actions are unavailable'), { code: 'unsupported' })
|
package/src/plugin/index.js
CHANGED
|
@@ -227,6 +227,9 @@ export function apply(ctx, profileConfig) {
|
|
|
227
227
|
createUserMessage,
|
|
228
228
|
stateFile: join(state.dataDir, 'rooms.json'),
|
|
229
229
|
})
|
|
230
|
+
// User input is routed to a Room only while its Conversation View is open;
|
|
231
|
+
// normal Chat with the host agent must not wake participants.
|
|
232
|
+
const focusedRooms = new Set()
|
|
230
233
|
|
|
231
234
|
// Client approval panel data plane (dev.9). Optional service: host-only
|
|
232
235
|
// deployments simply do not expose the panel routes.
|
|
@@ -330,6 +333,12 @@ export function apply(ctx, profileConfig) {
|
|
|
330
333
|
throw Object.assign(new Error('action must be add or update'), { code: 'bad-request' })
|
|
331
334
|
},
|
|
332
335
|
closeRoom: ({ roomId, reason } = {}) => rooms.close({ roomId, reason: reason ?? 'closed by user' }),
|
|
336
|
+
setRoomFocus: ({ roomId, focused } = {}) => {
|
|
337
|
+
if (typeof roomId !== 'string' || roomId === '') throw Object.assign(new Error('roomId is required'), { code: 'bad-request' })
|
|
338
|
+
if (focused === true) focusedRooms.add(roomId)
|
|
339
|
+
else focusedRooms.delete(roomId)
|
|
340
|
+
return { roomId, focused: focused === true }
|
|
341
|
+
},
|
|
333
342
|
removeMember: ({ roomId, memberId, reason } = {}) => rooms.removeMember({ roomId, memberId, reason: reason ?? 'removed by user' }),
|
|
334
343
|
async upgradeRoom({ sessionId } = {}) {
|
|
335
344
|
if (typeof sessionId !== 'string' || sessionId === '') throw Object.assign(new Error('sessionId is required'), { code: 'bad-request' })
|
|
@@ -445,6 +454,7 @@ export function apply(ctx, profileConfig) {
|
|
|
445
454
|
if (!session?.id || event?.type !== 'user/message') return
|
|
446
455
|
const room = rooms.rooms.get(session.id)
|
|
447
456
|
if (!room) return
|
|
457
|
+
if (!focusedRooms.has(session.id)) return
|
|
448
458
|
const message = event.data
|
|
449
459
|
if (message?.source?.kind && message.source.kind !== 'user') return
|
|
450
460
|
const text = (Array.isArray(message?.content) ? message.content : [])
|