dsh-device-fleet 0.1.0-dev.24 → 0.1.0-dev.25

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.
Binary file
Binary file
Binary file
package/client.js CHANGED
@@ -1231,23 +1231,21 @@ window.__ModuleLoader__.load({
1231
1231
  }
1232
1232
 
1233
1233
  function roomAvatar(name, tone) {
1234
- const text = String(name ?? '?').trim().slice(0, 1).toUpperCase() || '?'
1235
- return h('div', {
1234
+ const kind = tone === 'user' ? 'user' : String(name ?? '') === 'host' ? 'host' : 'worker'
1235
+ return h('img', {
1236
+ src: '/device-fleet/avatar/' + kind,
1237
+ alt: String(name ?? kind),
1238
+ draggable: false,
1236
1239
  style: {
1237
1240
  width: 26,
1238
1241
  height: 26,
1239
1242
  borderRadius: '50%',
1240
1243
  flex: '0 0 26px',
1241
- display: 'flex',
1242
- alignItems: 'center',
1243
- justifyContent: 'center',
1244
- fontSize: 12,
1245
- fontWeight: 600,
1246
- background: tone === 'user' ? '#2f6fed' : 'var(--dsw-alias-bg-layer-2, #262626)',
1247
- color: tone === 'user' ? '#fff' : 'var(--dsw-alias-label-primary, #e8e8e8)',
1244
+ objectFit: 'cover',
1248
1245
  border: '1px solid var(--dsw-alias-border-l1, #333)',
1246
+ background: 'var(--dsw-alias-bg-layer-2, #262626)',
1249
1247
  },
1250
- }, text)
1248
+ })
1251
1249
  }
1252
1250
 
1253
1251
  // Room conversation rendered as a chat: agents on the left with names and
@@ -128,4 +128,10 @@ dsh plugin --profile desktop add dsh-device-fleet@dev
128
128
 
129
129
  - 「房间」Conversation View 重做为聊天式:agent 消息在左(头像 + 名字 + 收件人),用户消息在右;时间戳、Enter 发送、收件人勾选;
130
130
  - 参与者消息原文直接作为左侧气泡展示,不再进入 host 的普通 Chat;
131
- - 看板可展开/收起,支持任务状态更新与添加。
131
+ - 看板可展开/收起,支持任务状态更新与添加。
132
+ ## 0.1.0-dev.24(Room @点名)
133
+
134
+ - 房间输入框支持输入 `@` 弹出成员自动补全;选中成员变为「发给」chip,可点击取消;
135
+ - 无点名时默认发给所有非 host 参与者;被点名成员才唤醒;
136
+ - 聊天气泡显示发送者与收件人(worker-c · → host),用户侧为右气泡、agent 侧为左气泡;
137
+ - 后续:把 Room 状态下的 Chat 替换为这套界面(插件增量,不卸载系统 Chat)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-device-fleet",
3
- "version": "0.1.0-dev.24",
3
+ "version": "0.1.0-dev.25",
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",
@@ -40,6 +40,7 @@
40
40
  "files": [
41
41
  "src",
42
42
  "client.js",
43
+ "assets",
43
44
  "cordis.patch.yml",
44
45
  "README.md",
45
46
  "CONTEXT.md",
@@ -0,0 +1,52 @@
1
+ // Agent avatars used by the Room conversation (dev.25). Files live in the
2
+ // package's assets/avatars directory and are served on exact same-origin routes
3
+ // so the browser can render them without an auth-bypassing asset surface.
4
+ import { readFileSync } from 'node:fs'
5
+ import { join } from 'node:path'
6
+
7
+ export const AVATARS = {
8
+ host: 'host.png',
9
+ worker: 'worker.png',
10
+ user: 'user.png',
11
+ }
12
+
13
+ /**
14
+ * @param {object} options
15
+ * @param {object} options.webServer ctx.webServer
16
+ * @param {string} options.dir directory holding the avatar PNG files
17
+ * @returns {() => void} disposer
18
+ */
19
+ export function registerAvatarRoutes({ webServer, dir, logger = null }) {
20
+ if (!webServer?.register || !dir) return () => {}
21
+ const disposers = []
22
+ for (const [name, file] of Object.entries(AVATARS)) {
23
+ disposers.push(
24
+ webServer.register({
25
+ kind: 'exact',
26
+ path: `/device-fleet/avatar/${name}`,
27
+ handler: async (req, res) => {
28
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
29
+ res.statusCode = 405
30
+ res.end()
31
+ return
32
+ }
33
+ try {
34
+ const data = readFileSync(join(dir, file))
35
+ res.statusCode = 200
36
+ res.setHeader('content-type', 'image/png')
37
+ res.setHeader('cache-control', 'public, max-age=3600')
38
+ res.setHeader('content-length', String(data.length))
39
+ res.end(req.method === 'HEAD' ? undefined : data)
40
+ } catch {
41
+ logger?.warn?.(`[device-fleet] avatar missing: ${file}`)
42
+ res.statusCode = 404
43
+ res.end()
44
+ }
45
+ },
46
+ }),
47
+ )
48
+ }
49
+ return () => {
50
+ for (const dispose of disposers) dispose()
51
+ }
52
+ }
package/src/host/index.js CHANGED
@@ -8,6 +8,7 @@ export * from './approval-inbox.js'
8
8
  export * from './approval-web.js'
9
9
  export * from './ui-web.js'
10
10
  export * from './config-web.js'
11
+ export * from './avatar-web.js'
11
12
  export * from './runtime-config.js'
12
13
  export * from './room-service.js'
13
14
  export * from './tools.js'
@@ -20,6 +20,7 @@ import { RoomService } from '../host/room-service.js'
20
20
  import { registerApprovalRoutes } from '../host/approval-web.js'
21
21
  import { registerUiRoutes } from '../host/ui-web.js'
22
22
  import { registerConfigRoutes } from '../host/config-web.js'
23
+ import { registerAvatarRoutes } from '../host/avatar-web.js'
23
24
  import { RuntimeConfig, deepMerge } from '../host/runtime-config.js'
24
25
  import { ExecutionSlot, DEFAULT_SHELL_TOOL } from '../host/execution-slot.js'
25
26
  import { ToolPolicy } from '../host/tool-policy.js'
@@ -280,6 +281,13 @@ export function apply(ctx, profileConfig) {
280
281
  }
281
282
  let pairingRef = null
282
283
  if (webServer) {
284
+ ctx.effect(() =>
285
+ registerAvatarRoutes({
286
+ webServer,
287
+ dir: fileURLToPath(new URL('../../assets/avatars/', import.meta.url)),
288
+ logger,
289
+ }),
290
+ )
283
291
  ctx.effect(() => registerApprovalRoutes({ webServer, inbox, logger }))
284
292
  ctx.effect(() =>
285
293
  registerConfigRoutes({