@dcrays/dcgchat-test 0.5.0-alpha.2 → 0.5.0-alpha.3

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.
Files changed (42) hide show
  1. package/index.js +292 -0
  2. package/package.json +7 -15
  3. package/schemas/gateway-cron-finished.payload.json +39 -0
  4. package/index.ts +0 -24
  5. package/src/agent.ts +0 -128
  6. package/src/bot.ts +0 -515
  7. package/src/channel.ts +0 -474
  8. package/src/cron.ts +0 -199
  9. package/src/cronToolCall.ts +0 -202
  10. package/src/gateway/cronFinishedPayload.ts +0 -118
  11. package/src/gateway/index.ts +0 -452
  12. package/src/gateway/security.ts +0 -95
  13. package/src/gateway/socket.ts +0 -285
  14. package/src/libs/ali-oss-6.23.0.tgz +0 -0
  15. package/src/libs/axios-1.13.6.tgz +0 -0
  16. package/src/libs/md5-2.3.0.tgz +0 -0
  17. package/src/libs/mime-types-3.0.2.tgz +0 -0
  18. package/src/libs/unzipper-0.12.3.tgz +0 -0
  19. package/src/libs/ws-8.19.0.tgz +0 -0
  20. package/src/monitor.ts +0 -165
  21. package/src/request/api.ts +0 -70
  22. package/src/request/oss.ts +0 -212
  23. package/src/request/request.ts +0 -192
  24. package/src/request/userInfo.ts +0 -93
  25. package/src/session.ts +0 -19
  26. package/src/sessionTermination.ts +0 -168
  27. package/src/skill.ts +0 -146
  28. package/src/tool.ts +0 -403
  29. package/src/tools/messageTool.ts +0 -273
  30. package/src/transport.ts +0 -206
  31. package/src/types.ts +0 -139
  32. package/src/utils/agentErrors.ts +0 -23
  33. package/src/utils/constant.ts +0 -7
  34. package/src/utils/gatewayMsgHanlder.ts +0 -84
  35. package/src/utils/global.ts +0 -161
  36. package/src/utils/log.ts +0 -15
  37. package/src/utils/params.ts +0 -88
  38. package/src/utils/searchFile.ts +0 -228
  39. package/src/utils/workspaceFilePaths.ts +0 -89
  40. package/src/utils/wsMessageHandler.ts +0 -64
  41. package/src/utils/zipExtract.ts +0 -97
  42. package/src/utils/zipPath.ts +0 -24
@@ -1,202 +0,0 @@
1
- import { channelInfo, ENV } from './utils/constant.js'
2
- /**
3
- * cron-delivery-guard — 定时任务投递守护插件
4
- *
5
- * 核心机制:通过 before_tool_call 钩子拦截 cron 工具调用,
6
- * 当 delivery.mode 为 "announce" 且未指定 channel 时,
7
- * 自动注入 bestEffort: true,使投递失败时静默降级,
8
- * 不影响 cron 执行结果的保存。
9
- *
10
- * 背景:
11
- * - 定时任务的 delivery 设为 announce 模式,如果没有指定 channel,
12
- * 投递可能因找不到有效渠道而失败
13
- * - bestEffort: true 让框架在投递失败时不报错,避免丢失执行结果
14
- */
15
-
16
- import { dcgLogger } from './utils/log.js'
17
-
18
- const LOG_TAG = 'cron-delivery-guard'
19
-
20
- // ---- 类型定义 ----
21
-
22
- interface ToolCallEvent {
23
- toolName: string
24
- toolCallId: string
25
- params: Record<string, unknown>
26
- result?: { content: string }
27
- }
28
-
29
- interface HookContext {
30
- agentId: string
31
- sessionKey: string
32
- }
33
-
34
- interface BeforeToolCallResult {
35
- block?: boolean
36
- blockReason?: string
37
- params?: Record<string, unknown>
38
- }
39
-
40
- // ---- delivery 类型 ----
41
-
42
- interface CronDelivery {
43
- mode?: string
44
- channel?: string
45
- to?: string
46
- bestEffort?: boolean
47
- [key: string]: unknown
48
- }
49
- /**
50
- * 解析 OpenClaw mobook direct 会话 key。
51
- * 形如 `agent:main:mobook:direct:14:5466`(大小写不敏感,与路由 toLowerCase 一致)
52
- * - 倒数第二段:account / peer(delivery.accountId)
53
- * - 最后一段:会话 id(delivery.to)
54
- */
55
- export function formatterSessionKey(sessionKey: string): { agentId: string; sessionId: string } {
56
- const parts = sessionKey.split(':').filter((s) => s.length > 0)
57
- const norm = parts.map((s) => s.toLowerCase())
58
- if (parts.length >= 6 && norm[0] === 'agent' && norm[2] === 'mobook' && norm[3] === 'direct') {
59
- return {
60
- agentId: parts[4] ?? '',
61
- sessionId: parts[5] ?? ''
62
- }
63
- }
64
- if (parts.length >= 2) {
65
- return {
66
- agentId: parts[parts.length - 2] ?? '',
67
- sessionId: parts[parts.length - 1] ?? ''
68
- }
69
- }
70
- return { agentId: '', sessionId: '' }
71
- }
72
-
73
- // ---- 辅助函数 ----
74
-
75
- /**
76
- * 判断是否为 cron 工具调用
77
- */
78
- function isCronTool(toolName: string): boolean {
79
- return toolName === 'cron'
80
- }
81
-
82
- /**
83
- * 从 cron 参数中提取 delivery 配置
84
- * cron 工具的参数结构可能是:
85
- * - params.delivery (顶层)
86
- * - params.job.delivery (嵌套在 job 中)
87
- */
88
- function extractDelivery(params: Record<string, unknown>): CronDelivery | null {
89
- // 尝试顶层 delivery
90
- if (params.delivery && typeof params.delivery === 'object') {
91
- return params.delivery as CronDelivery
92
- }
93
-
94
- // 尝试 job.delivery
95
- const job = params.job as Record<string, unknown> | undefined
96
- if (job?.delivery && typeof job.delivery === 'object') {
97
- return job.delivery as CronDelivery
98
- }
99
-
100
- // 尝试 payload 中的 deliver 相关字段 (兼容 qqbot-cron 风格)
101
- const payload = job?.payload as Record<string, unknown> | undefined
102
- if (payload?.deliver === true && payload.channel === undefined) {
103
- // payload 风格: { deliver: true, channel?: string }
104
- // 这种情况不是 delivery 对象,跳过
105
- return null
106
- }
107
-
108
- return null
109
- }
110
-
111
- /**
112
- * 判断 delivery 是否需要注入 bestEffort
113
- * 条件: mode 为 "announce" 且没有 channel
114
- */
115
- function needsBestEffort(delivery: CronDelivery): boolean {
116
- return delivery.mode === 'announce' && !delivery.channel && !delivery.bestEffort
117
- }
118
-
119
- /**
120
- * 深拷贝 params,在 delivery 上写入 dcg 路由(to / accountId / sessionKey)。
121
- * bestEffort + 默认 channel 仅在 needsBestEffort 为真时写入(announce 且无 channel)。
122
- *
123
- * 说明:原先仅在 needsBestEffort 为真时才改 delivery,若 jobs 里已有 channel 会整段跳过,
124
- * 导致 `delivery.to` 永远不会被本钩子写入;运行时若缺 to 就会一直 not-delivered。
125
- */
126
- function patchCronDeliveryInParams(
127
- params: Record<string, unknown>,
128
- sk: string,
129
- deliverySnapshot: CronDelivery
130
- ): Record<string, unknown> | null {
131
- const newParams = JSON.parse(JSON.stringify(params)) as Record<string, unknown>
132
- const { agentId } = formatterSessionKey(sk)
133
- const announceNoChannel = needsBestEffort(deliverySnapshot)
134
-
135
- const apply = (d: CronDelivery) => {
136
- d.to = `dcg-cron:${sk}`
137
- if (agentId) d.accountId = agentId
138
- if (announceNoChannel) {
139
- d.bestEffort = true
140
- d.channel = "dcgchat-test"
141
- }
142
- }
143
-
144
- if (newParams.delivery && typeof newParams.delivery === 'object') {
145
- apply(newParams.delivery as CronDelivery)
146
- newParams.sessionKey = sk
147
- return newParams
148
- }
149
-
150
- const job = newParams.job as Record<string, unknown> | undefined
151
- if (job?.delivery && typeof job.delivery === 'object') {
152
- apply(job.delivery as CronDelivery)
153
- newParams.sessionKey = sk
154
- return newParams
155
- }
156
-
157
- return null
158
- }
159
-
160
- export function cronToolCall(event: { toolName: any; params: any; toolCallId: any }, sk: string) {
161
- const { toolName, params, toolCallId } = event
162
-
163
- // 仅处理 cron 工具
164
- if (isCronTool(toolName)) {
165
- const delivery = extractDelivery(params)
166
- if (!delivery) {
167
- dcgLogger(`[${LOG_TAG}] cron call (${toolCallId}) has no delivery config, skip.`)
168
- return undefined
169
- }
170
- const newParams = patchCronDeliveryInParams(params, sk, delivery)
171
- if (!newParams) {
172
- dcgLogger(`[${LOG_TAG}] cron call (${toolCallId}) could not locate delivery on params clone, skip.`)
173
- return undefined
174
- }
175
-
176
- const patched = newParams.delivery ?? (newParams.job as Record<string, unknown>)?.delivery
177
- if (needsBestEffort(delivery)) {
178
- dcgLogger(
179
- `[${LOG_TAG}] cron call (${toolCallId}) patched delivery (announce, no channel): bestEffort=true, ` +
180
- `delivery=${JSON.stringify(patched)}`
181
- )
182
- } else {
183
- dcgLogger(
184
- `[${LOG_TAG}] cron call (${toolCallId}) patched delivery.to / accountId (sessionKey=${sk}), ` +
185
- `delivery=${JSON.stringify(patched)}`
186
- )
187
- }
188
-
189
- return { params: newParams }
190
- } else if (toolName === 'exec') {
191
- if (params.command.indexOf('cron create') > -1 || params.command.indexOf('cron add') > -1) {
192
- const newParams = JSON.parse(JSON.stringify(params)) as Record<string, unknown>
193
- newParams.command =
194
- params.command.replace('--json', '') + ` --session-key ${sk} --channel ${"dcgchat-test"} --to dcg-cron:${sk} --json`
195
- return { params: newParams }
196
- } else {
197
- return undefined
198
- }
199
- }
200
-
201
- return undefined
202
- }
@@ -1,118 +0,0 @@
1
- /**
2
- * 与 `schemas/gateway-cron-finished.payload.json` 对齐:`attachments` 为首选。
3
- * 若宿主未下发 `attachments`(当前 Gateway 常见情况),则仅在 `summary` 中按工作区规则提取路径(见 `resolveCronFinishedLocalPaths`),仍经 `sendDcgchatMedia` 发送。
4
- */
5
-
6
- import {
7
- expandTildePath,
8
- extractWorkspaceFilePathsFromText,
9
- isAllowedSendPath,
10
- trimArtifactPathCandidate
11
- } from '../utils/workspaceFilePaths.js'
12
-
13
- export type CronFinishedAttachmentItem = string | { path?: string; file?: string }
14
-
15
- /** 网关 `event: "cron"` 且 `action === "finished"` 时插件消费的 payload 子集 */
16
- export type CronGatewayFinishedPayload = {
17
- jobId?: string
18
- action?: string
19
- status?: string
20
- summary?: string
21
- delivered?: boolean
22
- deliveryStatus?: string
23
- sessionId?: string
24
- sessionKey?: string
25
- attachments?: CronFinishedAttachmentItem[]
26
- }
27
-
28
- /** JSON Schema 中 `attachments` 数组片段(便于在代码里引用、与仓库内 JSON 文件保持语义一致) */
29
- export const CRON_FINISHED_ATTACHMENTS_ITEMS_SCHEMA = {
30
- type: 'array',
31
- description: '本轮定时任务产出的本地文件路径,按顺序经通道发送为附件',
32
- items: {
33
- oneOf: [
34
- { type: 'string', minLength: 1, description: '绝对路径' },
35
- {
36
- type: 'object',
37
- additionalProperties: false,
38
- properties: {
39
- path: { type: 'string', minLength: 1 },
40
- file: { type: 'string', minLength: 1 }
41
- },
42
- description: '优先 path,否则 file'
43
- }
44
- ]
45
- }
46
- } as const
47
-
48
- function pushAttachmentItem(item: unknown, out: string[]): void {
49
- if (typeof item === 'string') {
50
- const s = item.trim()
51
- if (s) out.push(s)
52
- return
53
- }
54
- if (item && typeof item === 'object') {
55
- const o = item as Record<string, unknown>
56
- const p = typeof o.path === 'string' ? o.path.trim() : ''
57
- const f = typeof o.file === 'string' ? o.file.trim() : ''
58
- const s = p || f
59
- if (s) out.push(s)
60
- }
61
- }
62
-
63
- /**
64
- * 从网关 payload 取出 `attachments` 中的路径字符串(去重保序)。
65
- * 不解析 `summary`、不扫描 Markdown。
66
- */
67
- export function normalizeCronFinishedAttachmentPaths(payload: unknown): string[] {
68
- if (payload == null || typeof payload !== 'object') return []
69
- const p = payload as CronGatewayFinishedPayload
70
- const raw = p.attachments
71
- if (!Array.isArray(raw)) return []
72
- const acc: string[] = []
73
- for (const item of raw) {
74
- pushAttachmentItem(item, acc)
75
- }
76
- const seen = new Set<string>()
77
- const deduped: string[] = []
78
- for (const s of acc) {
79
- if (seen.has(s)) continue
80
- seen.add(s)
81
- deduped.push(s)
82
- }
83
- return deduped
84
- }
85
-
86
- function dedupePaths(paths: string[]): string[] {
87
- const seen = new Set<string>()
88
- const out: string[] = []
89
- for (const s of paths) {
90
- if (!s || seen.has(s)) continue
91
- seen.add(s)
92
- out.push(s)
93
- }
94
- return out
95
- }
96
-
97
- /**
98
- * 解析本轮待经通道发送的本地路径:优先 `payload.attachments`;
99
- * 若为空则用 `summary` 与 `extractWorkspaceFilePathsFromText` 一致的工作区/挂载规则提取(非任意全文扫描)。
100
- */
101
- export function resolveCronFinishedLocalPaths(
102
- payload: unknown,
103
- summary: string | undefined,
104
- workspaceDir: string,
105
- allowedPaths?: string[]
106
- ): string[] {
107
- const fromSchema = normalizeCronFinishedAttachmentPaths(payload)
108
- .map((s) => expandTildePath(trimArtifactPathCandidate(s)))
109
- .filter(Boolean)
110
- const schemaOk = fromSchema.filter((p: string) => isAllowedSendPath(p, workspaceDir, allowedPaths))
111
- if (schemaOk.length > 0) {
112
- return dedupePaths(schemaOk)
113
- }
114
- const fromSummary = extractWorkspaceFilePathsFromText(summary, workspaceDir).filter((p: string) =>
115
- isAllowedSendPath(p, workspaceDir, allowedPaths)
116
- )
117
- return dedupePaths(fromSummary)
118
- }