@dcrays/dcgchat-test 0.3.41 → 0.4.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/package.json +1 -1
- package/src/agent.ts +15 -23
- package/src/bot.ts +1 -1
- package/src/session.ts +2 -2
- package/src/tools/messageTool.ts +48 -18
- package/src/utils/wsMessageHandler.ts +2 -2
package/package.json
CHANGED
package/src/agent.ts
CHANGED
|
@@ -32,20 +32,12 @@ function sendEvent(msgContent: Record<string, any>) {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
workspace: string
|
|
38
|
-
name?: string
|
|
39
|
-
description?: string
|
|
40
|
-
msgContent?: Record<string, any>
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/** 若 workspace-${code}/agent 存在,则复制到 agents/${code}/agent */
|
|
44
|
-
function copyAgentsFiles(code: string) {
|
|
35
|
+
/** 若 workspace-${clone_code}/agent 存在,则复制到 agents/${clone_code}/agent */
|
|
36
|
+
function copyAgentsFiles(clone_code: string) {
|
|
45
37
|
const workspacePath = getWorkspaceDir()
|
|
46
38
|
if (!workspacePath) return
|
|
47
|
-
const workspaceDir = path.join(workspacePath, '../', `workspace-${
|
|
48
|
-
const agentDir = path.join(workspacePath, '../', `agents/${
|
|
39
|
+
const workspaceDir = path.join(workspacePath, '../', `workspace-${clone_code}`)
|
|
40
|
+
const agentDir = path.join(workspacePath, '../', `agents/${clone_code}`)
|
|
49
41
|
const sourceAgent = path.join(workspaceDir, 'agent')
|
|
50
42
|
try {
|
|
51
43
|
if (!fs.existsSync(sourceAgent)) return
|
|
@@ -62,15 +54,15 @@ function copyAgentsFiles(code: string) {
|
|
|
62
54
|
}
|
|
63
55
|
|
|
64
56
|
export async function onCreateAgent(params: Record<string, any>) {
|
|
65
|
-
const {
|
|
57
|
+
const { clone_code, name, description } = params
|
|
66
58
|
try {
|
|
67
|
-
await sendMessageToGateway(JSON.stringify({ method: 'agents.create', params: { name:
|
|
59
|
+
await sendMessageToGateway(JSON.stringify({ method: 'agents.create', params: { name: clone_code, workspace: clone_code } }))
|
|
68
60
|
} catch (err: unknown) {
|
|
69
61
|
dcgLogger(`agents.create failed: ${String(err)}`, 'error')
|
|
70
62
|
}
|
|
71
63
|
// Update config.name to the user-supplied display name (may contain CJK, spaces, etc.)
|
|
72
64
|
try {
|
|
73
|
-
await sendMessageToGateway(JSON.stringify({ method: 'agents.update', params: { name: name, agentId:
|
|
65
|
+
await sendMessageToGateway(JSON.stringify({ method: 'agents.update', params: { name: name, agentId: clone_code } }))
|
|
74
66
|
} catch (err: unknown) {
|
|
75
67
|
dcgLogger(`agents.update failed: ${String(err)}`, 'error')
|
|
76
68
|
}
|
|
@@ -79,7 +71,7 @@ export async function onCreateAgent(params: Record<string, any>) {
|
|
|
79
71
|
await sendMessageToGateway(
|
|
80
72
|
JSON.stringify({
|
|
81
73
|
method: 'agents.files.set',
|
|
82
|
-
params: { agentId:
|
|
74
|
+
params: { agentId: clone_code, name: 'IDENTITY.md', content: description.trim() }
|
|
83
75
|
})
|
|
84
76
|
)
|
|
85
77
|
} catch {
|
|
@@ -91,26 +83,26 @@ export async function onCreateAgent(params: Record<string, any>) {
|
|
|
91
83
|
await sendMessageToGateway(
|
|
92
84
|
JSON.stringify({
|
|
93
85
|
method: 'agents.files.set',
|
|
94
|
-
params: { agentId:
|
|
86
|
+
params: { agentId: clone_code, name: 'USER.md', content: name.trim() }
|
|
95
87
|
})
|
|
96
88
|
)
|
|
97
89
|
} catch {
|
|
98
90
|
// Non-fatal
|
|
99
91
|
}
|
|
100
92
|
}
|
|
101
|
-
copyAgentsFiles(
|
|
93
|
+
copyAgentsFiles(clone_code)
|
|
102
94
|
sendEvent({ ...params, status: 'ok' })
|
|
103
95
|
}
|
|
104
96
|
|
|
105
97
|
export async function createAgent(msgContent: Record<string, any>) {
|
|
106
|
-
const { url,
|
|
107
|
-
if (!url || !
|
|
108
|
-
dcgLogger(`createAgent failed empty url&
|
|
98
|
+
const { url, clone_code } = msgContent
|
|
99
|
+
if (!url || !clone_code) {
|
|
100
|
+
dcgLogger(`createAgent failed empty url&clone_code: ${JSON.stringify(msgContent)}`, 'error')
|
|
109
101
|
sendEvent({ ...msgContent, status: 'fail' })
|
|
110
102
|
return
|
|
111
103
|
}
|
|
112
104
|
const workspacePath = getWorkspaceDir()
|
|
113
|
-
const workspaceDir = path.join(workspacePath, '../', `workspace-${
|
|
105
|
+
const workspaceDir = path.join(workspacePath, '../', `workspace-${clone_code}`)
|
|
114
106
|
|
|
115
107
|
// 如果目标目录已存在,先删除
|
|
116
108
|
if (fs.existsSync(workspaceDir)) {
|
|
@@ -159,7 +151,7 @@ export async function createAgent(msgContent: Record<string, any>) {
|
|
|
159
151
|
return
|
|
160
152
|
}
|
|
161
153
|
|
|
162
|
-
const targetPath = path.join(
|
|
154
|
+
const targetPath = path.join(workspaceDir, newPath)
|
|
163
155
|
|
|
164
156
|
if (entry.type === 'Directory') {
|
|
165
157
|
fs.mkdirSync(targetPath, { recursive: true })
|
package/src/bot.ts
CHANGED
|
@@ -238,7 +238,7 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
|
|
|
238
238
|
if (msg.content.skills_scope.length > 0) {
|
|
239
239
|
const workspaceDir = getWorkspaceDir()
|
|
240
240
|
const skillCode = msg.content.skills_scope.map((skill) => `${workspaceDir}/skills/${skill.skill_code}`).join('\n')
|
|
241
|
-
const skillText =
|
|
241
|
+
const skillText = `技能${skillCode} 在目录${skillCode}下,在目录${skillCode}下查找技能 \n`
|
|
242
242
|
text = skillText ? `${skillText} \n ${text}` : text
|
|
243
243
|
}
|
|
244
244
|
const prefixContext = createReplyPrefixContext({
|
package/src/session.ts
CHANGED
|
@@ -6,11 +6,11 @@ interface TSession {
|
|
|
6
6
|
agent_id: string
|
|
7
7
|
session_id: string
|
|
8
8
|
agent_clone_code?: string
|
|
9
|
-
account_id
|
|
9
|
+
account_id: string
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export const onRemoveSession = async ({ agent_id, session_id, agent_clone_code, account_id }: TSession) => {
|
|
13
|
-
const sessionKey = getSessionKey({ agent_id, session_id }, account_id)
|
|
13
|
+
const sessionKey = getSessionKey({ agent_id, session_id, agent_clone_code }, account_id)
|
|
14
14
|
if (!session_id) {
|
|
15
15
|
dcgLogger('onRemoveSession: empty session_id', 'error')
|
|
16
16
|
return
|
package/src/tools/messageTool.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
|
+
import os from 'node:os'
|
|
2
3
|
import path from 'node:path'
|
|
3
4
|
import type { AnyAgentTool } from 'openclaw/plugin-sdk'
|
|
4
5
|
import { jsonResult } from 'openclaw/plugin-sdk'
|
|
@@ -12,7 +13,28 @@ export type DcgchatMessageToolContext = {
|
|
|
12
13
|
workspaceDir?: string
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
/** 统一为 POSIX 风格斜杠,便于跨平台判断(不改变语义,仅用于匹配)。 */
|
|
17
|
+
function toPosixPath(p: string): string {
|
|
18
|
+
return path.normalize(p.trim()).replace(/\\/g, '/')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 允许发送的挂载根:Unix/macOS/Linux 为 /workspace、/mobook;
|
|
23
|
+
* Windows 为盘符路径如 C:/workspace、D:\\mobook\\...(规范化后比较)。
|
|
24
|
+
*/
|
|
25
|
+
function isSafePath(filepath: string): boolean {
|
|
26
|
+
const p = toPosixPath(filepath)
|
|
27
|
+
if (p.startsWith('/workspace/') || p === '/workspace') return true
|
|
28
|
+
if (p.startsWith('/mobook/') || p === '/mobook') return true
|
|
29
|
+
// Windows: C:/workspace/...、c:/mobook/...
|
|
30
|
+
return /^[A-Za-z]:\/(workspace|mobook)(\/|$)/.test(p)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** 同一路径在 Windows 上可能大小写不同,用于 Set 去重。 */
|
|
34
|
+
function pathKey(filepath: string): string {
|
|
35
|
+
const n = path.normalize(filepath.trim())
|
|
36
|
+
return os.platform() === 'win32' ? n.toLowerCase() : n
|
|
37
|
+
}
|
|
16
38
|
|
|
17
39
|
const fileType1 = ['.webp', '.gif', '.bmp', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.pdf', '.txt', '.rtf', '.odt', '.json']
|
|
18
40
|
const fileType2 = ['.xml', '.csv', '.yaml', '.yml', '.html', '.htm', '.md', '.markdown', '.css', '.js', '.ts', '.png', '.jpg', '.jpeg']
|
|
@@ -26,8 +48,7 @@ const messageToolParameters = {
|
|
|
26
48
|
properties: {
|
|
27
49
|
target: {
|
|
28
50
|
type: 'string',
|
|
29
|
-
description:
|
|
30
|
-
'目标会话键(sessionKey),必须与当前会话 SessionKey 一致,禁止填写 userId。'
|
|
51
|
+
description: '目标会话键(sessionKey),必须与当前会话 SessionKey 一致,禁止填写 userId。'
|
|
31
52
|
},
|
|
32
53
|
content: {
|
|
33
54
|
type: 'string',
|
|
@@ -42,7 +63,8 @@ const messageToolParameters = {
|
|
|
42
63
|
properties: {
|
|
43
64
|
file: {
|
|
44
65
|
type: 'string',
|
|
45
|
-
description:
|
|
66
|
+
description:
|
|
67
|
+
'文件路径。Unix/macOS/Linux 如 /workspace/output/report.pdf;Windows 如 C:/workspace/output/report.pdf 或 C:\\workspace\\output\\report.pdf'
|
|
46
68
|
}
|
|
47
69
|
},
|
|
48
70
|
required: ['file']
|
|
@@ -52,14 +74,12 @@ const messageToolParameters = {
|
|
|
52
74
|
oneOf: [{ required: ['content'] }, { required: ['media'] }]
|
|
53
75
|
}
|
|
54
76
|
|
|
55
|
-
function extractPaths(text?: string) {
|
|
77
|
+
function extractPaths(text?: string): string[] {
|
|
56
78
|
if (!text) return []
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
function isSafePath(filepath: string) {
|
|
62
|
-
return SAFE_PREFIXES.some((prefix) => filepath.startsWith(prefix))
|
|
79
|
+
const unix = text.match(/\/workspace\/[^\s]+|\/mobook\/[^\s]+/g) ?? []
|
|
80
|
+
// Windows: C:\workspace\...、C:/mobook/...(不含空白)
|
|
81
|
+
const win = text.match(/[A-Za-z]:[/\\](?:workspace|mobook)[/\\][^\s]+/g) ?? []
|
|
82
|
+
return [...new Set([...unix, ...win])]
|
|
63
83
|
}
|
|
64
84
|
|
|
65
85
|
function isSafeFile(filepath: string) {
|
|
@@ -67,7 +87,7 @@ function isSafeFile(filepath: string) {
|
|
|
67
87
|
const stat = fs.statSync(filepath)
|
|
68
88
|
if (!stat.isFile()) return false
|
|
69
89
|
if (stat.size === 0) return false
|
|
70
|
-
const ext = path.extname(filepath)
|
|
90
|
+
const ext = path.extname(filepath).toLowerCase()
|
|
71
91
|
return SAFE_EXTENSIONS.has(ext)
|
|
72
92
|
}
|
|
73
93
|
|
|
@@ -84,9 +104,7 @@ export function createDcgchatMessageTool(pluginCtx: DcgchatMessageToolContext):
|
|
|
84
104
|
向用户发送消息。
|
|
85
105
|
若传 target,target 必须是 sessionKey,不能是 userId。
|
|
86
106
|
如果发送附件:必须使用 media 字段
|
|
87
|
-
|
|
88
|
-
/workspace/
|
|
89
|
-
/mobook/
|
|
107
|
+
支持路径目录(Unix/macOS/Linux:/workspace/、/mobook/;Windows:盘符下 workspace、mobook,如 C:/workspace/):
|
|
90
108
|
禁止直接输出路径文本
|
|
91
109
|
`,
|
|
92
110
|
parameters: messageToolParameters,
|
|
@@ -104,6 +122,7 @@ export function createDcgchatMessageTool(pluginCtx: DcgchatMessageToolContext):
|
|
|
104
122
|
|
|
105
123
|
try {
|
|
106
124
|
const sentFiles = new Set<string>()
|
|
125
|
+
const sentKeys = new Set<string>()
|
|
107
126
|
|
|
108
127
|
if (args.media?.length) {
|
|
109
128
|
for (const media of args.media) {
|
|
@@ -111,10 +130,12 @@ export function createDcgchatMessageTool(pluginCtx: DcgchatMessageToolContext):
|
|
|
111
130
|
if (!filepath) continue
|
|
112
131
|
if (!isSafePath(filepath)) continue
|
|
113
132
|
if (!isSafeFile(filepath)) continue
|
|
114
|
-
|
|
133
|
+
const key = pathKey(filepath)
|
|
134
|
+
if (sentKeys.has(key)) continue
|
|
115
135
|
|
|
116
136
|
await sendDcgchatMedia({ sessionKey, mediaUrl: filepath })
|
|
117
137
|
sentFiles.add(filepath)
|
|
138
|
+
sentKeys.add(key)
|
|
118
139
|
}
|
|
119
140
|
}
|
|
120
141
|
|
|
@@ -122,15 +143,24 @@ export function createDcgchatMessageTool(pluginCtx: DcgchatMessageToolContext):
|
|
|
122
143
|
for (const filepath of fallbackPaths) {
|
|
123
144
|
if (!isSafePath(filepath)) continue
|
|
124
145
|
if (!isSafeFile(filepath)) continue
|
|
125
|
-
|
|
146
|
+
const key = pathKey(filepath)
|
|
147
|
+
if (sentKeys.has(key)) continue
|
|
126
148
|
|
|
127
149
|
await sendDcgchatMedia({ sessionKey, mediaUrl: filepath })
|
|
128
150
|
sentFiles.add(filepath)
|
|
151
|
+
sentKeys.add(key)
|
|
129
152
|
}
|
|
130
153
|
|
|
131
154
|
let content = args.content ?? ''
|
|
132
155
|
for (const filepath of sentFiles) {
|
|
133
|
-
|
|
156
|
+
const posix = toPosixPath(filepath)
|
|
157
|
+
const variants = posix === filepath ? [filepath] : [filepath, posix]
|
|
158
|
+
const seen = new Set<string>()
|
|
159
|
+
for (const v of variants) {
|
|
160
|
+
if (!v || seen.has(v)) continue
|
|
161
|
+
seen.add(v)
|
|
162
|
+
content = content.split(v).join('')
|
|
163
|
+
}
|
|
134
164
|
}
|
|
135
165
|
content = content.trim()
|
|
136
166
|
|
|
@@ -39,8 +39,8 @@ export async function handleParsedWsMessage(parsed: ParsedWsPayload, rawPayload:
|
|
|
39
39
|
dcgLogger(`openclaw_bot_event unknown event_type: ${event_type}, ${rawPayload}`)
|
|
40
40
|
}
|
|
41
41
|
} else if (event_type === 'agent') {
|
|
42
|
-
if (operation_type === '
|
|
43
|
-
createAgent(parsed.content)
|
|
42
|
+
if (operation_type === 'create') {
|
|
43
|
+
await createAgent(parsed.content)
|
|
44
44
|
} else {
|
|
45
45
|
dcgLogger(`openclaw_bot_event unknown event_type: ${event_type}, ${rawPayload}`)
|
|
46
46
|
}
|