@next-open-ai/openclawx 0.8.1 → 0.8.7
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/apps/desktop/renderer/dist/assets/index-BvsIUz4D.css +10 -0
- package/apps/desktop/renderer/dist/assets/index-dT6euD7-.js +89 -0
- package/apps/desktop/renderer/dist/index.html +3 -3
- package/dist/core/config/desktop-config.d.ts +6 -0
- package/dist/gateway/channel/adapters/dingtalk.d.ts +11 -0
- package/dist/gateway/channel/adapters/dingtalk.js +190 -0
- package/dist/gateway/channel/channel-core.js +9 -1
- package/dist/gateway/channel/types.d.ts +8 -4
- package/dist/gateway/server.js +22 -5
- package/dist/server/agent-config/agent-config.controller.d.ts +2 -1
- package/dist/server/agent-config/agent-config.service.d.ts +5 -1
- package/dist/server/agent-config/agent-config.service.js +4 -1
- package/package.json +2 -1
- package/apps/desktop/renderer/dist/assets/index-DKXv_Kpk.css +0 -10
- package/apps/desktop/renderer/dist/assets/index-eCpjapym.js +0 -89
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
<meta charset="UTF-8" />
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
|
-
<title>
|
|
8
|
+
<title>OpenClawX</title>
|
|
9
9
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
10
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
11
|
<link
|
|
12
12
|
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Roboto+Mono:wght@400;500&display=swap"
|
|
13
13
|
rel="stylesheet">
|
|
14
|
-
<script type="module" crossorigin src="/assets/index-
|
|
15
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
14
|
+
<script type="module" crossorigin src="/assets/index-dT6euD7-.js"></script>
|
|
15
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BvsIUz4D.css">
|
|
16
16
|
</head>
|
|
17
17
|
|
|
18
18
|
<body>
|
|
@@ -15,6 +15,12 @@ export interface ChannelsConfig {
|
|
|
15
15
|
appSecret?: string;
|
|
16
16
|
defaultAgentId?: string;
|
|
17
17
|
};
|
|
18
|
+
dingtalk?: {
|
|
19
|
+
enabled?: boolean;
|
|
20
|
+
clientId?: string;
|
|
21
|
+
clientSecret?: string;
|
|
22
|
+
defaultAgentId?: string;
|
|
23
|
+
};
|
|
18
24
|
}
|
|
19
25
|
/** MCP 服务器配置(与 core/mcp 类型一致,避免 core/config 依赖 core/mcp 实现) */
|
|
20
26
|
export type DesktopMcpServerConfig = import("../mcp/index.js").McpServerConfig;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { IChannel } from "../types.js";
|
|
2
|
+
export interface DingTalkChannelConfig {
|
|
3
|
+
clientId: string;
|
|
4
|
+
clientSecret: string;
|
|
5
|
+
/** 默认绑定的 agentId */
|
|
6
|
+
defaultAgentId?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* 钉钉通道:Stream 入站 + Webhook 出站,共用一个 DWClient。
|
|
10
|
+
*/
|
|
11
|
+
export declare function createDingTalkChannel(config: DingTalkChannelConfig): IChannel;
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 钉钉通道适配器:使用 dingtalk-stream SDK Stream 模式接收机器人消息,通过 sessionWebhook 回传回复。
|
|
3
|
+
*/
|
|
4
|
+
import { DWClient, EventAck, TOPIC_ROBOT } from "dingtalk-stream";
|
|
5
|
+
import { dispatchMessage } from "../registry.js";
|
|
6
|
+
/** conversationId -> sessionWebhook,用于回复时 POST;收到新消息时更新 */
|
|
7
|
+
const sessionWebhookByConversation = new Map();
|
|
8
|
+
/**
|
|
9
|
+
* 钉钉 Stream 入站:DWClient + registerCallbackListener(TOPIC_ROBOT),解析 RobotMessage 转 UnifiedMessage。
|
|
10
|
+
* 回复发送完成后需调用 ack(socketCallBackResponse)避免钉钉重试。
|
|
11
|
+
*/
|
|
12
|
+
class DingTalkStreamInbound {
|
|
13
|
+
client;
|
|
14
|
+
messageHandler = null;
|
|
15
|
+
constructor(client) {
|
|
16
|
+
this.client = client;
|
|
17
|
+
}
|
|
18
|
+
setMessageHandler(handler) {
|
|
19
|
+
this.messageHandler = handler;
|
|
20
|
+
}
|
|
21
|
+
async start() {
|
|
22
|
+
this.client.registerCallbackListener(TOPIC_ROBOT, async (res) => {
|
|
23
|
+
try {
|
|
24
|
+
const data = JSON.parse(res.data);
|
|
25
|
+
const conversationId = data.conversationId;
|
|
26
|
+
const sessionWebhook = data.sessionWebhook;
|
|
27
|
+
if (sessionWebhook) {
|
|
28
|
+
sessionWebhookByConversation.set(conversationId, sessionWebhook);
|
|
29
|
+
}
|
|
30
|
+
const textContent = data.msgtype === "text" && data.text?.content ? data.text.content.trim() : "";
|
|
31
|
+
if (!textContent)
|
|
32
|
+
return;
|
|
33
|
+
const messageId = res.headers?.messageId ?? "";
|
|
34
|
+
const unified = {
|
|
35
|
+
channelId: "dingtalk",
|
|
36
|
+
threadId: conversationId,
|
|
37
|
+
userId: data.senderStaffId ?? data.senderId ?? "unknown",
|
|
38
|
+
userName: data.senderNick,
|
|
39
|
+
messageText: textContent,
|
|
40
|
+
replyTarget: "default",
|
|
41
|
+
messageId,
|
|
42
|
+
raw: data,
|
|
43
|
+
ack: (sendResult) => {
|
|
44
|
+
if (messageId) {
|
|
45
|
+
this.client.socketCallBackResponse(messageId, sendResult ?? {});
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
if (this.messageHandler) {
|
|
50
|
+
await this.messageHandler(unified);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
await dispatchMessage(unified);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
console.error("[DingTalk] onBotMessage error:", e);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
this.client.registerAllEventListener(() => ({ status: EventAck.SUCCESS }));
|
|
61
|
+
await this.client.connect();
|
|
62
|
+
console.log("[DingTalk] Stream client connected");
|
|
63
|
+
}
|
|
64
|
+
async stop() {
|
|
65
|
+
try {
|
|
66
|
+
this.client.disconnect();
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
console.warn("[DingTalk] disconnect error", e);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 钉钉出站:通过 sessionWebhook POST 发送文本回复(需 access_token)。
|
|
75
|
+
* 支持 sendStream:按 agent 每轮(turn_end)发一条消息,不拆解最终回答。
|
|
76
|
+
*/
|
|
77
|
+
class DingTalkWebhookOutbound {
|
|
78
|
+
client;
|
|
79
|
+
constructor(client) {
|
|
80
|
+
this.client = client;
|
|
81
|
+
}
|
|
82
|
+
async postOne(sessionWebhook, text) {
|
|
83
|
+
const accessToken = await this.client.getAccessToken();
|
|
84
|
+
const body = JSON.stringify({
|
|
85
|
+
msgtype: "text",
|
|
86
|
+
text: { content: text },
|
|
87
|
+
});
|
|
88
|
+
const res = await fetch(sessionWebhook, {
|
|
89
|
+
method: "POST",
|
|
90
|
+
headers: {
|
|
91
|
+
"Content-Type": "application/json",
|
|
92
|
+
"x-acs-dingtalk-access-token": accessToken,
|
|
93
|
+
},
|
|
94
|
+
body,
|
|
95
|
+
});
|
|
96
|
+
return res.json().catch(() => ({}));
|
|
97
|
+
}
|
|
98
|
+
async send(targetId, reply) {
|
|
99
|
+
const sessionWebhook = sessionWebhookByConversation.get(targetId);
|
|
100
|
+
if (!sessionWebhook) {
|
|
101
|
+
console.error("[DingTalk] send skipped: no sessionWebhook for conversationId", targetId);
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
const text = reply.text?.trim() || "(无内容)";
|
|
105
|
+
try {
|
|
106
|
+
return await this.postOne(sessionWebhook, text);
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
console.error("[DingTalk] send message failed:", e);
|
|
110
|
+
throw e;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async sendStream(targetId) {
|
|
114
|
+
const sessionWebhook = sessionWebhookByConversation.get(targetId);
|
|
115
|
+
if (!sessionWebhook) {
|
|
116
|
+
throw new Error("[DingTalk] sendStream: no sessionWebhook for conversationId " + targetId);
|
|
117
|
+
}
|
|
118
|
+
/** 已发送的字符右边界,[lastSentIndex, accumulated.length) 为未发送 */
|
|
119
|
+
let lastSentIndex = 0;
|
|
120
|
+
/** 串行锁:onTurnEnd 与 onDone 可能不 await,必须顺序执行发送,避免重复发同一段 */
|
|
121
|
+
let sendLock = Promise.resolve();
|
|
122
|
+
const sendOne = (text) => {
|
|
123
|
+
if (!text.trim())
|
|
124
|
+
return Promise.resolve();
|
|
125
|
+
return this.postOne(sessionWebhook, text.trim()).then(() => { }, (e) => {
|
|
126
|
+
console.error("[DingTalk] sendStream post failed:", e);
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
/** 仅发送尚未发送的区间,并推进 lastSentIndex;在锁内执行 */
|
|
130
|
+
const flushPending = (accumulated) => {
|
|
131
|
+
const pending = accumulated.slice(lastSentIndex).trim();
|
|
132
|
+
if (!pending)
|
|
133
|
+
return Promise.resolve();
|
|
134
|
+
return sendOne(pending).then(() => {
|
|
135
|
+
lastSentIndex = accumulated.length;
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
return {
|
|
139
|
+
onChunk: async () => {
|
|
140
|
+
// 钉钉按轮发,不按 chunk 发
|
|
141
|
+
},
|
|
142
|
+
onTurnEnd: async (accumulated) => {
|
|
143
|
+
sendLock = sendLock.then(() => flushPending(accumulated));
|
|
144
|
+
await sendLock;
|
|
145
|
+
},
|
|
146
|
+
onDone: async (accumulated) => {
|
|
147
|
+
const final = accumulated.trim() || "(无内容)";
|
|
148
|
+
sendLock = sendLock.then(() => {
|
|
149
|
+
const remaining = final.slice(lastSentIndex).trim();
|
|
150
|
+
if (remaining) {
|
|
151
|
+
return sendOne(remaining).then(() => {
|
|
152
|
+
lastSentIndex = final.length;
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
if (lastSentIndex === 0) {
|
|
156
|
+
return sendOne(final).then(() => {
|
|
157
|
+
lastSentIndex = final.length;
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
return Promise.resolve();
|
|
161
|
+
});
|
|
162
|
+
await sendLock;
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* 钉钉通道:Stream 入站 + Webhook 出站,共用一个 DWClient。
|
|
169
|
+
*/
|
|
170
|
+
export function createDingTalkChannel(config) {
|
|
171
|
+
const { clientId, clientSecret } = config;
|
|
172
|
+
if (!clientId?.trim() || !clientSecret?.trim()) {
|
|
173
|
+
throw new Error("[DingTalk] clientId and clientSecret are required");
|
|
174
|
+
}
|
|
175
|
+
const client = new DWClient({
|
|
176
|
+
clientId: clientId.trim(),
|
|
177
|
+
clientSecret: clientSecret.trim(),
|
|
178
|
+
debug: false,
|
|
179
|
+
});
|
|
180
|
+
const inbound = new DingTalkStreamInbound(client);
|
|
181
|
+
const outbound = new DingTalkWebhookOutbound(client);
|
|
182
|
+
inbound.setMessageHandler((msg) => dispatchMessage(msg));
|
|
183
|
+
return {
|
|
184
|
+
id: "dingtalk",
|
|
185
|
+
name: "钉钉",
|
|
186
|
+
defaultAgentId: config.defaultAgentId ?? "default",
|
|
187
|
+
getInbounds: () => [inbound],
|
|
188
|
+
getOutbounds: () => [outbound],
|
|
189
|
+
};
|
|
190
|
+
}
|
|
@@ -91,6 +91,11 @@ export async function handleChannelMessage(channel, msg) {
|
|
|
91
91
|
accumulated += delta;
|
|
92
92
|
throttled.run();
|
|
93
93
|
},
|
|
94
|
+
onTurnEnd() {
|
|
95
|
+
throttled.flush();
|
|
96
|
+
if (sink.onTurnEnd)
|
|
97
|
+
void Promise.resolve(sink.onTurnEnd(accumulated)).catch((e) => console.error("[ChannelCore] stream onTurnEnd error:", e));
|
|
98
|
+
},
|
|
94
99
|
onDone() {
|
|
95
100
|
throttled.cancel();
|
|
96
101
|
const final = accumulated.trim() || "(无文本回复)";
|
|
@@ -100,12 +105,14 @@ export async function handleChannelMessage(channel, msg) {
|
|
|
100
105
|
});
|
|
101
106
|
if (donePromise)
|
|
102
107
|
await donePromise;
|
|
108
|
+
await msg.ack?.(undefined);
|
|
103
109
|
}
|
|
104
110
|
catch (err) {
|
|
105
111
|
console.error("[ChannelCore] runAgent failed:", err);
|
|
106
112
|
throttled.cancel();
|
|
107
113
|
const fallback = accumulated.trim() || "处理时出错,请稍后再试。";
|
|
108
114
|
await Promise.resolve(sink.onDone(fallback)).catch(() => { });
|
|
115
|
+
await msg.ack?.(undefined);
|
|
109
116
|
}
|
|
110
117
|
return;
|
|
111
118
|
}
|
|
@@ -123,5 +130,6 @@ export async function handleChannelMessage(channel, msg) {
|
|
|
123
130
|
}
|
|
124
131
|
persistChannelAssistantMessage(sessionId, replyText);
|
|
125
132
|
const reply = { text: replyText };
|
|
126
|
-
await outbound.send(threadId, reply);
|
|
133
|
+
const sendResult = await outbound.send(threadId, reply);
|
|
134
|
+
await msg.ack?.(sendResult);
|
|
127
135
|
}
|
|
@@ -25,6 +25,8 @@ export interface UnifiedMessage {
|
|
|
25
25
|
replyTarget?: string;
|
|
26
26
|
/** 平台消息 ID(可选,用于回复引用等) */
|
|
27
27
|
messageId?: string;
|
|
28
|
+
/** 通道可选:回复发送完成后调用的 ack(如钉钉 Stream 需回调响应防重试),参数为 send 返回值 */
|
|
29
|
+
ack?: (sendResult?: unknown) => void | Promise<void>;
|
|
28
30
|
}
|
|
29
31
|
/** 统一出站回复 */
|
|
30
32
|
export interface UnifiedReply {
|
|
@@ -42,16 +44,18 @@ export interface IInboundTransport {
|
|
|
42
44
|
/** 设置收到消息时的回调 */
|
|
43
45
|
setMessageHandler(handler: (msg: UnifiedMessage) => void | Promise<void>): void;
|
|
44
46
|
}
|
|
45
|
-
/** 流式出站:先发一条占位消息,再由调用方按累积内容多次更新。返回的 sink 由 channel-core 在 onChunk/onDone 时调用。 */
|
|
47
|
+
/** 流式出站:先发一条占位消息,再由调用方按累积内容多次更新。返回的 sink 由 channel-core 在 onChunk / onTurnEnd / onDone 时调用。 */
|
|
46
48
|
export interface StreamSink {
|
|
47
|
-
/**
|
|
49
|
+
/** 更新当前已累积的全文(节流后调用),通道可用来做占位或实时更新 */
|
|
48
50
|
onChunk(accumulated: string): void | Promise<void>;
|
|
51
|
+
/** 可选:agent 每轮结束(turn_end)时调用,通道可在此发一条消息(如钉钉按轮发) */
|
|
52
|
+
onTurnEnd?(accumulated: string): void | Promise<void>;
|
|
49
53
|
/** 流结束,做最终一次更新 */
|
|
50
54
|
onDone(accumulated: string): void | Promise<void>;
|
|
51
55
|
}
|
|
52
|
-
/** 出站传输:将 UnifiedReply
|
|
56
|
+
/** 出站传输:将 UnifiedReply 发到外部;返回值可传给 UnifiedMessage.ack */
|
|
53
57
|
export interface IOutboundTransport {
|
|
54
|
-
send(targetId: string, reply: UnifiedReply): Promise<
|
|
58
|
+
send(targetId: string, reply: UnifiedReply): Promise<unknown>;
|
|
55
59
|
/** 可选:该出站是否还能往 targetId 发(如连接是否有效) */
|
|
56
60
|
canSend?(targetId: string): boolean;
|
|
57
61
|
/** 可选:流式发送。先创建占位消息,返回 sink 供调用方按累积内容更新(如飞书 create + patch)。 */
|
package/dist/gateway/server.js
CHANGED
|
@@ -39,6 +39,7 @@ import { ensureDesktopConfigInitialized, getChannelsConfigSync } from "../core/c
|
|
|
39
39
|
import { createNestAppEmbedded } from "../server/bootstrap.js";
|
|
40
40
|
import { registerChannel, startAllChannels, stopAllChannels } from "./channel/registry.js";
|
|
41
41
|
import { createFeishuChannel } from "./channel/adapters/feishu.js";
|
|
42
|
+
import { createDingTalkChannel } from "./channel/adapters/dingtalk.js";
|
|
42
43
|
import { setChannelSessionPersistence } from "./channel/session-persistence.js";
|
|
43
44
|
import { setSessionCurrentAgentResolver, setSessionCurrentAgentUpdater, setAgentListProvider, setCreateAgentProvider, } from "../core/session-current-agent.js";
|
|
44
45
|
import { AgentsService } from "../server/agents/agents.service.js";
|
|
@@ -205,27 +206,43 @@ export async function startGatewayServer(port = 38080) {
|
|
|
205
206
|
resolve(p);
|
|
206
207
|
});
|
|
207
208
|
});
|
|
208
|
-
//
|
|
209
|
+
// 通道:根据配置注册并启动(飞书 WebSocket、钉钉 Stream 等)
|
|
209
210
|
const channelsConfig = getChannelsConfigSync();
|
|
210
211
|
const feishuCfg = channelsConfig.feishu;
|
|
211
212
|
if (feishuCfg?.enabled && feishuCfg.appId?.trim() && feishuCfg.appSecret?.trim()) {
|
|
212
213
|
try {
|
|
213
|
-
console.log("[Channel] Starting Feishu (WebSocket)...");
|
|
214
214
|
const feishuChannel = createFeishuChannel({
|
|
215
215
|
appId: feishuCfg.appId.trim(),
|
|
216
216
|
appSecret: feishuCfg.appSecret.trim(),
|
|
217
217
|
defaultAgentId: feishuCfg.defaultAgentId?.trim() || "default",
|
|
218
218
|
});
|
|
219
219
|
registerChannel(feishuChannel);
|
|
220
|
-
await startAllChannels();
|
|
221
220
|
}
|
|
222
221
|
catch (e) {
|
|
223
|
-
console.warn("Feishu channel
|
|
222
|
+
console.warn("Feishu channel register failed:", e);
|
|
224
223
|
}
|
|
225
224
|
}
|
|
226
225
|
else if (feishuCfg?.enabled) {
|
|
227
|
-
console.warn("[Channel] Feishu is enabled but appId or appSecret is missing; skip
|
|
226
|
+
console.warn("[Channel] Feishu is enabled but appId or appSecret is missing; skip. Check Settings → Channels.");
|
|
228
227
|
}
|
|
228
|
+
const dingtalkCfg = channelsConfig.dingtalk;
|
|
229
|
+
if (dingtalkCfg?.enabled && dingtalkCfg.clientId?.trim() && dingtalkCfg.clientSecret?.trim()) {
|
|
230
|
+
try {
|
|
231
|
+
const dingtalkChannel = createDingTalkChannel({
|
|
232
|
+
clientId: dingtalkCfg.clientId.trim(),
|
|
233
|
+
clientSecret: dingtalkCfg.clientSecret.trim(),
|
|
234
|
+
defaultAgentId: dingtalkCfg.defaultAgentId?.trim() || "default",
|
|
235
|
+
});
|
|
236
|
+
registerChannel(dingtalkChannel);
|
|
237
|
+
}
|
|
238
|
+
catch (e) {
|
|
239
|
+
console.warn("DingTalk channel register failed:", e);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
else if (dingtalkCfg?.enabled) {
|
|
243
|
+
console.warn("[Channel] DingTalk is enabled but clientId or clientSecret is missing; skip. Check Settings → Channels.");
|
|
244
|
+
}
|
|
245
|
+
await startAllChannels();
|
|
229
246
|
const close = async () => {
|
|
230
247
|
console.log("Closing gateway server...");
|
|
231
248
|
await stopAllChannels();
|
|
@@ -20,11 +20,12 @@ export declare class AgentConfigController {
|
|
|
20
20
|
model?: string;
|
|
21
21
|
modelItemCode?: string;
|
|
22
22
|
systemPrompt?: string;
|
|
23
|
+
icon?: string;
|
|
23
24
|
}): Promise<{
|
|
24
25
|
success: boolean;
|
|
25
26
|
data: AgentConfigItem;
|
|
26
27
|
}>;
|
|
27
|
-
updateAgent(id: string, body: Partial<Pick<AgentConfigItem, 'name' | 'provider' | 'model' | 'modelItemCode' | 'mcpServers' | 'systemPrompt'>>): Promise<{
|
|
28
|
+
updateAgent(id: string, body: Partial<Pick<AgentConfigItem, 'name' | 'provider' | 'model' | 'modelItemCode' | 'mcpServers' | 'systemPrompt' | 'icon'>>): Promise<{
|
|
28
29
|
success: boolean;
|
|
29
30
|
data: AgentConfigItem;
|
|
30
31
|
}>;
|
|
@@ -19,6 +19,8 @@ export interface AgentConfigItem {
|
|
|
19
19
|
mcpServers?: McpServerConfig[];
|
|
20
20
|
/** 自定义系统提示词,会与技能等一起组成最终 systemPrompt */
|
|
21
21
|
systemPrompt?: string;
|
|
22
|
+
/** 智能体图标标识(前端预设图标 id,如 default、star、code 等) */
|
|
23
|
+
icon?: string;
|
|
22
24
|
}
|
|
23
25
|
export declare class AgentConfigService {
|
|
24
26
|
private configDir;
|
|
@@ -40,8 +42,10 @@ export declare class AgentConfigService {
|
|
|
40
42
|
modelItemCode?: string;
|
|
41
43
|
/** 自定义系统提示词;可由用户描述生成,创建后可在详情页修改 */
|
|
42
44
|
systemPrompt?: string;
|
|
45
|
+
/** 智能体图标标识 */
|
|
46
|
+
icon?: string;
|
|
43
47
|
}): Promise<AgentConfigItem>;
|
|
44
|
-
updateAgent(id: string, updates: Partial<Pick<AgentConfigItem, 'name' | 'provider' | 'model' | 'modelItemCode' | 'mcpServers' | 'systemPrompt'>>): Promise<AgentConfigItem>;
|
|
48
|
+
updateAgent(id: string, updates: Partial<Pick<AgentConfigItem, 'name' | 'provider' | 'model' | 'modelItemCode' | 'mcpServers' | 'systemPrompt' | 'icon'>>): Promise<AgentConfigItem>;
|
|
45
49
|
deleteAgent(id: string): Promise<void>;
|
|
46
50
|
/**
|
|
47
51
|
* 根据 config 的 defaultProvider / defaultModel / defaultModelItemCode 及 configuredModels 同步 agents.json 中缺省智能体的 provider、model、modelItemCode。
|
|
@@ -93,7 +93,7 @@ let AgentConfigService = class AgentConfigService {
|
|
|
93
93
|
return null;
|
|
94
94
|
}
|
|
95
95
|
async createAgent(params) {
|
|
96
|
-
const { name, workspace, provider, model, modelItemCode, systemPrompt } = params;
|
|
96
|
+
const { name, workspace, provider, model, modelItemCode, systemPrompt, icon } = params;
|
|
97
97
|
if (workspace === DEFAULT_AGENT_ID) {
|
|
98
98
|
throw new BadRequestException('工作空间名 default 为系统保留(主智能体),请使用其他名称');
|
|
99
99
|
}
|
|
@@ -122,6 +122,7 @@ let AgentConfigService = class AgentConfigService {
|
|
|
122
122
|
model: model?.trim() || undefined,
|
|
123
123
|
modelItemCode: modelItemCode?.trim() || undefined,
|
|
124
124
|
systemPrompt: systemPrompt?.trim() || undefined,
|
|
125
|
+
icon: icon?.trim() || undefined,
|
|
125
126
|
};
|
|
126
127
|
file.agents.push(agent);
|
|
127
128
|
await this.writeAgentsFile(file);
|
|
@@ -156,6 +157,8 @@ let AgentConfigService = class AgentConfigService {
|
|
|
156
157
|
agent.mcpServers = updates.mcpServers;
|
|
157
158
|
if (updates.systemPrompt !== undefined)
|
|
158
159
|
agent.systemPrompt = updates.systemPrompt?.trim() || undefined;
|
|
160
|
+
if (updates.icon !== undefined)
|
|
161
|
+
agent.icon = updates.icon?.trim() || undefined;
|
|
159
162
|
await this.writeAgentsFile(file);
|
|
160
163
|
return { ...agent, isDefault: agent.id === DEFAULT_AGENT_ID };
|
|
161
164
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.8.
|
|
6
|
+
"version": "0.8.7",
|
|
7
7
|
"description": "OpenClawX - A professional desktop application for managing and executing AI agents with real-time chat, session management, and skills browsing.",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"main": "dist/index.js",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"agent-browser": "^0.8.5",
|
|
39
39
|
"commander": "^14.0.2",
|
|
40
40
|
"croner": "^9.1.0",
|
|
41
|
+
"dingtalk-stream": "^2.1.4",
|
|
41
42
|
"multer": "^1.4.5-lts.1",
|
|
42
43
|
"reflect-metadata": "^0.2.1",
|
|
43
44
|
"rxjs": "^7.8.1",
|