@dcrays/dcgchat-test 0.6.2 → 0.6.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.
- package/index.js +304 -0
- package/openclaw.plugin.json +41 -12
- package/package.json +9 -36
- package/README.md +0 -167
- package/index.ts +0 -31
- package/src/agent.ts +0 -592
- package/src/bot.ts +0 -676
- package/src/channel/index.ts +0 -329
- package/src/channel/outboundMedia.ts +0 -144
- package/src/channel/outboundTarget.ts +0 -24
- package/src/channel/uploadMediaUrl.ts +0 -76
- package/src/cron/message.ts +0 -109
- package/src/cron/params.ts +0 -166
- package/src/cron/toolGuard.ts +0 -285
- package/src/cron/types.ts +0 -15
- package/src/gateway/index.ts +0 -430
- package/src/gateway/security.ts +0 -95
- package/src/gateway/socket.ts +0 -256
- package/src/libs/ali-oss-6.23.0.tgz +0 -0
- package/src/libs/axios-1.13.6.tgz +0 -0
- package/src/libs/md5-2.3.0.tgz +0 -0
- package/src/libs/mime-types-3.0.2.tgz +0 -0
- package/src/libs/unzipper-0.12.3.tgz +0 -0
- package/src/libs/ws-8.19.0.tgz +0 -0
- package/src/monitor.ts +0 -269
- package/src/request/api.ts +0 -80
- package/src/request/oss.ts +0 -200
- package/src/request/request.ts +0 -191
- package/src/request/userInfo.ts +0 -44
- package/src/session.ts +0 -28
- package/src/skill.ts +0 -114
- package/src/tool.ts +0 -625
- package/src/tools/messageTool.ts +0 -334
- package/src/tools/toolCallGuard.ts +0 -323
- package/src/transport.ts +0 -217
- package/src/types.ts +0 -139
- package/src/utils/agentErrors.ts +0 -23
- package/src/utils/constant.ts +0 -60
- package/src/utils/env-config.ts +0 -19
- package/src/utils/formatLlmInputEvent.ts +0 -48
- package/src/utils/gatewayMsgHandler.ts +0 -89
- package/src/utils/global.ts +0 -306
- package/src/utils/inboundTurnState.ts +0 -66
- package/src/utils/log.ts +0 -77
- package/src/utils/mediaAttached.ts +0 -244
- package/src/utils/mediaEmitter.ts +0 -54
- package/src/utils/outboundAssistantText.ts +0 -110
- package/src/utils/params.ts +0 -104
- package/src/utils/passTxt.ts +0 -4
- package/src/utils/resolveRegisterConfig.ts +0 -33
- package/src/utils/searchFile.ts +0 -228
- package/src/utils/sessionState.ts +0 -137
- package/src/utils/sessionTermination.ts +0 -228
- package/src/utils/streamMerge.ts +0 -150
- package/src/utils/subagentRunMap.ts +0 -71
- package/src/utils/workspaceFilePaths.ts +0 -18
- package/src/utils/wsMessageHandler.ts +0 -124
- package/src/utils/zipExtract.ts +0 -97
- package/src/utils/zipPath.ts +0 -24
package/src/cron/params.ts
DELETED
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import { PluginHookAfterToolCallEvent } from 'openclaw/plugin-sdk/plugin-runtime'
|
|
2
|
-
import { dcgLogger } from '../utils/log.js'
|
|
3
|
-
import { CHANNEL_ID } from '../utils/constant.js'
|
|
4
|
-
import { sendGatewayRpc } from '../gateway/socket.js'
|
|
5
|
-
import { sendEventMessage } from '../transport.js'
|
|
6
|
-
|
|
7
|
-
// 为处理的jobId列表
|
|
8
|
-
const processedJobIds = new Set<string>()
|
|
9
|
-
// 定时任务执行时间处理
|
|
10
|
-
export async function cronAddHandler(jobId: string) {
|
|
11
|
-
const hasSessionKey = await checkCronSessionKey(jobId, true)
|
|
12
|
-
if (!hasSessionKey) {
|
|
13
|
-
processedJobIds.add(jobId)
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
// 删除处理的jobId
|
|
17
|
-
export function removeProcessedJobId(jobId: string) {
|
|
18
|
-
processedJobIds.delete(jobId)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export async function getSessionKeyByJobId(jobId: string) {
|
|
22
|
-
const cronData = (await sendGatewayRpc({ method: 'cron.get', params: { id: jobId } })) as Record<string, any>
|
|
23
|
-
return cronData?.sessionKey || cronData.delivery?.to?.replace('dcg-cron:', '').trim()
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* 检查定时任务中是否含有sessionKey或deliveryTo,数据完整直接返回true,否则返回false 两者有一个补充数据更新
|
|
28
|
-
* @param jobId
|
|
29
|
-
* @returns
|
|
30
|
-
*/
|
|
31
|
-
export async function checkCronSessionKey(jobId: string, isSend?: boolean) {
|
|
32
|
-
const cronData = (await sendGatewayRpc({ method: 'cron.get', params: { id: jobId } })) as Record<string, any>
|
|
33
|
-
if (!cronData) {
|
|
34
|
-
dcgLogger(`getJobIdBySessionKey failed for jobId: ${jobId}`, 'error')
|
|
35
|
-
return
|
|
36
|
-
}
|
|
37
|
-
const sessionKey = cronData?.sessionKey
|
|
38
|
-
const deliveryTo = cronData.delivery?.to?.replace('dcg-cron:', '').trim()
|
|
39
|
-
if (!sessionKey && !deliveryTo) {
|
|
40
|
-
dcgLogger(`checkCronSessionKey failed for jobId: ${jobId}`, 'error')
|
|
41
|
-
processedJobIds.add(jobId)
|
|
42
|
-
return false
|
|
43
|
-
}
|
|
44
|
-
if (sessionKey && deliveryTo) {
|
|
45
|
-
if (isSend) {
|
|
46
|
-
const params = {
|
|
47
|
-
event_type: 'cron',
|
|
48
|
-
operation_type: 'install',
|
|
49
|
-
data: cronData
|
|
50
|
-
}
|
|
51
|
-
sendEventMessage(params)
|
|
52
|
-
}
|
|
53
|
-
return true
|
|
54
|
-
}
|
|
55
|
-
return (await supplementSessionKey(jobId)) !== null
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// 判断after_tool_call中是否含有jobId
|
|
59
|
-
export function getJobIdInAfterToolCall(event: PluginHookAfterToolCallEvent) {
|
|
60
|
-
if (event.toolName !== 'cron') return null
|
|
61
|
-
if (String(event.params.action ?? '').toLowerCase() !== 'add') return
|
|
62
|
-
if (event.error) return
|
|
63
|
-
|
|
64
|
-
const result = event.result as { details?: { id?: unknown } }
|
|
65
|
-
const jobId = result.details?.id
|
|
66
|
-
|
|
67
|
-
if (typeof jobId === 'string') {
|
|
68
|
-
return jobId
|
|
69
|
-
}
|
|
70
|
-
const evtStr = JSON.stringify(event)
|
|
71
|
-
// 便利processedJobIds,找到包含在evtStr的jobId
|
|
72
|
-
for (const id of processedJobIds) {
|
|
73
|
-
if (evtStr.includes(id)) {
|
|
74
|
-
return id
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return null
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// 添加jobId -> sessionKey 映射
|
|
81
|
-
export async function cronAfterToolCallHandler(event: PluginHookAfterToolCallEvent, sessionKey: string) {
|
|
82
|
-
const jobId = getJobIdInAfterToolCall(event)
|
|
83
|
-
if (!jobId) return dcgLogger(`getJobIdInAfterToolCall failed for event not has jobId: ${JSON.stringify(event)}`, 'error')
|
|
84
|
-
jobIdToSessionKeyMap.set(jobId, sessionKey)
|
|
85
|
-
sessionKeyToJobIdMap.set(sessionKey, jobId)
|
|
86
|
-
const hasCompleteSessionKey = await checkCronSessionKey(jobId)
|
|
87
|
-
if (hasCompleteSessionKey) {
|
|
88
|
-
removeProcessedJobId(jobId)
|
|
89
|
-
return
|
|
90
|
-
}
|
|
91
|
-
if (processedJobIds.has(jobId)) {
|
|
92
|
-
await supplementSessionKey(jobId)
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// jobId -> sessionKey 映射
|
|
97
|
-
const jobIdToSessionKeyMap = new Map<string, string>()
|
|
98
|
-
const sessionKeyToJobIdMap = new Map<string, string>()
|
|
99
|
-
// 获取jobId -> sessionKey 映射
|
|
100
|
-
function getJobIdToSessionKeyMap(jobId: string) {
|
|
101
|
-
return jobIdToSessionKeyMap.get(jobId)
|
|
102
|
-
}
|
|
103
|
-
// 删除jobId -> sessionKey 映射
|
|
104
|
-
export function removeJobIdToSessionKeyMap(jobId: string) {
|
|
105
|
-
const sessionKey = jobIdToSessionKeyMap.get(jobId)
|
|
106
|
-
jobIdToSessionKeyMap.delete(jobId)
|
|
107
|
-
if (sessionKey) sessionKeyToJobIdMap.delete(sessionKey)
|
|
108
|
-
}
|
|
109
|
-
// 补充定时任务数据
|
|
110
|
-
export async function supplementSessionKey(jobId: string) {
|
|
111
|
-
const sk = getJobIdToSessionKeyMap(jobId)
|
|
112
|
-
if (!sk) {
|
|
113
|
-
dcgLogger(`getJobIdToSessionKeyMap failed for jobId: ${jobId}`, 'error')
|
|
114
|
-
return null
|
|
115
|
-
}
|
|
116
|
-
const cronData = (await sendGatewayRpc({ method: 'cron.get', params: { id: jobId } })) as Record<string, any>
|
|
117
|
-
if (!cronData) {
|
|
118
|
-
dcgLogger(`getJobIdToSessionKeyMap failed for jobId: ${jobId}`, 'error')
|
|
119
|
-
return null
|
|
120
|
-
}
|
|
121
|
-
const patch = structuredClone(cronData)
|
|
122
|
-
patch.sessionKey = sk
|
|
123
|
-
if (!patch?.delivery) {
|
|
124
|
-
patch.delivery = {}
|
|
125
|
-
}
|
|
126
|
-
patch.delivery.to = `dcg-cron:${sk}`
|
|
127
|
-
patch.delivery.channel = CHANNEL_ID
|
|
128
|
-
delete patch.id
|
|
129
|
-
delete patch.createdAtMs
|
|
130
|
-
delete patch.updatedAtMs
|
|
131
|
-
try {
|
|
132
|
-
await sendGatewayRpc({ method: 'cron.update', params: { id: jobId, patch } })
|
|
133
|
-
removeProcessedJobId(jobId)
|
|
134
|
-
removeJobIdToSessionKeyMap(jobId)
|
|
135
|
-
return true
|
|
136
|
-
} catch (e) {
|
|
137
|
-
dcgLogger(`supplementSessionKey failed for jobId: ${jobId}: ${String(e)}`, 'error')
|
|
138
|
-
return null
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export function isCronAddAction(params: unknown): boolean {
|
|
143
|
-
const record = params !== null && typeof params === 'object' && !Array.isArray(params) ? (params as Record<string, unknown>) : null
|
|
144
|
-
const action = typeof record?.action === 'string' ? record.action.trim().toLowerCase() : ''
|
|
145
|
-
return action === 'add' || action === 'create'
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export function isCronCreateExecCommand(command: unknown): boolean {
|
|
149
|
-
if (typeof command !== 'string') return false
|
|
150
|
-
return /(?:^|\s)cron\s+(?:create|add)(?:\s|$)/i.test(command.trim())
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export function isCreateCronTool(event: { toolName: any; params: any; toolCallId: any }) {
|
|
154
|
-
const { toolName, params } = event
|
|
155
|
-
|
|
156
|
-
if (toolName === 'cron') {
|
|
157
|
-
return isCronAddAction(params)
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (toolName === 'exec') {
|
|
161
|
-
const record = params !== null && typeof params === 'object' && !Array.isArray(params) ? (params as Record<string, unknown>) : null
|
|
162
|
-
return isCronCreateExecCommand(record?.command)
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return false
|
|
166
|
-
}
|
package/src/cron/toolGuard.ts
DELETED
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
import { CHANNEL_ID } from '../utils/constant.js'
|
|
2
|
-
import type { CronJobLike } from './types.js'
|
|
3
|
-
/**
|
|
4
|
-
* cron-delivery-guard — 定时任务投递守护插件
|
|
5
|
-
*
|
|
6
|
-
* 核心机制:通过 before_tool_call 钩子拦截 cron 工具调用,
|
|
7
|
-
* 对 DCG 创建的 cron 统一注入会话、执行、payload 和 delivery 业务字段,
|
|
8
|
-
* 并在模型生成不完整或冲突配置时强制规范化。
|
|
9
|
-
*
|
|
10
|
-
* 背景:
|
|
11
|
-
* - 创建前尽量让 OpenClaw 收到完整合法的 cron 配置
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { dcgLogger } from '../utils/log.js'
|
|
15
|
-
import { isCronAddAction, isCronCreateExecCommand } from './params.js'
|
|
16
|
-
|
|
17
|
-
const LOG_TAG = 'cron-delivery-guard'
|
|
18
|
-
const DCG_CRON_TZ = 'Asia/Shanghai'
|
|
19
|
-
const CRON_PAYLOAD_TIMEOUT_SECONDS = 600
|
|
20
|
-
const DEFAULT_CRON_MESSAGE = '请按定时任务要求提醒用户。'
|
|
21
|
-
|
|
22
|
-
function trimCronString(value: unknown): string {
|
|
23
|
-
return typeof value === 'string' ? value.trim() : ''
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function formatterSessionKey(sessionKey: string): { agentId: string; sessionId: string } {
|
|
27
|
-
const parts = sessionKey.split(':').filter((part) => part.length > 0)
|
|
28
|
-
const normalized = parts.map((part) => part.toLowerCase())
|
|
29
|
-
if (parts.length >= 6 && normalized[0] === 'agent' && normalized[2] === 'mobook' && normalized[3] === 'direct') {
|
|
30
|
-
return {
|
|
31
|
-
agentId: parts[4] ?? '',
|
|
32
|
-
sessionId: parts[5] ?? ''
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
if (parts.length >= 2) {
|
|
36
|
-
return {
|
|
37
|
-
agentId: parts[parts.length - 2] ?? '',
|
|
38
|
-
sessionId: parts[parts.length - 1] ?? ''
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return { agentId: '', sessionId: '' }
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function resolveCronMessage(job: CronJobLike): string {
|
|
45
|
-
const payload = job.payload
|
|
46
|
-
return (
|
|
47
|
-
trimCronString(payload?.message) ||
|
|
48
|
-
trimCronString(payload?.text) ||
|
|
49
|
-
trimCronString(job.message) ||
|
|
50
|
-
trimCronString(job.text) ||
|
|
51
|
-
trimCronString(job.description) ||
|
|
52
|
-
trimCronString(job.name) ||
|
|
53
|
-
DEFAULT_CRON_MESSAGE
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function buildDcgCronJob(job: CronJobLike, sessionKey: string): CronJobLike {
|
|
58
|
-
const sk = sessionKey.trim()
|
|
59
|
-
const { agentId } = formatterSessionKey(sk)
|
|
60
|
-
const delivery: Record<string, unknown> = {
|
|
61
|
-
...(job.delivery ?? {}),
|
|
62
|
-
mode: 'announce',
|
|
63
|
-
channel: CHANNEL_ID,
|
|
64
|
-
to: `dcg-cron:${sk}`,
|
|
65
|
-
bestEffort: true
|
|
66
|
-
}
|
|
67
|
-
if (agentId) delivery.accountId = agentId
|
|
68
|
-
|
|
69
|
-
const sourcePayload = job.payload ?? {}
|
|
70
|
-
const payload: Record<string, unknown> = {
|
|
71
|
-
kind: 'agentTurn',
|
|
72
|
-
message: resolveCronMessage(job),
|
|
73
|
-
timeoutSeconds: CRON_PAYLOAD_TIMEOUT_SECONDS
|
|
74
|
-
}
|
|
75
|
-
for (const field of ['model', 'fallbacks', 'thinking', 'allowUnsafeExternalContent', 'lightContext', 'toolsAllow']) {
|
|
76
|
-
if (sourcePayload[field] !== undefined) payload[field] = sourcePayload[field]
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
...job,
|
|
81
|
-
sessionKey: sk,
|
|
82
|
-
delivery,
|
|
83
|
-
schedule: job.schedule?.kind === 'cron' ? { ...job.schedule, tz: DCG_CRON_TZ } : job.schedule,
|
|
84
|
-
payload
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/** `--every` / `--at` 由 CLI 自行处理时区语义,不再追加 `--tz`(避免与 `--at` 等冲突) */
|
|
89
|
-
function cronExecCmdSkipsInjectedTz(cmd: string): boolean {
|
|
90
|
-
return /--every(?:\s|=|$)/.test(cmd) || /(^|\s)--at(?:\s|=|$)/.test(cmd)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/** 移除 CLI 中“带值”的选项,例如 `--tz Asia/Shanghai` 或 `--tz=Asia/Shanghai` */
|
|
94
|
-
function removeCliValueOption(command: string, option: string): string {
|
|
95
|
-
const escaped = option.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
96
|
-
const value = `(?:"[^"]*"|'[^']*'|\\S+)`
|
|
97
|
-
return command.replace(new RegExp(`(^|\\s)${escaped}(?:=${value}|\\s+${value})`, 'g'), '$1')
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** 移除 CLI 中“无值”的开关型选项,例如 `--announce` 或 `--json` */
|
|
101
|
-
function removeCliFlag(command: string, option: string): string {
|
|
102
|
-
const escaped = option.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
103
|
-
return command.replace(new RegExp(`(^|\\s)${escaped}(?=\\s|$)`, 'g'), '$1')
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* 规范化 `exec` 工具中的 `cron create/add` 命令字符串。
|
|
108
|
-
*
|
|
109
|
-
* 背景:模型可能通过自由文本的 `exec` 工具创建 cron,写出来的命令参数可能:
|
|
110
|
-
* 1. 缺少 DCG 必须的字段(如 --to、--channel、--best-effort-deliver);
|
|
111
|
-
* 2. 与 DCG 要求冲突(如 --session default、--channel last);
|
|
112
|
-
* 3. 使用旧参数名(如 --system-event)。
|
|
113
|
-
*
|
|
114
|
-
* 本函数先“清洗”掉这些不确定/冲突的参数,再“强制注入”一套 DCG 统一的参数,
|
|
115
|
-
* 保证最终命令在会话隔离、投递路由、超时、输出格式等方面一致。
|
|
116
|
-
*/
|
|
117
|
-
function normalizeCronExecCommand(command: string, sessionKey: string): string {
|
|
118
|
-
// 1. 预处理:判断是否需要跳过 --tz 注入,并解析当前 agentId
|
|
119
|
-
const skipTz = cronExecCmdSkipsInjectedTz(command)
|
|
120
|
-
const { agentId } = formatterSessionKey(sessionKey)
|
|
121
|
-
|
|
122
|
-
// 兼容旧命令:将 --system-event 替换为 --message
|
|
123
|
-
let next = command.replace(/(^|\s)--system-event(?=\s|=)/g, '$1--message')
|
|
124
|
-
|
|
125
|
-
// 2. 清洗:移除所有 DCG 会强制接管的有值选项,避免与后续注入的值冲突
|
|
126
|
-
for (const option of ['--tz', '--timeout-seconds', '--session-key', '--session', '--wake', '--channel', '--to', '--account']) {
|
|
127
|
-
next = removeCliValueOption(next, option)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// 3. 清洗:移除所有 DCG 会强制接管的无值开关,避免重复或冲突
|
|
131
|
-
for (const option of ['--announce', '--deliver', '--no-deliver', '--best-effort-deliver', '--no-best-effort-deliver', '--json']) {
|
|
132
|
-
next = removeCliFlag(next, option)
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// 4. 组装 DCG 强制要求的一套参数
|
|
136
|
-
const required = [
|
|
137
|
-
// 仅当命令不含 --every/--at 时才注入默认时区,避免时区语义冲突
|
|
138
|
-
skipTz ? '' : `--tz ${DCG_CRON_TZ}`,
|
|
139
|
-
// 控制 payload 执行超时
|
|
140
|
-
`--timeout-seconds ${CRON_PAYLOAD_TIMEOUT_SECONDS}`,
|
|
141
|
-
// 把 sessionKey 带给 cron payload,用于后续路由和修复
|
|
142
|
-
`--session-key ${sessionKey}`,
|
|
143
|
-
// 要求 cron 创建完成后发送announce消息,便于 DCG 拦截处理
|
|
144
|
-
'--announce',
|
|
145
|
-
// 固定投递到 DCG 监控的频道
|
|
146
|
-
`--channel ${CHANNEL_ID}`,
|
|
147
|
-
// 投递目标指向 DCG 专用队列,带上 sessionKey 以便路由
|
|
148
|
-
`--to dcg-cron:${sessionKey}`,
|
|
149
|
-
// 带上 agentId,便于多租户/多 agent 场景定位来源
|
|
150
|
-
agentId ? `--account ${agentId}` : '',
|
|
151
|
-
// 投递失败时尽量重试,不直接丢弃任务
|
|
152
|
-
'--best-effort-deliver',
|
|
153
|
-
// 输出 JSON,方便下游解析
|
|
154
|
-
'--json'
|
|
155
|
-
].filter(Boolean)
|
|
156
|
-
|
|
157
|
-
// 5. 保留模型原始命令中未被清洗的部分,追加 DCG 强制参数,并压缩多余空格
|
|
158
|
-
return `${next.trim()} ${required.join(' ')}`.replace(/\s+/g, ' ')
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// ---- 辅助函数 ----
|
|
162
|
-
|
|
163
|
-
/** 与 OpenClaw cron tool 的 CRON_RECOVERABLE_OBJECT_KEYS 保持一致。 */
|
|
164
|
-
const CRON_RECOVERABLE_JOB_KEYS = [
|
|
165
|
-
'name',
|
|
166
|
-
'schedule',
|
|
167
|
-
'payload',
|
|
168
|
-
'delivery',
|
|
169
|
-
'enabled',
|
|
170
|
-
'description',
|
|
171
|
-
'deleteAfterRun',
|
|
172
|
-
'agentId',
|
|
173
|
-
'sessionKey',
|
|
174
|
-
'failureAlert',
|
|
175
|
-
'message',
|
|
176
|
-
'text',
|
|
177
|
-
'model',
|
|
178
|
-
'fallbacks',
|
|
179
|
-
'toolsAllow',
|
|
180
|
-
'thinking',
|
|
181
|
-
'timeoutSeconds',
|
|
182
|
-
'lightContext',
|
|
183
|
-
'allowUnsafeExternalContent'
|
|
184
|
-
] as const
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* 将任意值安全地收窄为普通对象类型。
|
|
188
|
-
*
|
|
189
|
-
* 主要用于处理来自模型/上游的 `params`:它可能是 `null`、数组、字符串等,
|
|
190
|
-
* 本函数先排除这些非法形态,再返回 `Record<string, unknown>`,
|
|
191
|
-
* 让调用方可以安全地访问 `.action` 等字段。
|
|
192
|
-
*/
|
|
193
|
-
function asRecord(value: unknown): Record<string, unknown> | null {
|
|
194
|
-
return value !== null && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : null
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* OpenClaw cron tool 的正式创建 action 是 `add`。
|
|
199
|
-
* `create` 只是 `openclaw cron create` CLI 的 alias,不是 tool action。
|
|
200
|
-
*/
|
|
201
|
-
function isCronAddParams(params: Record<string, unknown>): boolean {
|
|
202
|
-
const action = typeof params.action === 'string' ? params.action.trim().toLowerCase() : ''
|
|
203
|
-
return action === 'add'
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* 模型偶尔会把 job 字段错误地放到 params 顶层。
|
|
208
|
-
* OpenClaw 执行 cron.add 前也会按同一组字段恢复 params.job;这里提前恢复,
|
|
209
|
-
* 这样 before_tool_call 可以在真正执行前应用完整的 DCG 规范。
|
|
210
|
-
*/
|
|
211
|
-
function recoverFlatCronJob(params: Record<string, unknown>): Record<string, unknown> | null {
|
|
212
|
-
const recovered: Record<string, unknown> = {}
|
|
213
|
-
for (const key of CRON_RECOVERABLE_JOB_KEYS) {
|
|
214
|
-
if (params[key] !== undefined) recovered[key] = params[key]
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// 与 OpenClaw 保持一致:至少存在 schedule、payload 或可推断 payload 的文本字段。
|
|
218
|
-
const hasJobContent =
|
|
219
|
-
recovered.schedule !== undefined ||
|
|
220
|
-
recovered.payload !== undefined ||
|
|
221
|
-
recovered.message !== undefined ||
|
|
222
|
-
recovered.text !== undefined
|
|
223
|
-
|
|
224
|
-
return hasJobContent ? recovered : null
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* 规范化 cron.add 参数。
|
|
229
|
-
*
|
|
230
|
-
* 正式结构是 `{ action: "add", job: CronJobCreate }`,因此只修改 params.job。
|
|
231
|
-
* 如果 job 缺失,则使用 OpenClaw 自身支持的 flat 参数兼容规则恢复 job。
|
|
232
|
-
* update/remove/run 等 action 有各自的 patch/jobId 结构,不应在这里处理。
|
|
233
|
-
*/
|
|
234
|
-
function patchCronAddParams(params: Record<string, unknown>, sk: string): Record<string, unknown> | null {
|
|
235
|
-
const newParams = structuredClone(params) as Record<string, unknown>
|
|
236
|
-
const explicitJob = asRecord(newParams.job)
|
|
237
|
-
const job = explicitJob && Object.keys(explicitJob).length > 0 ? explicitJob : recoverFlatCronJob(newParams)
|
|
238
|
-
if (!job) return null
|
|
239
|
-
|
|
240
|
-
// buildDcgCronJob 保留 job 的其他合法字段,只强制覆盖 DCG 业务字段。
|
|
241
|
-
newParams.job = buildDcgCronJob(job as CronJobLike, sk)
|
|
242
|
-
return newParams
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
export function cronToolCall(event: { toolName: any; params: any; toolCallId: any }, sk: string) {
|
|
246
|
-
const { toolName, params, toolCallId } = event
|
|
247
|
-
const isCronTool = toolName === 'cron' && isCronAddAction(params)
|
|
248
|
-
// 仅处理 cron 工具
|
|
249
|
-
if (isCronTool) {
|
|
250
|
-
const paramsRecord = asRecord(params)
|
|
251
|
-
if (!paramsRecord || !isCronAddParams(paramsRecord)) {
|
|
252
|
-
return undefined
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const newParams = patchCronAddParams(paramsRecord, sk)
|
|
256
|
-
|
|
257
|
-
dcgLogger(`[${LOG_TAG}] cronToolCall params (${toolCallId}) ==> ${JSON.stringify(params)}`)
|
|
258
|
-
dcgLogger(`[${LOG_TAG}] cronToolCall newParams (${toolCallId}) ==> ${JSON.stringify(newParams)}`)
|
|
259
|
-
|
|
260
|
-
if (!newParams) {
|
|
261
|
-
dcgLogger(`[${LOG_TAG}] cronToolCall (${toolCallId}) could not resolve cron.add job, skip.`)
|
|
262
|
-
return undefined
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
return { params: newParams }
|
|
266
|
-
} else if (toolName === 'exec') {
|
|
267
|
-
const cmd = params.command
|
|
268
|
-
if (typeof cmd !== 'string') {
|
|
269
|
-
return undefined
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// 保留原有逻辑(非 openclaw cron 命令的 cron create/add,或其他 exec)
|
|
273
|
-
if (isCronCreateExecCommand(cmd)) {
|
|
274
|
-
const newParams = structuredClone(params) as Record<string, unknown>
|
|
275
|
-
newParams.command = normalizeCronExecCommand(cmd, sk)
|
|
276
|
-
|
|
277
|
-
dcgLogger(`cron exec create or add params: ${JSON.stringify(newParams)}`)
|
|
278
|
-
|
|
279
|
-
return { params: newParams }
|
|
280
|
-
}
|
|
281
|
-
return undefined
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
return undefined
|
|
285
|
-
}
|
package/src/cron/types.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cron 领域内创建前规范化与创建后修复共用的最小任务结构。
|
|
3
|
-
* 保留索引签名,以兼容 OpenClaw 后续增加的合法 job 字段。
|
|
4
|
-
*/
|
|
5
|
-
export interface CronJobLike {
|
|
6
|
-
sessionKey?: string
|
|
7
|
-
sessionTarget?: string
|
|
8
|
-
wakeMode?: string
|
|
9
|
-
name?: string
|
|
10
|
-
description?: string
|
|
11
|
-
delivery?: Record<string, unknown>
|
|
12
|
-
schedule?: Record<string, unknown>
|
|
13
|
-
payload?: Record<string, unknown>
|
|
14
|
-
[key: string]: unknown
|
|
15
|
-
}
|