@dcrays/dcgchat 0.4.9 → 0.4.12

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/index.ts CHANGED
@@ -18,7 +18,6 @@ const plugin = {
18
18
  api.registerChannel({ plugin: dcgchatPlugin })
19
19
  setWorkspaceDir(api.config?.agents?.defaults?.workspace)
20
20
  api.registerTool((ctx) => {
21
- setWorkspaceDir(ctx.workspaceDir)
22
21
  return createDcgchatMessageTool(ctx)
23
22
  })
24
23
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dcrays/dcgchat",
3
- "version": "0.4.9",
3
+ "version": "0.4.12",
4
4
  "type": "module",
5
5
  "description": "OpenClaw channel plugin for 书灵墨宝 (WebSocket)",
6
6
  "main": "index.ts",
package/src/agent.ts CHANGED
@@ -53,7 +53,10 @@ function copyAgentsFiles(clone_code: string) {
53
53
  export async function onCreateAgent(params: Record<string, any>) {
54
54
  const { clone_code, name, description } = params
55
55
  try {
56
- await sendMessageToGateway(JSON.stringify({ method: 'agents.create', params: { name: clone_code, workspace: clone_code } }))
56
+ const workspacePath = getWorkspaceDir()
57
+ if (!workspacePath) return
58
+ const workspaceDir = path.join(workspacePath, '../', `workspace-${clone_code}`)
59
+ await sendMessageToGateway(JSON.stringify({ method: 'agents.create', params: { name: clone_code, workspace: workspaceDir } }))
57
60
  } catch (err: unknown) {
58
61
  dcgLogger(`agents.create failed: ${String(err)}`, 'error')
59
62
  }
package/src/bot.ts CHANGED
@@ -47,7 +47,15 @@ export function extractAgentIdFromConversationId(conversationId: string): string
47
47
  if (idx <= 0) return null
48
48
  return conversationId.slice(0, idx)
49
49
  }
50
-
50
+ function formatText(text: string): string {
51
+ if (!text) return ''
52
+ const str = String(text).replace(/\s/g, '')
53
+ if (!str) return ''
54
+ if (str.length <= 50) {
55
+ return str
56
+ }
57
+ return str.slice(0, 25) + `...[此处省略${str.length - 50}字]....` + str.slice(-25)
58
+ }
51
59
  async function resolveMediaFromUrls(files: TFileInfo[], botToken: string): Promise<MediaInfo[]> {
52
60
  const core = getDcgchatRuntime()
53
61
  const out: MediaInfo[] = []
@@ -274,6 +282,7 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
274
282
  sentMediaKeys.add(key)
275
283
  await sendDcgchatMedia({ sessionKey: dcgSessionKey, mediaUrl, text: '' })
276
284
  }
285
+ dcgLogger(`[deliver]: len=${payload?.text?.length} sessionId=${outboundCtx.sessionId} ${formatText(payload?.text ?? '')}`)
277
286
  },
278
287
  onError: (err: unknown, info: { kind: string }) => {
279
288
  setMsgStatus(dcgSessionKey, 'finished')
@@ -397,9 +406,6 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
397
406
  const prev = streamChunkIdxBySessionKey.get(dcgSessionKey) ?? 0
398
407
  streamChunkIdxBySessionKey.set(dcgSessionKey, prev + 1)
399
408
  sendChunk(delta, outboundCtx, prev)
400
- dcgLogger(
401
- `[stream]: chunkIdx=${prev} len=${delta.length} sessionId=${outboundCtx.sessionId} ${delta.slice(0, 100)}`
402
- )
403
409
  }
404
410
  streamedTextLen = payload.text.length
405
411
  } else {
package/src/channel.ts CHANGED
@@ -69,6 +69,12 @@ function normalizeSessionTarget(rawTo: string): string {
69
69
  return getParamsMessage(cleaned)?.sessionKey?.trim() || cleaned
70
70
  }
71
71
 
72
+ /** 出站返回的 chatId:含 `dcg-cron:` 时保留原始 `to`,便于下游识别定时投递 */
73
+ function outboundChatId(rawTo: string | undefined, normalizedTo: string): string {
74
+ const raw = rawTo?.trim() ?? ''
75
+ return raw.indexOf('dcg-cron:') >= 0 ? raw : normalizedTo
76
+ }
77
+
72
78
  export async function sendDcgchatMedia(opts: DcgchatMediaSendOptions): Promise<void> {
73
79
  const sessionKey = normalizeSessionTarget(opts.sessionKey ?? '')
74
80
  const msgCtx = getOutboundMsgParams(sessionKey)
@@ -224,8 +230,8 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
224
230
  },
225
231
  agentPrompt: {
226
232
  messageToolHints: () => [
227
- '书灵墨宝 / 内置 `message`:**优先不要传 `target`**,即回复当前会话;OpenClaw 会用工具上下文里的 `currentChannelId`(与入站 `To` / `SessionKey` / `OriginatingTo` 相同)。',
228
- '仅在需要显式指定时:`target` 必须与上下文中的 **完整 SessionKey 字符串逐字一致**(形如 `agent:…:mobook:direct:…` 或以 `agent:` 开头的路由键)。**禁止**填 `From`、`SenderId`、通道配置里的 WS `userId`、会话 id 纯数字等。',
233
+ '书灵墨宝 / 内置 `message`:回复当前会话时 **不要传 `target`**,由 OpenClaw 使用工具上下文里的 `currentChannelId`(来自入站 `To`,即当前 SessionKey)。',
234
+ '必须指定会话时:`target` 须为上下文中出现的 **整段 SessionKey,逐字一致**(如 `agent:…:mobook:direct:…` 或以 `agent:` 开头的路由键);**禁止**使用 `userId`、`From`、纯数字会话号等代替。',
229
235
  '生成文件后,**尽可能不要**把文件路径、地址直接告诉用户;把文件名告诉用户;须通过工具发文件,勿在正文里直接输出可访问路径。',
230
236
  '使用 `dcgchat_message` 时同样遵守上述 SessionKey 规则(该工具通常由插件注入当前会话,一般无需自造 target)。'
231
237
  ]
@@ -273,7 +279,7 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
273
279
  return {
274
280
  channel: "dcgchat",
275
281
  messageId: `${messageId}`,
276
- chatId: to
282
+ chatId: outboundChatId(ctx.to, to)
277
283
  }
278
284
  },
279
285
  sendMedia: async (ctx) => {
@@ -290,7 +296,7 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
290
296
  return {
291
297
  channel: "dcgchat",
292
298
  messageId,
293
- chatId: to || ''
299
+ chatId: outboundChatId(ctx.to, to || '')
294
300
  }
295
301
  }
296
302
 
@@ -299,7 +305,7 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
299
305
  return {
300
306
  channel: "dcgchat",
301
307
  messageId,
302
- chatId: to || ''
308
+ chatId: outboundChatId(ctx.to, to || '')
303
309
  }
304
310
  }
305
311
  },
@@ -132,13 +132,14 @@ function injectBestEffort(params: Record<string, unknown>, sk: string): Record<s
132
132
  return newParams
133
133
  }
134
134
 
135
- // job.delivery
135
+ // job.delivery(须改 job 内对象,勿写 newParams.delivery——顶层可能不存在)
136
136
  const job = newParams.job as Record<string, unknown> | undefined
137
137
  if (job?.delivery && typeof job.delivery === 'object') {
138
- ;(job.delivery as CronDelivery).bestEffort = true
139
- ;(newParams.delivery as CronDelivery).to = `dcg-cron:${sk}`
140
- ;(newParams.delivery as CronDelivery).accountId = agentId
141
- ;(newParams.delivery as CronDelivery).channel = "dcgchat"
138
+ const del = job.delivery as CronDelivery
139
+ del.bestEffort = true
140
+ del.to = `dcg-cron:${sk}`
141
+ del.accountId = agentId
142
+ del.channel = "dcgchat"
142
143
  newParams.sessionKey = sk
143
144
  return newParams
144
145
  }
package/src/skill.ts CHANGED
@@ -34,14 +34,13 @@ export async function installSkill(params: ISkillParams, msgContent: Record<stri
34
34
  const { path: cdnUrl, code } = params
35
35
  const workspacePath = getWorkspaceDir()
36
36
 
37
- const skillDir = path.join(workspacePath, 'skills', code)
38
-
39
37
  // 确保 skills 目录存在
40
38
  const skillsDir = path.join(workspacePath, 'skills')
41
39
  if (!fs.existsSync(skillsDir)) {
42
40
  fs.mkdirSync(skillsDir, { recursive: true })
43
41
  }
44
42
  // 如果目标目录已存在,先删除
43
+ const skillDir = path.join(workspacePath, 'skills', code)
45
44
  if (fs.existsSync(skillDir)) {
46
45
  fs.rmSync(skillDir, { recursive: true, force: true })
47
46
  }
@@ -15,7 +15,6 @@ export function handleGatewayEventMessage(msg: { event?: string; payload?: Recor
15
15
  const outboundCtx = getEffectiveMsgParams(sessionKey)
16
16
  if (pl.data?.delta) {
17
17
  if (outboundCtx.sessionId) {
18
- dcgLogger(`[Gateway] 收到agent事件: ${JSON.stringify(msg).slice(0, 100)}`)
19
18
  sendChunk(pl.data.delta as string, outboundCtx, 0)
20
19
  }
21
20
  }